]> git.tue.mpg.de Git - paraslash.git/commitdiff
openssl: Use the EVP library for RSA public encryption.
authorAndre Noll <maan@tuebingen.mpg.de>
Sun, 7 May 2023 15:49:58 +0000 (17:49 +0200)
committerAndre Noll <maan@tuebingen.mpg.de>
Thu, 16 May 2024 23:36:55 +0000 (01:36 +0200)
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
openssl.c

index b9ac3d0d47a4dc2adcfd8b6c28468aab35c50601..d6796e5641aeea757b84db65d6e6f50f4a489240 100644 (file)
@@ -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],
index 5f981437061ee40f1110405c31160d6defbe2772..d30d83716ed9261e6c285493dac5508bcbbe9be2 100644 (file)
--- 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 {