From 2282adb56d722ede1070e0bdc540f27bbd5f3386 Mon Sep 17 00:00:00 2001 From: Andre Noll Date: Sun, 7 May 2023 17:49:58 +0200 Subject: [PATCH] openssl: Use the EVP library for RSA public encryption. Many functions related to RSA have been deprecated in openssl-3. Users of the deprecated API are expected to switch to the high-level cryptographic functions of the EVP library which ships together with openssl. Since openssl-1.0 is still supported and even openssl-1.1 lacks some of the features we need for EVP, for example OSSL_PARAM_construct_BN(), we check for this symbol at configure time and use #ifdefs in openssl.c to compile the code conditionally depending on the value of the new HAVE_OSSL_PARAM preprocessor macro. The code should work with both old and new openssl versions. apc_get_pubkey() used to call RSA_size() to obtain the key size in bytes for the return value, but RSA_size() is one of the functions that got deprecated in openssl-3. So modify read_public_key() to return the number of bits of the modulus (rather than the constant one), and use 1/8 of this number as the return value. --- configure.ac | 5 ++ openssl.c | 134 +++++++++++++++++++++++++++++++++++++-------------- 2 files changed, 104 insertions(+), 35 deletions(-) diff --git a/configure.ac b/configure.ac index 92560e00..6a10b296 100644 --- a/configure.ac +++ b/configure.ac @@ -110,6 +110,11 @@ if test $HAVE_OPENSSL = yes; then will be removed in the next major paraslash release. Please upgrade your openssl installation.]) fi + + AC_CHECK_LIB([crypto], [OSSL_PARAM_construct_BN], [HAVE_OSSL_PARAM=yes], + [HAVE_OSSL_PARAM=no]) + test $HAVE_OSSL_PARAM = yes && + AC_DEFINE([HAVE_OSSL_PARAM], [1], [openssl >= 3.0]) HAVE_CRYPTO_CLEANUP_ALL_EX_DATA=yes AC_CHECK_DECL([CRYPTO_cleanup_all_ex_data], [], [HAVE_CRYPTO_CLEANUP_ALL_EX_DATA=no], diff --git a/openssl.c b/openssl.c index 5f981437..d30d8371 100644 --- a/openssl.c +++ b/openssl.c @@ -22,6 +22,8 @@ struct asymmetric_key { RSA *rsa; + EVP_PKEY *pkey; + EVP_PKEY_CTX *ctx; }; static int openssl_perror(const char *pfx) @@ -103,35 +105,78 @@ static int read_bignum(const unsigned char *buf, size_t len, BIGNUM **result) return bnsize + 4; } -static int read_public_key(const unsigned char *blob, int blen, - struct asymmetric_key *result) +#ifdef HAVE_OSSL_PARAM /* openssl-3 */ +/* + * Convert bignumns e and n to a pkey and context. + */ +static int generate_public_pkey(struct asymmetric_key *pub, + const BIGNUM *e, const BIGNUM *n) { - int ret; - RSA *rsa; - BIGNUM *n, *e; + unsigned char *ebuf, *nbuf; + int ret, ebytes = BN_num_bytes(e), nbytes = BN_num_bytes(n); + OSSL_PARAM params[3]; + + /* Convert e and n to a buffer for OSSL_PARAM_construct_BN() */ + ebuf = alloc(ebytes); + assert(BN_bn2nativepad(e, ebuf, ebytes) > 0); + nbuf = alloc(nbytes); + assert(BN_bn2nativepad(n, nbuf, nbytes) > 0); + /* Init params[] with {e,n}buf and create the pkey from it */ + params[0] = OSSL_PARAM_construct_BN("e", ebuf, ebytes); + params[1] = OSSL_PARAM_construct_BN("n", nbuf, nbytes); + params[2] = OSSL_PARAM_construct_end(); + pub->ctx = EVP_PKEY_CTX_new_from_name(NULL, "RSA", NULL); + assert(pub->ctx); + assert(EVP_PKEY_fromdata_init(pub->ctx) > 0); + ret = EVP_PKEY_fromdata(pub->ctx, &pub->pkey, EVP_PKEY_PUBLIC_KEY, + params); + free(nbuf); + free(ebuf); + if (ret <= 0) { + EVP_PKEY_CTX_free(pub->ctx); + return openssl_perror("EVP_PKEY_fromdata()"); + } + assert(pub->pkey); + return nbytes * 8; +} +#endif /* HAVE_OSSL_PARAM */ + +static int read_public_key(const unsigned char *blob, size_t blen, + struct asymmetric_key *pub) +{ + int ret, bits; const unsigned char *p = blob, *end = blob + blen; + BIGNUM *e, *n; - assert((rsa = RSA_new())); ret = read_bignum(p, end - p, &e); if (ret < 0) - goto free_rsa; + return ret; p += ret; ret = read_bignum(p, end - p, &n); - if (ret < 0) - goto free_e; -#ifdef HAVE_RSA_SET0_KEY - RSA_set0_key(rsa, n, e, NULL); -#else - rsa->n = n; - rsa->e = e; -#endif - result->rsa = rsa; - return 1; -free_e: + if (ret < 0) { + BN_free(e); + return ret; + } + bits = BN_num_bytes(n) * 8; + PARA_DEBUG_LOG("modulus: %d bits\n", bits); +#ifdef HAVE_OSSL_PARAM /* openssl-3 */ + ret = generate_public_pkey(pub, e, n); BN_free(e); -free_rsa: - RSA_free(rsa); - return ret; + BN_free(n); + if (ret < 0) + return ret; +#else /* openssl < 3.0 */ + pub->rsa = RSA_new(); + assert(pub->rsa); + #if HAVE_RSA_SET0_KEY /* openssl-1.1 */ + RSA_set0_key(pub->rsa, n, e, NULL); + #else /* openssl-1.0 */ + pub->rsa->n = n; + pub->rsa->e = e; + #endif + /* e and n are now owned by openssl */ +#endif /* HAVE_OSSL_PARAM */ + return bits; } static int read_pem_private_key(const char *path, struct asymmetric_key *priv) @@ -244,33 +289,35 @@ int apc_get_pubkey(const char *key_file, struct asymmetric_key **result) unsigned char *blob; size_t decoded_size; int ret; - struct asymmetric_key *pub = alloc(sizeof(*pub)); + struct asymmetric_key *pub; ret = decode_public_key(key_file, &blob, &decoded_size); if (ret < 0) - goto out; + return ret; + pub = zalloc(sizeof(*pub)); /* ->pkey needs to start out zeroed */ ret = read_public_key(blob + ret, decoded_size - ret, pub); - if (ret < 0) - goto free_blob; - ret = RSA_size(pub->rsa); - assert(ret > 0); - *result = pub; -free_blob: free(blob); -out: if (ret < 0) { free(pub); *result = NULL; PARA_ERROR_LOG("can not load key %s\n", key_file); + return ret; } - return ret; + PARA_NOTICE_LOG("loaded %d bit key from %s\n", ret, key_file); + *result = pub; + return ret / 8; } void apc_free_pubkey(struct asymmetric_key *pub) { if (!pub) return; +#ifdef HAVE_OSSL_PARAM /* openssl-3 */ + EVP_PKEY_CTX_free(pub->ctx); + EVP_PKEY_free(pub->pkey); +#else RSA_free(pub->rsa); +#endif free(pub); } @@ -317,13 +364,29 @@ out: int apc_pub_encrypt(struct asymmetric_key *pub, unsigned char *inbuf, unsigned len, unsigned char **outbuf) { - int ret, flen = len; /* RSA_public_encrypt expects a signed int */ + int ret; +#ifdef HAVE_OSSL_PARAM /* openssl-3 */ + EVP_PKEY_CTX *ctx; + size_t outlen; *outbuf = NULL; - if (flen < 0) - return -E_ENCRYPT; + assert((ctx = EVP_PKEY_CTX_new(pub->pkey, NULL))); + assert((EVP_PKEY_encrypt_init(ctx) > 0)); + assert((EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING) > 0)); + if (EVP_PKEY_encrypt(ctx, NULL, &outlen, inbuf, len) <= 0) { + ret = openssl_perror("EVP_PKEY_encrypt()"); + goto free_ctx; + } + *outbuf = alloc(outlen); + assert((EVP_PKEY_encrypt(ctx, *outbuf, &outlen, inbuf, len) > 0)); + PARA_INFO_LOG("wrote %zu encrypted data bytes\n", outlen); + ret = outlen; +free_ctx: + EVP_PKEY_CTX_free(ctx); + return ret; +#else /* openssl < 3.0 */ *outbuf = alloc(RSA_size(pub->rsa)); - ret = RSA_public_encrypt(flen, inbuf, *outbuf, pub->rsa, + ret = RSA_public_encrypt((int)len, inbuf, *outbuf, pub->rsa, RSA_PKCS1_OAEP_PADDING); if (ret < 0) { free(*outbuf); @@ -331,6 +394,7 @@ int apc_pub_encrypt(struct asymmetric_key *pub, unsigned char *inbuf, return -E_ENCRYPT; } return ret; +#endif /* HAVE_OSSL_PARAM */ } struct stream_cipher { -- 2.39.2