]> git.tuebingen.mpg.de Git - paraslash.git/commitdiff
openssl: Use the EVP library for RSA private decryption.
authorAndre Noll <maan@tuebingen.mpg.de>
Mon, 15 May 2023 16:35:30 +0000 (18:35 +0200)
committerAndre Noll <maan@tuebingen.mpg.de>
Sun, 17 Mar 2024 11:35:04 +0000 (12:35 +0100)
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
openssl.c

diff --git a/error.h b/error.h
index 899543ab22d764e9d23e63633b9f06aba4caf5e4..971d3e7f9b7df5a1c96744cc003f93343cd0d6c9 100644 (file)
--- a/error.h
+++ b/error.h
        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"), \
index d30d83716ed9261e6c285493dac5508bcbbe9be2..0ab047dba413d5074e2fb14bf814f5b9cc0a1dcc 100644 (file)
--- 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;
 }