From bc031cc68fc5eb709f6cdb7b31ec36b96c28bec7 Mon Sep 17 00:00:00 2001 From: Andre Noll Date: Mon, 15 May 2023 18:35:30 +0200 Subject: [PATCH] openssl: Use the EVP library for RSA private decryption. This is the counterpart of the previous commit which converted the public RSA encryption. We employ the HAVE_OSSL_PARAM macro again to distinguish between the openssl-1 and openssl-3 cases. --- error.h | 1 - openssl.c | 141 ++++++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 110 insertions(+), 32 deletions(-) diff --git a/error.h b/error.h index 899543ab..971d3e7f 100644 --- a/error.h +++ b/error.h @@ -163,7 +163,6 @@ PARA_ERROR(PATH_FOUND, ""), /* not really an error */ \ PARA_ERROR(PLAYLIST_EMPTY, "attempted to load empty playlist"), \ PARA_ERROR(PREBUFFER_SUCCESS, "prebuffering complete"), \ - PARA_ERROR(PRIVATE_KEY, "can not read private key"), \ PARA_ERROR(QUEUE, "packet queue overrun"), \ PARA_ERROR(READ_PATTERN, "did not read expected pattern"), \ PARA_ERROR(RECVMSG, "recvmsg() failed"), \ diff --git a/openssl.c b/openssl.c index d30d8371..0ab047db 100644 --- a/openssl.c +++ b/openssl.c @@ -106,6 +106,45 @@ static int read_bignum(const unsigned char *buf, size_t len, BIGNUM **result) } #ifdef HAVE_OSSL_PARAM /* openssl-3 */ + +static int generate_private_pkey(struct asymmetric_key *priv, + const BIGNUM *n, const BIGNUM *e, const BIGNUM *d, + const BIGNUM *p, const BIGNUM *q) +{ + const BIGNUM *bignums[] = {n, e, d, p, q}; + const char *strings[] = {"n", "e", "d", "p", "q"}; + int ret, bytes[ARRAY_SIZE(bignums)]; + unsigned char *bufs[ARRAY_SIZE(bignums)]; + OSSL_PARAM params[ARRAY_SIZE(bignums) + 1]; + /* + * Convert bignums to buffers for OSSL_PARAM_construct_BN() and init + * params[]. + */ + for (int i = 0; i < ARRAY_SIZE(bignums); i++) { + bytes[i] = BN_num_bytes(bignums[i]); + PARA_DEBUG_LOG("%s: %d bits\n", strings[i], bytes[i] * 8); + bufs[i] = alloc(bytes[i]); + assert(BN_bn2nativepad(bignums[i], bufs[i], bytes[i]) > 0); + params[i] = OSSL_PARAM_construct_BN(strings[i], bufs[i], + bytes[i]); + } + params[ARRAY_SIZE(bignums)] = OSSL_PARAM_construct_end(); + /* Transfer buffers to openssl to create the pkey from it */ + priv->ctx = EVP_PKEY_CTX_new_from_name(NULL, "RSA", NULL); + assert(priv->ctx); + assert(EVP_PKEY_fromdata_init(priv->ctx) > 0); + ret = EVP_PKEY_fromdata(priv->ctx, &priv->pkey, + EVP_PKEY_KEYPAIR, params); + for (int i = 0; i < ARRAY_SIZE(bignums); i++) + free(bufs[i]); + if (ret <= 0) { + EVP_PKEY_CTX_free(priv->ctx); + return openssl_perror("EVP_PKEY_fromdata()"); + } + assert(priv->pkey); + return BN_num_bytes(n) * 8; +} + /* * Convert bignumns e and n to a pkey and context. */ @@ -139,6 +178,7 @@ static int generate_public_pkey(struct asymmetric_key *pub, assert(pub->pkey); return nbytes * 8; } + #endif /* HAVE_OSSL_PARAM */ static int read_public_key(const unsigned char *blob, size_t blen, @@ -181,35 +221,39 @@ static int read_public_key(const unsigned char *blob, size_t blen, static int read_pem_private_key(const char *path, struct asymmetric_key *priv) { - EVP_PKEY *pkey; BIO *bio; + int ret; assert((bio = BIO_new(BIO_s_file()))); - priv->rsa = NULL; - if (BIO_read_filename(bio, path) <= 0) - goto bio_free; - pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL); - if (!pkey) - goto bio_free; - priv->rsa = EVP_PKEY_get1_RSA(pkey); - EVP_PKEY_free(pkey); -bio_free: + ret = BIO_read_filename(bio, path); + if (ret <= 0) { + priv->pkey = NULL; + ret = openssl_perror("BIO_read_filename"); + goto free_bio; + } + priv->pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL); + if (!priv->pkey) { + ret = openssl_perror("PEM_read_bio_PrivateKey"); + goto free_bio; + } +#ifndef HAVE_OSSL_PARAM /* openssl-1 */ + priv->rsa = EVP_PKEY_get1_RSA(priv->pkey); +#endif +free_bio: BIO_free(bio); - return priv->rsa? RSA_size(priv->rsa) : -E_PRIVATE_KEY; + return ret; } static int read_openssh_private_key(const unsigned char *blob, const unsigned char *end, struct asymmetric_key *priv) { int ret; - RSA *rsa; BIGNUM *n, *e, *d, *iqmp, *p, *q; /* stored in the key file */ const unsigned char *cp = blob; - assert((rsa = RSA_new())); ret = read_bignum(cp, end - cp, &n); if (ret < 0) - goto free_rsa; + return ret; cp += ret; ret = read_bignum(cp, end - cp, &e); if (ret < 0) @@ -230,21 +274,30 @@ static int read_openssh_private_key(const unsigned char *blob, ret = read_bignum(cp, end - cp, &q); if (ret < 0) goto free_p; -#ifdef HAVE_RSA_SET0_KEY - RSA_set0_key(rsa, n, e, d); - RSA_set0_factors(rsa, p, q); - RSA_set0_crt_params(rsa, NULL, NULL, iqmp); - +#ifdef HAVE_OSSL_PARAM /* openssl-3 */ + /* + * Ignore iqmp, the coefficient for Chinese remainder theorem. It is + * dispensable because it can be derived from the other values. Passing + * it to the EVP API results in a memory leak. + */ + ret = generate_private_pkey(priv, n, e, d, p, q); #else - rsa->n = n; - rsa->e = e; - rsa->d = d; - rsa->iqmp = iqmp; - rsa->p = p; - rsa->q = q; -#endif - priv->rsa = rsa; + assert((priv->rsa = RSA_new())); + #ifdef HAVE_RSA_SET0_KEY + RSA_set0_key(priv->rsa, n, e, d); + RSA_set0_factors(priv->rsa, p, q); + RSA_set0_crt_params(priv->rsa, NULL, NULL, iqmp); + #else + priv->rsa->n = n; + priv->rsa->e = e; + priv->rsa->d = d; + priv->rsa->iqmp = iqmp; + priv->rsa->p = p; + priv->rsa->q = q; + #endif return 1; +#endif /* HAVE_OSSL_PARAM */ + BN_clear_free(q); free_p: BN_clear_free(p); free_iqmp: @@ -255,8 +308,6 @@ free_e: BN_free(e); free_n: BN_free(n); -free_rsa: - RSA_free(rsa); return ret; } @@ -266,7 +317,6 @@ static int get_private_key(const char *path, struct asymmetric_key *priv) unsigned char *blob, *end; size_t blob_size; - priv->rsa = NULL; ret = decode_private_key(path, &blob, &blob_size); if (ret < 0) return ret; @@ -321,6 +371,29 @@ void apc_free_pubkey(struct asymmetric_key *pub) free(pub); } +#ifdef HAVE_OSSL_PARAM /* openssl-3 */ +static int pkey_priv_decrypt(const struct asymmetric_key *priv, + unsigned char **outbuf, unsigned char *inbuf, int inlen) +{ + EVP_PKEY_CTX *ctx; + size_t outlen; + + assert((ctx = EVP_PKEY_CTX_new(priv->pkey, NULL))); + assert((EVP_PKEY_decrypt_init(ctx) > 0)); + assert(EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING) > 0); + if (EVP_PKEY_decrypt(ctx, NULL, &outlen, inbuf, inlen) <= 0) { + *outbuf = NULL; + EVP_PKEY_CTX_free(ctx); + return openssl_perror("EVP_PKEY_encrypt()"); + } + *outbuf = alloc(outlen); + assert((EVP_PKEY_decrypt(ctx, *outbuf, &outlen, inbuf, inlen) > 0)); + EVP_PKEY_CTX_free(ctx); + PARA_INFO_LOG("wrote %zu decrypted data bytes\n", outlen); + return outlen; +} +#endif /* HAVE_OSSL_PARAM */ + int apc_priv_decrypt(const char *key_file, unsigned char **outbuf, unsigned char *inbuf, int inlen) { @@ -333,12 +406,17 @@ int apc_priv_decrypt(const char *key_file, unsigned char **outbuf, return ret; if (inlen < 0) return -E_RSA; - priv = alloc(sizeof(*priv)); + priv = zalloc(sizeof(*priv)); /* ->pkey needs to start out zeroed */ ret = get_private_key(key_file, priv); if (ret < 0) { free(priv); return ret; } +#ifdef HAVE_OSSL_PARAM /* openssl-3 */ + ret = pkey_priv_decrypt(priv, outbuf, inbuf, inlen); + EVP_PKEY_CTX_free(priv->ctx); + EVP_PKEY_free(priv->pkey); +#else /* * RSA is vulnerable to timing attacks. Generate a random blinding * factor to protect against this kind of attack. @@ -357,6 +435,7 @@ int apc_priv_decrypt(const char *key_file, unsigned char **outbuf, } out: RSA_free(priv->rsa); +#endif free(priv); return ret; } -- 2.39.2