Browse Source

Augment RSA provider to generate CRT coefficients on EVP_PKEY_fromdata()

It would be helpful to be able to generate RSA's dmp1/dmq1/iqmp values
when not provided in the param list to EVP_PKEY_fromdata.  Augment the
provider in ossl_rsa_fromdata to preform this generation iff:
a) At least p q n e and e are provided
b) the new parameter OSSL_PARAM_RSA_DERIVE_PQ is set to 1

Fixes #21826

Reviewed-by: Shane Lontis <shane.lontis@oracle.com>
Reviewed-by: Tomas Mraz <tomas@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/21875)
Neil Horman 8 months ago
parent
commit
f3be536686

+ 6 - 0
CHANGES.md

@@ -80,6 +80,12 @@ OpenSSL 3.2
 
 
 ### Changes between 3.1 and 3.2 [xx XXX xxxx]
 ### Changes between 3.1 and 3.2 [xx XXX xxxx]
 
 
+ * The EVP_PKEY_fromdata function has been augmented to allow for the derivation
+   of CRT (Chinese Remainder Theorem) parameters when requested.  See the
+   OSSL_PKEY_PARAM_DERIVE_FROM_PQ param in the EVP_PKEY-RSA documentation.
+
+   *Neil Horman*
+
  * The BLAKE2b hash algorithm supports a configurable output length
  * The BLAKE2b hash algorithm supports a configurable output length
    by setting the "size" parameter.
    by setting the "size" parameter.
 
 

+ 143 - 13
crypto/rsa/rsa_backend.c

@@ -64,22 +64,56 @@ static int collect_numbers(STACK_OF(BIGNUM) *numbers,
 int ossl_rsa_fromdata(RSA *rsa, const OSSL_PARAM params[], int include_private)
 int ossl_rsa_fromdata(RSA *rsa, const OSSL_PARAM params[], int include_private)
 {
 {
     const OSSL_PARAM *param_n, *param_e,  *param_d = NULL;
     const OSSL_PARAM *param_n, *param_e,  *param_d = NULL;
-    BIGNUM *n = NULL, *e = NULL, *d = NULL;
+    const OSSL_PARAM *param_p, *param_q = NULL;
+    const OSSL_PARAM *param_derive = NULL;
+    BIGNUM *p = NULL, *q = NULL, *n = NULL, *e = NULL, *d = NULL;
     STACK_OF(BIGNUM) *factors = NULL, *exps = NULL, *coeffs = NULL;
     STACK_OF(BIGNUM) *factors = NULL, *exps = NULL, *coeffs = NULL;
     int is_private = 0;
     int is_private = 0;
+    int derive_from_pq = 0;
+    BN_CTX *ctx = NULL;
 
 
     if (rsa == NULL)
     if (rsa == NULL)
         return 0;
         return 0;
 
 
     param_n = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_RSA_N);
     param_n = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_RSA_N);
     param_e = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_RSA_E);
     param_e = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_RSA_E);
-    if (include_private)
-        param_d = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_RSA_D);
 
 
-    if ((param_n != NULL && !OSSL_PARAM_get_BN(param_n, &n))
-        || (param_e != NULL && !OSSL_PARAM_get_BN(param_e, &e))
-        || (param_d != NULL && !OSSL_PARAM_get_BN(param_d, &d)))
+    if ((param_n == NULL || !OSSL_PARAM_get_BN(param_n, &n))
+        || (param_e == NULL || !OSSL_PARAM_get_BN(param_e, &e))) {
+        ERR_raise(ERR_LIB_RSA, ERR_R_PASSED_NULL_PARAMETER);
         goto err;
         goto err;
+    }
+
+    if (include_private) {
+
+        param_derive = OSSL_PARAM_locate_const(params,
+                                           OSSL_PKEY_PARAM_RSA_DERIVE_FROM_PQ);
+        if ((param_derive != NULL)
+            && !OSSL_PARAM_get_int(param_derive, &derive_from_pq))
+            goto err;
+
+        param_d = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_RSA_D);
+        if (param_d != NULL && !OSSL_PARAM_get_BN(param_d, &d)) {
+            ERR_raise(ERR_LIB_RSA, ERR_R_PASSED_NULL_PARAMETER);
+            goto err;
+        }
+
+        if (derive_from_pq) {
+            ctx = BN_CTX_new_ex(rsa->libctx);
+            if (ctx == NULL)
+                goto err;
+
+            /* we need at minimum p, q */
+            param_p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_RSA_FACTOR1);
+            param_q = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_RSA_FACTOR2);
+            if ((param_p == NULL || !OSSL_PARAM_get_BN(param_p, &p))
+                || (param_q == NULL || !OSSL_PARAM_get_BN(param_q, &q))) {
+                ERR_raise(ERR_LIB_RSA, ERR_R_PASSED_NULL_PARAMETER);
+                goto err;
+            }
+
+        }
+    }
 
 
     is_private = (d != NULL);
     is_private = (d != NULL);
 
 
@@ -96,25 +130,121 @@ int ossl_rsa_fromdata(RSA *rsa, const OSSL_PARAM params[], int include_private)
                                 ossl_rsa_mp_coeff_names))
                                 ossl_rsa_mp_coeff_names))
             goto err;
             goto err;
 
 
-        /* It's ok if this private key just has n, e and d */
+        if (derive_from_pq && sk_BIGNUM_num(exps) == 0
+            && sk_BIGNUM_num(coeffs) == 0) {
+            /*
+             * If we want to use crt to derive our exponents/coefficients, we
+             * need to have at least 2 factors
+             */
+            if (sk_BIGNUM_num(factors) < 2) {
+                ERR_raise(ERR_LIB_RSA, ERR_R_PASSED_NULL_PARAMETER);
+                goto err;
+            }
+
+            /*
+             * if we have more than two factors, n and d must also have
+             * been provided
+             */
+            if (sk_BIGNUM_num(factors) > 2
+                && (param_n == NULL || param_d == NULL)) {
+                ERR_raise(ERR_LIB_RSA, ERR_R_PASSED_NULL_PARAMETER);
+                goto err;
+            }
+
+            /* build our exponents and coefficients here */
+            if (sk_BIGNUM_num(factors) == 2) {
+                /* for 2 factors we can use the sp800 functions to do this */
+                if (!RSA_set0_factors(rsa, sk_BIGNUM_value(factors, 0),
+                                      sk_BIGNUM_value(factors, 1))) {
+                    ERR_raise(ERR_LIB_RSA, ERR_R_INTERNAL_ERROR);
+                    goto err;
+                }
+                /*
+                 * once consumed by RSA_set0_factors, pop those off the stack
+                 * so we don't free them below
+                 */
+                sk_BIGNUM_pop(factors);
+                sk_BIGNUM_pop(factors);
+
+                /*
+                 * Note: Because we only have 2 factors here, there will be no
+                 * additional pinfo fields to hold additional factors, and
+                 * since we set our key and 2 factors above we can skip
+                 * the call to ossl_rsa_set0_all_params
+                 */
+                if (!ossl_rsa_sp800_56b_derive_params_from_pq(rsa,
+                                                              RSA_bits(rsa),
+                                                              NULL, ctx)) {
+                    ERR_raise(ERR_LIB_RSA, ERR_R_INTERNAL_ERROR);
+                    goto err;
+                }
+            } else {
+#ifndef FIPS_MODULE
+                /*
+                 * in the multiprime case we have to generate exps/coeffs here
+                 * for each additional prime
+                 */
+                if (!ossl_rsa_multiprime_derive(rsa, RSA_bits(rsa),
+                                                sk_BIGNUM_num(factors),
+                                                rsa->e, factors, exps,
+                                                coeffs)) {
+                    ERR_raise(ERR_LIB_RSA, ERR_R_INTERNAL_ERROR);
+                    goto err;
+                }
+
+                /*
+                 * Now we should have all our factors, exponents and
+                 * coefficients
+                 */
+                if (!ossl_rsa_set0_all_params(rsa, factors, exps, coeffs)) {
+                    ERR_raise(ERR_LIB_RSA, ERR_R_INTERNAL_ERROR);
+                    goto err;
+                }
+
+#else
+                /* multiprime case is disallowed in FIPS mode, raise an error */
+                ERR_raise(ERR_LIB_RSA, ERR_R_UNSUPPORTED);
+                goto err;
+#endif
+            }
+
+        } else {
+            /*
+             * It's ok if this private key just has n, e and d
+             * but only if we're not using derive_from_pq
+             */
+            if (sk_BIGNUM_num(factors) != 0
+                && !ossl_rsa_set0_all_params(rsa, factors, exps, coeffs))
+                goto err;
+        }
+        /* sanity check to ensure we used everything in our stacks */
         if (sk_BIGNUM_num(factors) != 0
         if (sk_BIGNUM_num(factors) != 0
-            && !ossl_rsa_set0_all_params(rsa, factors, exps, coeffs))
+            || sk_BIGNUM_num(exps) != 0
+            || sk_BIGNUM_num(coeffs) != 0) {
+            ERR_raise_data(ERR_LIB_RSA, ERR_R_INTERNAL_ERROR,
+                           "There are %d, %d, %d elements left on our factors, exps, coeffs stacks\n",
+                           sk_BIGNUM_num(factors), sk_BIGNUM_num(exps),
+                           sk_BIGNUM_num(coeffs));
             goto err;
             goto err;
+        }
     }
     }
 
 
-
+    BN_clear_free(p);
+    BN_clear_free(q);
     sk_BIGNUM_free(factors);
     sk_BIGNUM_free(factors);
     sk_BIGNUM_free(exps);
     sk_BIGNUM_free(exps);
     sk_BIGNUM_free(coeffs);
     sk_BIGNUM_free(coeffs);
+    BN_CTX_free(ctx);
     return 1;
     return 1;
 
 
  err:
  err:
     BN_free(n);
     BN_free(n);
     BN_free(e);
     BN_free(e);
     BN_free(d);
     BN_free(d);
-    sk_BIGNUM_pop_free(factors, BN_free);
-    sk_BIGNUM_pop_free(exps, BN_free);
-    sk_BIGNUM_pop_free(coeffs, BN_free);
+    sk_BIGNUM_pop_free(factors, BN_clear_free);
+    sk_BIGNUM_pop_free(exps, BN_clear_free);
+    sk_BIGNUM_pop_free(coeffs, BN_clear_free);
+    BN_CTX_free(ctx);
     return 0;
     return 0;
 }
 }
 
 
@@ -152,7 +282,7 @@ int ossl_rsa_todata(RSA *rsa, OSSL_PARAM_BLD *bld, OSSL_PARAM params[],
             || !ossl_param_build_set_multi_key_bn(bld, params,
             || !ossl_param_build_set_multi_key_bn(bld, params,
                                                   ossl_rsa_mp_coeff_names,
                                                   ossl_rsa_mp_coeff_names,
                                                   coeffs))
                                                   coeffs))
-        goto err;
+            goto err;
     }
     }
 
 
 #if defined(FIPS_MODULE) && !defined(OPENSSL_NO_ACVP_TESTS)
 #if defined(FIPS_MODULE) && !defined(OPENSSL_NO_ACVP_TESTS)

+ 254 - 75
crypto/rsa/rsa_gen.c

@@ -71,15 +71,201 @@ int RSA_generate_multi_prime_key(RSA *rsa, int bits, int primes,
     return rsa_keygen(rsa->libctx, rsa, bits, primes, e_value, cb, 0);
     return rsa_keygen(rsa->libctx, rsa, bits, primes, e_value, cb, 0);
 }
 }
 
 
+DEFINE_STACK_OF(BIGNUM)
+
+/*
+ * Given input values, q, p, n, d and e, derive the exponents
+ * and coefficients for each prime in this key, placing the result
+ * on their respective exps and coeffs stacks
+ */
 #ifndef FIPS_MODULE
 #ifndef FIPS_MODULE
+int ossl_rsa_multiprime_derive(RSA *rsa, int bits, int primes,
+                               BIGNUM *e_value,
+                               STACK_OF(BIGNUM) *factors,
+                               STACK_OF(BIGNUM) *exps,
+                               STACK_OF(BIGNUM) *coeffs)
+{
+    STACK_OF(BIGNUM) *pplist = NULL, *pdlist = NULL;
+    BIGNUM *factor = NULL, *newpp = NULL, *newpd = NULL;
+    BIGNUM *dval = NULL, *newexp = NULL, *newcoeff = NULL;
+    BIGNUM *p = NULL, *q = NULL;
+    BIGNUM *dmp1 = NULL, *dmq1 = NULL, *iqmp = NULL;
+    BIGNUM *r0 = NULL, *r1 = NULL, *r2 = NULL;
+    BN_CTX *ctx = NULL;
+    BIGNUM *tmp = NULL;
+    int i;
+    int ret = 0;
+
+    ctx = BN_CTX_new_ex(rsa->libctx);
+    if (ctx == NULL)
+        goto err;
+
+    BN_CTX_start(ctx);
+
+    pplist = sk_BIGNUM_new_null();
+    if (pplist == NULL)
+        goto err;
+
+    pdlist = sk_BIGNUM_new_null();
+    if (pdlist == NULL)
+        goto err;
+
+    r0 = BN_CTX_get(ctx);
+    r1 = BN_CTX_get(ctx);
+    r2 = BN_CTX_get(ctx);
+
+    if (r2 == NULL)
+        goto err;
+
+    BN_set_flags(r0, BN_FLG_CONSTTIME);
+    BN_set_flags(r1, BN_FLG_CONSTTIME);
+    BN_set_flags(r2, BN_FLG_CONSTTIME);
+
+    if (BN_copy(r1, rsa->n) == NULL)
+        goto err;
+
+    p = sk_BIGNUM_value(factors, 0);
+    q = sk_BIGNUM_value(factors, 1);
+
+    /* Build list of partial products of primes */
+    for (i = 0; i < sk_BIGNUM_num(factors); i++) {
+        switch (i) {
+        case 0:
+            /* our first prime, p */
+            if (!BN_sub(r2, p, BN_value_one()))
+                goto err;
+            BN_set_flags(r2, BN_FLG_CONSTTIME);
+            if (BN_mod_inverse(r1, r2, rsa->e, ctx) == NULL)
+                goto err;
+            break;
+        case 1:
+            /* second prime q */
+            if (!BN_mul(r1, p, q, ctx))
+                goto err;
+            tmp = BN_dup(r1);
+            if (tmp == NULL)
+                goto err;
+            if (!sk_BIGNUM_insert(pplist, tmp, sk_BIGNUM_num(pplist)))
+                goto err;
+            break;
+        default:
+            factor = sk_BIGNUM_value(factors, i);
+            /* all other primes */
+            if (!BN_mul(r1, r1, factor, ctx))
+                goto err;
+            tmp = BN_dup(r1);
+            if (tmp == NULL)
+                goto err;
+            if (!sk_BIGNUM_insert(pplist, tmp, sk_BIGNUM_num(pplist)))
+                goto err;
+            break;
+        }
+    }
+
+    /* build list of relative d values */
+    /* p -1 */
+    if (!BN_sub(r1, p, BN_value_one()))
+        goto err;
+    if (!BN_sub(r2, q, BN_value_one()))
+        goto err;
+    if (!BN_mul(r0, r1, r2, ctx))
+        goto err;
+    for (i = 2; i < sk_BIGNUM_num(factors); i++) {
+        factor = sk_BIGNUM_value(factors, i);
+        dval = BN_new();
+        if (dval == NULL)
+            goto err;
+        BN_set_flags(dval, BN_FLG_CONSTTIME);
+        if (!BN_sub(dval, factor, BN_value_one()))
+            goto err;
+        if (!BN_mul(r0, r0, dval, ctx))
+            goto err;
+        if (!sk_BIGNUM_insert(pdlist, dval, sk_BIGNUM_num(pdlist)))
+            goto err;
+    }
+
+    /* Calculate dmp1, dmq1 and additional exponents */
+    dmp1 = BN_secure_new();
+    if (dmp1 == NULL)
+        goto err;
+    dmq1 = BN_secure_new();
+    if (dmq1 == NULL)
+        goto err;
+
+    if (!BN_mod(dmp1, rsa->d, r1, ctx))
+        goto err;
+    if (!sk_BIGNUM_insert(exps, dmp1, sk_BIGNUM_num(exps)))
+        goto err;
+    dmp1 = NULL;
+
+    if (!BN_mod(dmq1, rsa->d, r2, ctx))
+        goto err;
+    if (!sk_BIGNUM_insert(exps, dmq1, sk_BIGNUM_num(exps)))
+        goto err;
+    dmq1 = NULL;
+
+    for (i = 2; i < sk_BIGNUM_num(factors); i++) {
+        newpd = sk_BIGNUM_value(pdlist, i - 2);
+        newexp = BN_new();
+        if (newexp == NULL)
+            goto err;
+        if (!BN_mod(newexp, rsa->d, newpd, ctx)) {
+            BN_free(newexp);
+            goto err;
+        }
+        if (!sk_BIGNUM_insert(exps, newexp, sk_BIGNUM_num(exps)))
+            goto err;
+    }
+
+    /* Calculate iqmp and additional coefficients */
+    iqmp = BN_new();
+    if (iqmp == NULL)
+        goto err;
+
+    if (BN_mod_inverse(iqmp, sk_BIGNUM_value(factors, 1),
+                       sk_BIGNUM_value(factors, 0), ctx) == NULL)
+        goto err;
+    if (!sk_BIGNUM_insert(coeffs, iqmp, sk_BIGNUM_num(coeffs)))
+        goto err;
+    iqmp = NULL;
+
+    for (i = 2; i < sk_BIGNUM_num(factors); i++) {
+        newpp = sk_BIGNUM_value(pplist, i - 2);
+        newcoeff = BN_new();
+        if (newcoeff == NULL)
+            goto err;
+        if (BN_mod_inverse(newcoeff, newpp, sk_BIGNUM_value(factors, i),
+                           ctx) == NULL) {
+            BN_free(newcoeff);
+            goto err;
+        }
+        if (!sk_BIGNUM_insert(coeffs, newcoeff, sk_BIGNUM_num(coeffs)))
+            goto err;
+    }
+
+    ret = 1;
+ err:
+    sk_BIGNUM_pop_free(pplist, BN_free);
+    sk_BIGNUM_pop_free(pdlist, BN_free);
+    BN_CTX_end(ctx);
+    BN_CTX_free(ctx);
+    BN_clear_free(dmp1);
+    BN_clear_free(dmq1);
+    BN_clear_free(iqmp);
+    return ret;
+}
+
 static int rsa_multiprime_keygen(RSA *rsa, int bits, int primes,
 static int rsa_multiprime_keygen(RSA *rsa, int bits, int primes,
                                  BIGNUM *e_value, BN_GENCB *cb)
                                  BIGNUM *e_value, BN_GENCB *cb)
 {
 {
-    BIGNUM *r0 = NULL, *r1 = NULL, *r2 = NULL, *tmp, *prime;
+    BIGNUM *r0 = NULL, *r1 = NULL, *r2 = NULL, *tmp, *tmp2, *prime;
     int n = 0, bitsr[RSA_MAX_PRIME_NUM], bitse = 0;
     int n = 0, bitsr[RSA_MAX_PRIME_NUM], bitse = 0;
     int i = 0, quo = 0, rmd = 0, adj = 0, retries = 0;
     int i = 0, quo = 0, rmd = 0, adj = 0, retries = 0;
     RSA_PRIME_INFO *pinfo = NULL;
     RSA_PRIME_INFO *pinfo = NULL;
     STACK_OF(RSA_PRIME_INFO) *prime_infos = NULL;
     STACK_OF(RSA_PRIME_INFO) *prime_infos = NULL;
+    STACK_OF(BIGNUM) *factors = NULL;
+    STACK_OF(BIGNUM) *exps = NULL;
+    STACK_OF(BIGNUM) *coeffs = NULL;
     BN_CTX *ctx = NULL;
     BN_CTX *ctx = NULL;
     BN_ULONG bitst = 0;
     BN_ULONG bitst = 0;
     unsigned long error = 0;
     unsigned long error = 0;
@@ -104,6 +290,18 @@ static int rsa_multiprime_keygen(RSA *rsa, int bits, int primes,
         return 0;
         return 0;
     }
     }
 
 
+    factors = sk_BIGNUM_new_null();
+    if (factors == NULL)
+        return 0;
+
+    exps = sk_BIGNUM_new_null();
+    if (exps == NULL)
+        goto err;
+
+    coeffs = sk_BIGNUM_new_null();
+    if (coeffs == NULL)
+        goto err;
+
     ctx = BN_CTX_new_ex(rsa->libctx);
     ctx = BN_CTX_new_ex(rsa->libctx);
     if (ctx == NULL)
     if (ctx == NULL)
         goto err;
         goto err;
@@ -137,15 +335,6 @@ static int rsa_multiprime_keygen(RSA *rsa, int bits, int primes,
     if (!rsa->q && ((rsa->q = BN_secure_new()) == NULL))
     if (!rsa->q && ((rsa->q = BN_secure_new()) == NULL))
         goto err;
         goto err;
     BN_set_flags(rsa->q, BN_FLG_CONSTTIME);
     BN_set_flags(rsa->q, BN_FLG_CONSTTIME);
-    if (!rsa->dmp1 && ((rsa->dmp1 = BN_secure_new()) == NULL))
-        goto err;
-    BN_set_flags(rsa->dmp1, BN_FLG_CONSTTIME);
-    if (!rsa->dmq1 && ((rsa->dmq1 = BN_secure_new()) == NULL))
-        goto err;
-    BN_set_flags(rsa->dmq1, BN_FLG_CONSTTIME);
-    if (!rsa->iqmp && ((rsa->iqmp = BN_secure_new()) == NULL))
-        goto err;
-    BN_set_flags(rsa->iqmp, BN_FLG_CONSTTIME);
 
 
     /* initialize multi-prime components */
     /* initialize multi-prime components */
     if (primes > RSA_DEFAULT_PRIME_NUM) {
     if (primes > RSA_DEFAULT_PRIME_NUM) {
@@ -220,7 +409,7 @@ static int rsa_multiprime_keygen(RSA *rsa, int bits, int primes,
             ERR_set_mark();
             ERR_set_mark();
             BN_set_flags(r2, BN_FLG_CONSTTIME);
             BN_set_flags(r2, BN_FLG_CONSTTIME);
             if (BN_mod_inverse(r1, r2, rsa->e, ctx) != NULL) {
             if (BN_mod_inverse(r1, r2, rsa->e, ctx) != NULL) {
-               /* GCD == 1 since inverse exists */
+                /* GCD == 1 since inverse exists */
                 break;
                 break;
             }
             }
             error = ERR_peek_last_error();
             error = ERR_peek_last_error();
@@ -250,8 +439,14 @@ static int rsa_multiprime_keygen(RSA *rsa, int bits, int primes,
             /* i == 0, do nothing */
             /* i == 0, do nothing */
             if (!BN_GENCB_call(cb, 3, i))
             if (!BN_GENCB_call(cb, 3, i))
                 goto err;
                 goto err;
+            tmp = BN_dup(prime);
+            if (tmp == NULL)
+                goto err;
+            if (!sk_BIGNUM_insert(factors, tmp, sk_BIGNUM_num(factors)))
+                goto err;
             continue;
             continue;
         }
         }
+
         /*
         /*
          * if |r1|, product of factors so far, is not as long as expected
          * if |r1|, product of factors so far, is not as long as expected
          * (by checking the first 4 bits are less than 0x9 or greater than
          * (by checking the first 4 bits are less than 0x9 or greater than
@@ -298,6 +493,10 @@ static int rsa_multiprime_keygen(RSA *rsa, int bits, int primes,
                  */
                  */
                 i = -1;
                 i = -1;
                 bitse = 0;
                 bitse = 0;
+                sk_BIGNUM_pop_free(factors, BN_clear_free);
+                factors = sk_BIGNUM_new_null();
+                if (factors == NULL)
+                    goto err;
                 continue;
                 continue;
             }
             }
             retries++;
             retries++;
@@ -310,12 +509,20 @@ static int rsa_multiprime_keygen(RSA *rsa, int bits, int primes,
             goto err;
             goto err;
         if (!BN_GENCB_call(cb, 3, i))
         if (!BN_GENCB_call(cb, 3, i))
             goto err;
             goto err;
+        tmp = BN_dup(prime);
+        if (tmp == NULL)
+            goto err;
+        if (!sk_BIGNUM_insert(factors, tmp, sk_BIGNUM_num(factors)))
+            goto err;
     }
     }
 
 
     if (BN_cmp(rsa->p, rsa->q) < 0) {
     if (BN_cmp(rsa->p, rsa->q) < 0) {
         tmp = rsa->p;
         tmp = rsa->p;
         rsa->p = rsa->q;
         rsa->p = rsa->q;
         rsa->q = tmp;
         rsa->q = tmp;
+        /* mirror this in our factor stack */
+        if (!sk_BIGNUM_insert(factors, sk_BIGNUM_delete(factors, 0), 1))
+            goto err;
     }
     }
 
 
     /* calculate d */
     /* calculate d */
@@ -339,79 +546,51 @@ static int rsa_multiprime_keygen(RSA *rsa, int bits, int primes,
             goto err;
             goto err;
     }
     }
 
 
-    {
-        BIGNUM *pr0 = BN_new();
 
 
-        if (pr0 == NULL)
-            goto err;
-
-        BN_with_flags(pr0, r0, BN_FLG_CONSTTIME);
-        if (!BN_mod_inverse(rsa->d, rsa->e, pr0, ctx)) {
-            BN_free(pr0);
-            goto err;               /* d */
-        }
-        /* We MUST free pr0 before any further use of r0 */
-        BN_free(pr0);
-    }
-
-    {
-        BIGNUM *d = BN_new();
-
-        if (d == NULL)
-            goto err;
-
-        BN_with_flags(d, rsa->d, BN_FLG_CONSTTIME);
-
-        /* calculate d mod (p-1) and d mod (q - 1) */
-        if (!BN_mod(rsa->dmp1, d, r1, ctx)
-            || !BN_mod(rsa->dmq1, d, r2, ctx)) {
-            BN_free(d);
-            goto err;
-        }
-
-        /* calculate CRT exponents */
-        for (i = 2; i < primes; i++) {
-            pinfo = sk_RSA_PRIME_INFO_value(prime_infos, i - 2);
-            /* pinfo->d == r_i - 1 */
-            if (!BN_mod(pinfo->d, d, pinfo->d, ctx)) {
-                BN_free(d);
-                goto err;
-            }
-        }
-
-        /* We MUST free d before any further use of rsa->d */
-        BN_free(d);
+    BN_set_flags(r0, BN_FLG_CONSTTIME);
+    if (BN_mod_inverse(rsa->d, rsa->e, r0, ctx) == NULL) {
+        goto err;               /* d */
     }
     }
 
 
-    {
-        BIGNUM *p = BN_new();
+    /* derive any missing exponents and coefficients */
+    if (!ossl_rsa_multiprime_derive(rsa, bits, primes, e_value,
+                                    factors, exps, coeffs))
+        goto err;
 
 
-        if (p == NULL)
+    /*
+     * first 2 factors/exps are already tracked in p/q/dmq1/dmp1
+     * and the first coeff is in iqmp, so pop those off the stack
+     * Note, the first 2 factors/exponents are already tracked by p and q
+     * assign dmp1/dmq1 and iqmp
+     * the remaining pinfo values are separately allocated, so copy and delete 
+     * those
+     */
+    BN_clear_free(sk_BIGNUM_delete(factors, 0));
+    BN_clear_free(sk_BIGNUM_delete(factors, 0));
+    rsa->dmp1 = sk_BIGNUM_delete(exps, 0);
+    rsa->dmq1 = sk_BIGNUM_delete(exps, 0);
+    rsa->iqmp = sk_BIGNUM_delete(coeffs, 0);
+    for (i = 2; i < primes; i++) {
+        pinfo = sk_RSA_PRIME_INFO_value(prime_infos, i - 2);
+        tmp = sk_BIGNUM_delete(factors, 0);
+        BN_copy(pinfo->r, tmp);
+        BN_clear_free(tmp);
+        tmp = sk_BIGNUM_delete(exps, 0);
+        tmp2 = BN_copy(pinfo->d, tmp);
+        BN_clear_free(tmp);
+        if (tmp2 == NULL)
             goto err;
             goto err;
-        BN_with_flags(p, rsa->p, BN_FLG_CONSTTIME);
-
-        /* calculate inverse of q mod p */
-        if (!BN_mod_inverse(rsa->iqmp, rsa->q, p, ctx)) {
-            BN_free(p);
+        tmp = sk_BIGNUM_delete(coeffs, 0);
+        tmp2 = BN_copy(pinfo->t, tmp);
+        BN_clear_free(tmp);
+        if (tmp2 == NULL)
             goto err;
             goto err;
-        }
-
-        /* calculate CRT coefficient for other primes */
-        for (i = 2; i < primes; i++) {
-            pinfo = sk_RSA_PRIME_INFO_value(prime_infos, i - 2);
-            BN_with_flags(p, pinfo->r, BN_FLG_CONSTTIME);
-            if (!BN_mod_inverse(pinfo->t, pinfo->pp, p, ctx)) {
-                BN_free(p);
-                goto err;
-            }
-        }
-
-        /* We MUST free p before any further use of rsa->p */
-        BN_free(p);
     }
     }
-
     ok = 1;
     ok = 1;
  err:
  err:
+    sk_BIGNUM_free(factors);
+    sk_BIGNUM_free(exps);
+    sk_BIGNUM_free(coeffs);
     if (ok == -1) {
     if (ok == -1) {
         ERR_raise(ERR_LIB_RSA, ERR_R_BN_LIB);
         ERR_raise(ERR_LIB_RSA, ERR_R_BN_LIB);
         ok = 0;
         ok = 0;

+ 28 - 6
crypto/rsa/rsa_lib.c

@@ -744,9 +744,13 @@ int RSA_pkey_ctx_ctrl(EVP_PKEY_CTX *ctx, int optype, int cmd, int p1, void *p2)
 
 
 DEFINE_STACK_OF(BIGNUM)
 DEFINE_STACK_OF(BIGNUM)
 
 
-int ossl_rsa_set0_all_params(RSA *r, const STACK_OF(BIGNUM) *primes,
-                             const STACK_OF(BIGNUM) *exps,
-                             const STACK_OF(BIGNUM) *coeffs)
+/*
+ * Note: This function deletes values from the parameter
+ * stack values as they are consumed and set in the RSA key.
+ */
+int ossl_rsa_set0_all_params(RSA *r, STACK_OF(BIGNUM) *primes,
+                             STACK_OF(BIGNUM) *exps,
+                             STACK_OF(BIGNUM) *coeffs)
 {
 {
 #ifndef FIPS_MODULE
 #ifndef FIPS_MODULE
     STACK_OF(RSA_PRIME_INFO) *prime_infos, *old_infos = NULL;
     STACK_OF(RSA_PRIME_INFO) *prime_infos, *old_infos = NULL;
@@ -757,6 +761,8 @@ int ossl_rsa_set0_all_params(RSA *r, const STACK_OF(BIGNUM) *primes,
         return 0;
         return 0;
 
 
     pnum = sk_BIGNUM_num(primes);
     pnum = sk_BIGNUM_num(primes);
+
+    /* we need at least 2 primes */
     if (pnum < 2)
     if (pnum < 2)
         return 0;
         return 0;
 
 
@@ -764,6 +770,17 @@ int ossl_rsa_set0_all_params(RSA *r, const STACK_OF(BIGNUM) *primes,
                           sk_BIGNUM_value(primes, 1)))
                           sk_BIGNUM_value(primes, 1)))
         return 0;
         return 0;
 
 
+    /*
+     * if we managed to set everything above, remove those elements from the
+     * stack
+     * Note, we do this after the above all to ensure that we have taken
+     * ownership of all the elements in the RSA key to avoid memory leaks
+     * we also use delete 0 here as we are grabbing items from the end of the
+     * stack rather than the start, otherwise we could use pop
+     */
+    sk_BIGNUM_delete(primes, 0);
+    sk_BIGNUM_delete(primes, 0);
+
     if (pnum == sk_BIGNUM_num(exps)
     if (pnum == sk_BIGNUM_num(exps)
         && pnum == sk_BIGNUM_num(coeffs) + 1) {
         && pnum == sk_BIGNUM_num(coeffs) + 1) {
 
 
@@ -771,6 +788,11 @@ int ossl_rsa_set0_all_params(RSA *r, const STACK_OF(BIGNUM) *primes,
                                  sk_BIGNUM_value(exps, 1),
                                  sk_BIGNUM_value(exps, 1),
                                  sk_BIGNUM_value(coeffs, 0)))
                                  sk_BIGNUM_value(coeffs, 0)))
         return 0;
         return 0;
+
+        /* as above, once we consume the above params, delete them from the list */
+        sk_BIGNUM_delete(exps, 0);
+        sk_BIGNUM_delete(exps, 0);
+        sk_BIGNUM_delete(coeffs, 0);
     }
     }
 
 
 #ifndef FIPS_MODULE
 #ifndef FIPS_MODULE
@@ -786,9 +808,9 @@ int ossl_rsa_set0_all_params(RSA *r, const STACK_OF(BIGNUM) *primes,
             return 0;
             return 0;
 
 
         for (i = 2; i < pnum; i++) {
         for (i = 2; i < pnum; i++) {
-            BIGNUM *prime = sk_BIGNUM_value(primes, i);
-            BIGNUM *exp = sk_BIGNUM_value(exps, i);
-            BIGNUM *coeff = sk_BIGNUM_value(coeffs, i - 1);
+            BIGNUM *prime = sk_BIGNUM_pop(primes);
+            BIGNUM *exp = sk_BIGNUM_pop(exps);
+            BIGNUM *coeff = sk_BIGNUM_pop(coeffs);
             RSA_PRIME_INFO *pinfo = NULL;
             RSA_PRIME_INFO *pinfo = NULL;
 
 
             if (!ossl_assert(prime != NULL && exp != NULL && coeff != NULL))
             if (!ossl_assert(prime != NULL && exp != NULL && coeff != NULL))

+ 4 - 0
crypto/rsa/rsa_local.h

@@ -150,6 +150,10 @@ struct rsa_meth_st {
 /* Macros to test if a pkey or ctx is for a PSS key */
 /* Macros to test if a pkey or ctx is for a PSS key */
 #define pkey_is_pss(pkey) (pkey->ameth->pkey_id == EVP_PKEY_RSA_PSS)
 #define pkey_is_pss(pkey) (pkey->ameth->pkey_id == EVP_PKEY_RSA_PSS)
 #define pkey_ctx_is_pss(ctx) (ctx->pmeth->pkey_id == EVP_PKEY_RSA_PSS)
 #define pkey_ctx_is_pss(ctx) (ctx->pmeth->pkey_id == EVP_PKEY_RSA_PSS)
+int ossl_rsa_multiprime_derive(RSA *rsa, int bits, int primes,
+                                 BIGNUM *e_value,
+                                 STACK_OF(BIGNUM) *factors, STACK_OF(BIGNUM) *exps,
+                                 STACK_OF(BIGNUM) *coeffs);
 
 
 RSA_PSS_PARAMS *ossl_rsa_pss_params_create(const EVP_MD *sigmd,
 RSA_PSS_PARAMS *ossl_rsa_pss_params_create(const EVP_MD *sigmd,
                                            const EVP_MD *mgf1md, int saltlen);
                                            const EVP_MD *mgf1md, int saltlen);

+ 32 - 24
crypto/rsa/rsa_sp800_56b_gen.c

@@ -228,13 +228,16 @@ static int rsa_validate_rng_strength(EVP_RAND_CTX *rng, int nbits)
  * Returns: -1 = error,
  * Returns: -1 = error,
  *           0 = d is too small,
  *           0 = d is too small,
  *           1 = success.
  *           1 = success.
+ *
+ * SP800-56b key generation always passes a non NULL value for e.
+ * For other purposes, if e is NULL then it is assumed that e, n and d are
+ * already set in the RSA key and do not need to be recalculated.
  */
  */
 int ossl_rsa_sp800_56b_derive_params_from_pq(RSA *rsa, int nbits,
 int ossl_rsa_sp800_56b_derive_params_from_pq(RSA *rsa, int nbits,
                                              const BIGNUM *e, BN_CTX *ctx)
                                              const BIGNUM *e, BN_CTX *ctx)
 {
 {
     int ret = -1;
     int ret = -1;
     BIGNUM *p1, *q1, *lcm, *p1q1, *gcd;
     BIGNUM *p1, *q1, *lcm, *p1q1, *gcd;
-
     BN_CTX_start(ctx);
     BN_CTX_start(ctx);
     p1 = BN_CTX_get(ctx);
     p1 = BN_CTX_get(ctx);
     q1 = BN_CTX_get(ctx);
     q1 = BN_CTX_get(ctx);
@@ -254,32 +257,37 @@ int ossl_rsa_sp800_56b_derive_params_from_pq(RSA *rsa, int nbits,
     if (ossl_rsa_get_lcm(ctx, rsa->p, rsa->q, lcm, gcd, p1, q1, p1q1) != 1)
     if (ossl_rsa_get_lcm(ctx, rsa->p, rsa->q, lcm, gcd, p1, q1, p1q1) != 1)
         goto err;
         goto err;
 
 
-    /* copy e */
-    BN_free(rsa->e);
-    rsa->e = BN_dup(e);
-    if (rsa->e == NULL)
-        goto err;
+    /*
+     * if e is provided as a parameter, don't recompute e, d or n
+     */
+    if (e != NULL) {
+        /* copy e */
+        BN_free(rsa->e);
+        rsa->e = BN_dup(e);
+        if (rsa->e == NULL)
+            goto err;
 
 
-    BN_clear_free(rsa->d);
-    /* (Step 3) d = (e^-1) mod (LCM(p-1, q-1)) */
-    rsa->d = BN_secure_new();
-    if (rsa->d == NULL)
-        goto err;
-    BN_set_flags(rsa->d, BN_FLG_CONSTTIME);
-    if (BN_mod_inverse(rsa->d, e, lcm, ctx) == NULL)
-        goto err;
+        BN_clear_free(rsa->d);
+        /* (Step 3) d = (e^-1) mod (LCM(p-1, q-1)) */
+        rsa->d = BN_secure_new();
+        if (rsa->d == NULL)
+            goto err;
+        BN_set_flags(rsa->d, BN_FLG_CONSTTIME);
+        if (BN_mod_inverse(rsa->d, e, lcm, ctx) == NULL)
+            goto err;
 
 
-    /* (Step 3) return an error if d is too small */
-    if (BN_num_bits(rsa->d) <= (nbits >> 1)) {
-        ret = 0;
-        goto err;
-    }
+        /* (Step 3) return an error if d is too small */
+        if (BN_num_bits(rsa->d) <= (nbits >> 1)) {
+            ret = 0;
+            goto err;
+        }
 
 
-    /* (Step 4) n = pq */
-    if (rsa->n == NULL)
-        rsa->n = BN_new();
-    if (rsa->n == NULL || !BN_mul(rsa->n, rsa->p, rsa->q, ctx))
-        goto err;
+        /* (Step 4) n = pq */
+        if (rsa->n == NULL)
+            rsa->n = BN_new();
+        if (rsa->n == NULL || !BN_mul(rsa->n, rsa->p, rsa->q, ctx))
+            goto err;
+    }
 
 
     /* (Step 5a) dP = d mod (p-1) */
     /* (Step 5a) dP = d mod (p-1) */
     if (rsa->dmp1 == NULL)
     if (rsa->dmp1 == NULL)

+ 9 - 0
doc/man7/EVP_PKEY-RSA.pod

@@ -132,6 +132,15 @@ The RSA "e" value. The value may be any odd number greater than or equal to
 65537. The default value is 65537.
 65537. The default value is 65537.
 For legacy reasons a value of 3 is currently accepted but is deprecated.
 For legacy reasons a value of 3 is currently accepted but is deprecated.
 
 
+=item "rsa-derive-from-pq"  (B<OSSL_PKEY_PARAM_RSA_DERIVE_FROM_PQ>) <unsigned integer>
+
+Indicate that missing parameters not passed in the parameter list should be
+derived if not provided.  Setting a nonzero value will cause all
+needed exponents and coefficients to be derived if not available.  Setting this
+option requires at least OSSL_PARAM_RSA_FACTOR1, OSSL_PARAM_RSA_FACTOR2,
+and OSSL_PARAM_RSA_N to be provided.  This option is ignored if
+OSSL_KEYMGMT_SELECT_PRIVATE_KEY is not set in the selection parameter.
+
 =back
 =back
 
 
 =head2 RSA key generation parameters for FIPS module testing
 =head2 RSA key generation parameters for FIPS module testing

+ 3 - 3
include/crypto/rsa.h

@@ -54,9 +54,9 @@ RSA *ossl_rsa_new_with_ctx(OSSL_LIB_CTX *libctx);
 OSSL_LIB_CTX *ossl_rsa_get0_libctx(RSA *r);
 OSSL_LIB_CTX *ossl_rsa_get0_libctx(RSA *r);
 void ossl_rsa_set0_libctx(RSA *r, OSSL_LIB_CTX *libctx);
 void ossl_rsa_set0_libctx(RSA *r, OSSL_LIB_CTX *libctx);
 
 
-int ossl_rsa_set0_all_params(RSA *r, const STACK_OF(BIGNUM) *primes,
-                             const STACK_OF(BIGNUM) *exps,
-                             const STACK_OF(BIGNUM) *coeffs);
+int ossl_rsa_set0_all_params(RSA *r, STACK_OF(BIGNUM) *primes,
+                             STACK_OF(BIGNUM) *exps,
+                             STACK_OF(BIGNUM) *coeffs);
 int ossl_rsa_get0_all_params(RSA *r, STACK_OF(BIGNUM_const) *primes,
 int ossl_rsa_get0_all_params(RSA *r, STACK_OF(BIGNUM_const) *primes,
                              STACK_OF(BIGNUM_const) *exps,
                              STACK_OF(BIGNUM_const) *exps,
                              STACK_OF(BIGNUM_const) *coeffs);
                              STACK_OF(BIGNUM_const) *coeffs);

+ 67 - 0
test/evp_extra_test.c

@@ -3080,6 +3080,70 @@ static int test_RSA_OAEP_set_null_label(void)
     return ret;
     return ret;
 }
 }
 
 
+#ifndef OPENSSL_NO_DEPRECATED_3_0
+static int test_RSA_legacy(void)
+{
+    int ret = 0;
+    BIGNUM *p = NULL;
+    BIGNUM *q = NULL;
+    BIGNUM *n = NULL;
+    BIGNUM *e = NULL;
+    BIGNUM *d = NULL;
+    const EVP_MD *md = EVP_sha256();
+    EVP_MD_CTX *ctx = NULL;
+    EVP_PKEY *pkey = NULL;
+    RSA *rsa = NULL;
+
+    if (nullprov != NULL)
+        return TEST_skip("Test does not support a non-default library context");
+
+    if (!TEST_ptr(p = BN_dup(BN_value_one()))
+        || !TEST_ptr(q = BN_dup(BN_value_one()))
+        || !TEST_ptr(n = BN_dup(BN_value_one()))
+        || !TEST_ptr(e = BN_dup(BN_value_one()))
+        || !TEST_ptr(d = BN_dup(BN_value_one())))
+        goto err;
+
+    if (!TEST_ptr(rsa = RSA_new())
+        || !TEST_ptr(pkey = EVP_PKEY_new())
+        || !TEST_ptr(ctx = EVP_MD_CTX_new()))
+        goto err;
+
+    if (!TEST_true(RSA_set0_factors(rsa, p, q)))
+        goto err;
+    p = NULL;
+    q = NULL;
+
+    if (!TEST_true(RSA_set0_key(rsa, n, e, d)))
+        goto err;
+    n = NULL;
+    e = NULL;
+    d = NULL;
+
+    if (!TEST_true(EVP_PKEY_assign_RSA(pkey, rsa)))
+        goto err;
+
+    rsa = NULL;
+
+    if (!TEST_true(EVP_DigestSignInit(ctx, NULL, md, NULL, pkey)))
+        goto err;
+
+    ret = 1;
+
+err:
+    RSA_free(rsa);
+    EVP_MD_CTX_free(ctx);
+    EVP_PKEY_free(pkey);
+    BN_free(p);
+    BN_free(q);
+    BN_free(n);
+    BN_free(e);
+    BN_free(d);
+
+    return ret;
+}
+#endif
+
 #if !defined(OPENSSL_NO_CHACHA) && !defined(OPENSSL_NO_POLY1305)
 #if !defined(OPENSSL_NO_CHACHA) && !defined(OPENSSL_NO_POLY1305)
 static int test_decrypt_null_chunks(void)
 static int test_decrypt_null_chunks(void)
 {
 {
@@ -5520,6 +5584,9 @@ int setup_tests(void)
     ADD_TEST(test_RSA_get_set_params);
     ADD_TEST(test_RSA_get_set_params);
     ADD_TEST(test_RSA_OAEP_set_get_params);
     ADD_TEST(test_RSA_OAEP_set_get_params);
     ADD_TEST(test_RSA_OAEP_set_null_label);
     ADD_TEST(test_RSA_OAEP_set_null_label);
+#ifndef OPENSSL_NO_DEPRECATED_3_0
+    ADD_TEST(test_RSA_legacy);
+#endif
 #if !defined(OPENSSL_NO_CHACHA) && !defined(OPENSSL_NO_POLY1305)
 #if !defined(OPENSSL_NO_CHACHA) && !defined(OPENSSL_NO_POLY1305)
     ADD_TEST(test_decrypt_null_chunks);
     ADD_TEST(test_decrypt_null_chunks);
 #endif
 #endif

+ 396 - 2
test/evp_pkey_provided_test.c

@@ -429,7 +429,8 @@ static int test_fromdata_rsa(void)
     /* for better diagnostics always compare key params */
     /* for better diagnostics always compare key params */
     for (i = 0; fromdata_params[i].key != NULL; ++i) {
     for (i = 0; fromdata_params[i].key != NULL; ++i) {
         if (!TEST_true(BN_set_word(bn_from, key_numbers[i]))
         if (!TEST_true(BN_set_word(bn_from, key_numbers[i]))
-            || !TEST_true(EVP_PKEY_get_bn_param(pk, fromdata_params[i].key, &bn))
+            || !TEST_true(EVP_PKEY_get_bn_param(pk, fromdata_params[i].key,
+                                                &bn))
             || !TEST_BN_eq(bn, bn_from))
             || !TEST_BN_eq(bn, bn_from))
             ret = 0;
             ret = 0;
     }
     }
@@ -443,6 +444,397 @@ static int test_fromdata_rsa(void)
     return ret;
     return ret;
 }
 }
 
 
+struct check_data {
+    const char *pname;
+    BIGNUM *comparebn;
+};
+
+static int do_fromdata_rsa_derive(OSSL_PARAM *fromdata_params,
+                                  struct check_data check[],
+                                  int expected_nbits, int expected_sbits,
+                                  int expected_ksize)
+{
+    const OSSL_PARAM *check_param = NULL;
+    BIGNUM *check_bn = NULL;
+    OSSL_PARAM *todata_params = NULL;
+    EVP_PKEY_CTX *ctx = NULL, *key_ctx = NULL;
+    EVP_PKEY *pk = NULL, *copy_pk = NULL, *dup_pk = NULL;
+    int i;
+    int ret = 0;
+
+    if (!TEST_ptr(ctx = EVP_PKEY_CTX_new_from_name(NULL, "RSA", NULL))
+        || !TEST_int_eq(EVP_PKEY_fromdata_init(ctx), 1)
+        || !TEST_int_eq(EVP_PKEY_fromdata(ctx, &pk, EVP_PKEY_KEYPAIR,
+                                          fromdata_params), 1))
+        goto err;
+
+    /*
+     * get the generated key parameters back and validate that the
+     * exponents/coeffs are correct
+     */
+    if (!TEST_int_eq(EVP_PKEY_todata(pk, EVP_PKEY_KEYPAIR, &todata_params), 1))
+        goto err;
+
+    for (i = 0; check[i].pname != NULL; i++) {
+        if (!TEST_ptr(check_param = OSSL_PARAM_locate_const(todata_params,
+                                                            check[i].pname)))
+            goto err;
+        if (!TEST_int_eq(OSSL_PARAM_get_BN(check_param, &check_bn), 1))
+            goto err;
+        if (!TEST_BN_eq(check_bn, check[i].comparebn)) {
+            TEST_info("Data mismatch for parameter %s", check[i].pname);
+            goto err;
+        }
+        BN_free(check_bn);
+        check_bn = NULL;
+    }
+
+    while (dup_pk == NULL) {
+        if (!TEST_int_eq(EVP_PKEY_get_bits(pk), expected_nbits)
+            || !TEST_int_eq(EVP_PKEY_get_security_bits(pk), expected_sbits)
+            || !TEST_int_eq(EVP_PKEY_get_size(pk), expected_ksize)
+            || !TEST_false(EVP_PKEY_missing_parameters(pk)))
+            goto err;
+
+        EVP_PKEY_CTX_free(key_ctx);
+        if (!TEST_ptr(key_ctx = EVP_PKEY_CTX_new_from_pkey(NULL, pk, "")))
+            goto err;
+
+        if (!TEST_int_gt(EVP_PKEY_check(key_ctx), 0)
+            || !TEST_int_gt(EVP_PKEY_public_check(key_ctx), 0)
+            || !TEST_int_gt(EVP_PKEY_private_check(key_ctx), 0)
+            || !TEST_int_gt(EVP_PKEY_pairwise_check(key_ctx), 0))
+            goto err;
+
+        /* EVP_PKEY_copy_parameters() should fail for RSA */
+        if (!TEST_ptr(copy_pk = EVP_PKEY_new())
+            || !TEST_false(EVP_PKEY_copy_parameters(copy_pk, pk)))
+            goto err;
+        EVP_PKEY_free(copy_pk);
+        copy_pk = NULL;
+
+        if (!TEST_ptr(dup_pk = EVP_PKEY_dup(pk)))
+            goto err;
+        if (!TEST_int_eq(EVP_PKEY_eq(pk, dup_pk), 1)) {
+            EVP_PKEY_free(dup_pk);
+            goto err;
+        }
+        EVP_PKEY_free(pk);
+        pk = dup_pk;
+    }
+    ret = 1;
+err:
+    BN_free(check_bn);
+    EVP_PKEY_free(pk);
+    EVP_PKEY_CTX_free(ctx);
+    EVP_PKEY_CTX_free(key_ctx);
+    OSSL_PARAM_free(fromdata_params);
+    OSSL_PARAM_free(todata_params);
+    return ret;
+}
+
+static int test_fromdata_rsa_derive_from_pq_sp800(void)
+{
+    OSSL_PARAM_BLD *bld = NULL;
+    BIGNUM *n = NULL, *e = NULL, *d = NULL, *p = NULL, *q = NULL;
+    BIGNUM *dmp1 = NULL, *dmq1 = NULL, *iqmp = NULL;
+    OSSL_PARAM *fromdata_params = NULL;
+    struct check_data cdata[4];
+    int ret = 0;
+    /*
+     * 512-bit RSA key, extracted from this command,
+     * openssl genrsa 512 | openssl rsa -text
+     * Note: When generating a key with EVP_PKEY_fromdata, and using
+     * crt derivation, openssl requires a minimum of 512 bits of n data,
+     * and 2048 bits in the FIPS case
+     */
+    static unsigned char n_data[] =
+        {0x00, 0xc7, 0x06, 0xd8, 0x6b, 0x3c, 0x4f, 0xb7, 0x95, 0x42, 0x44, 0x90,
+         0xbd, 0xef, 0xf3, 0xc4, 0xb5, 0xa8, 0x55, 0x9e, 0x33, 0xa3, 0x04, 0x3a,
+         0x90, 0xe5, 0x13, 0xff, 0x87, 0x69, 0x15, 0xa4, 0x8a, 0x17, 0x10, 0xcc,
+         0xdf, 0xf9, 0xc5, 0x0f, 0xf1, 0x12, 0xff, 0x12, 0x11, 0xe5, 0x6b, 0x5c,
+         0x83, 0xd9, 0x43, 0xd1, 0x8a, 0x7e, 0xa6, 0x60, 0x07, 0x2e, 0xbb, 0x03,
+         0x17, 0x2d, 0xec, 0x17, 0x87};
+    static unsigned char e_data[] = {0x01, 0x00, 0x01};
+    static unsigned char d_data[] =
+        {0x1e, 0x5e, 0x5d, 0x07, 0x7f, 0xdc, 0x6a, 0x16, 0xcc, 0x55, 0xca, 0x00,
+         0x31, 0x6c, 0xf0, 0xc7, 0x07, 0x38, 0x89, 0x3b, 0x37, 0xd4, 0x9d, 0x5b,
+         0x1e, 0x99, 0x3e, 0x94, 0x5a, 0xe4, 0x82, 0x86, 0x8a, 0x78, 0x34, 0x09,
+         0x37, 0xd5, 0xe7, 0xb4, 0xef, 0x5f, 0x83, 0x94, 0xff, 0xe5, 0x36, 0x79,
+         0x10, 0x0c, 0x38, 0xc5, 0x3a, 0x33, 0xa6, 0x7c, 0x3c, 0xcc, 0x98, 0xe0,
+         0xf5, 0xdb, 0xe6, 0x81};
+    static unsigned char p_data[] =
+        {0x00, 0xf6, 0x61, 0x38, 0x0e, 0x1f, 0x82, 0x7c, 0xb8, 0xba, 0x00, 0xd3,
+         0xac, 0xdc, 0x4e, 0x6b, 0x7e, 0xf7, 0x58, 0xf3, 0xd9, 0xd8, 0x21, 0xed,
+         0x54, 0xa3, 0x36, 0xd2, 0x2c, 0x5f, 0x06, 0x7d, 0xc5};
+    static unsigned char q_data[] =
+        {0x00, 0xce, 0xcc, 0x4a, 0xa5, 0x4f, 0xd6, 0x73, 0xd0, 0x20, 0xc3, 0x98,
+         0x64, 0x20, 0x9b, 0xc1, 0x23, 0xd8, 0x5c, 0x82, 0x4f, 0xe8, 0xa5, 0x32,
+         0xcd, 0x7e, 0x97, 0xb4, 0xde, 0xf6, 0x4c, 0x80, 0xdb};
+    static unsigned char dmp1_data[] =
+        {0x00, 0xd1, 0x07, 0xb6, 0x79, 0x34, 0xfe, 0x8e, 0x36, 0x63, 0x88, 0xa4,
+         0x0e, 0x3a, 0x73, 0x45, 0xfc, 0x58, 0x7a, 0x5d, 0x98, 0xeb, 0x28, 0x0d,
+         0xa5, 0x0b, 0x3c, 0x4d, 0xa0, 0x5b, 0x96, 0xb4, 0x49};
+    static unsigned char dmq1_data[] =
+        {0x5b, 0x47, 0x02, 0xdf, 0xaa, 0xb8, 0xae, 0x8f, 0xbc, 0x16, 0x79, 0x6a,
+         0x20, 0x96, 0x7f, 0x0e, 0x92, 0x4e, 0x6a, 0xda, 0x58, 0x86, 0xaa, 0x40,
+         0xd7, 0xd2, 0xa0, 0x6c, 0x15, 0x6c, 0xb9, 0x27};
+    static unsigned char iqmp_data[] =
+        {0x00, 0xa0, 0xd6, 0xf0, 0xe8, 0x17, 0x9e, 0xe7, 0xe6, 0x99, 0x12, 0xd6,
+         0xd9, 0x43, 0xcf, 0xed, 0x37, 0x29, 0xf5, 0x6c, 0x3e, 0xc1, 0x7f, 0x2e,
+         0x31, 0x3f, 0x64, 0x34, 0x66, 0x68, 0x5c, 0x22, 0x08};
+
+    if (!TEST_ptr(bld = OSSL_PARAM_BLD_new())
+        || !TEST_ptr(n = BN_bin2bn(n_data, sizeof(n_data), NULL))
+        || !TEST_ptr(e = BN_bin2bn(e_data, sizeof(e_data), NULL))
+        || !TEST_ptr(d = BN_bin2bn(d_data, sizeof(d_data), NULL))
+        || !TEST_ptr(p = BN_bin2bn(p_data, sizeof(p_data), NULL))
+        || !TEST_ptr(q = BN_bin2bn(q_data, sizeof(q_data), NULL))
+        || !TEST_ptr(dmp1 = BN_bin2bn(dmp1_data, sizeof(dmp1_data), NULL))
+        || !TEST_ptr(dmq1 = BN_bin2bn(dmq1_data, sizeof(dmq1_data), NULL))
+        || !TEST_ptr(iqmp = BN_bin2bn(iqmp_data, sizeof(iqmp_data), NULL))
+        || !TEST_true(OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_N, n))
+        || !TEST_true(OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_E, e))
+        || !TEST_true(OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_D, d))
+        || !TEST_true(OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_FACTOR1,
+                                             p))
+        || !TEST_true(OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_FACTOR2,
+                                             q))
+        || !TEST_true(OSSL_PARAM_BLD_push_int(bld,
+                                              OSSL_PKEY_PARAM_RSA_DERIVE_FROM_PQ, 1))
+        || !TEST_ptr(fromdata_params = OSSL_PARAM_BLD_to_param(bld)))
+        goto err;
+
+    cdata[0].pname = OSSL_PKEY_PARAM_RSA_EXPONENT1;
+    cdata[0].comparebn = dmp1;
+    cdata[1].pname = OSSL_PKEY_PARAM_RSA_EXPONENT2;
+    cdata[1].comparebn = dmq1;
+    cdata[2].pname = OSSL_PKEY_PARAM_RSA_COEFFICIENT1;
+    cdata[2].comparebn = iqmp;
+    cdata[3].pname = NULL;
+    cdata[3].comparebn = NULL;
+
+    ret = do_fromdata_rsa_derive(fromdata_params, cdata, 512, 56, 64);
+
+err:
+    BN_free(n);
+    BN_free(e);
+    BN_free(d);
+    BN_free(p);
+    BN_free(q);
+    BN_free(dmp1);
+    BN_free(dmq1);
+    BN_free(iqmp);
+    OSSL_PARAM_BLD_free(bld);
+    return ret;
+}
+
+static int test_fromdata_rsa_derive_from_pq_multiprime(void)
+{
+    OSSL_PARAM_BLD *bld = NULL;
+    BIGNUM *n = NULL, *e = NULL, *d = NULL;
+    BIGNUM *p = NULL, *q = NULL, *p2 = NULL;
+    BIGNUM *dmp1 = NULL, *dmq1 = NULL, *iqmp = NULL;
+    BIGNUM *exp3 = NULL, *coeff2 = NULL;
+    OSSL_PARAM *fromdata_params = NULL;
+    struct check_data cdata[12];
+    int ret = 0;
+    /*
+     * multiprime RSA key,  extracted from this command,
+     * openssl genrsa -primes 3 | openssl rsa -text
+     * Note: When generating a key with EVP_PKEY_fromdata,  and using
+     * crt derivation,  openssl requires a minimum of 512 bits of n data,
+     * and 2048 bits in the FIPS case
+     */
+    static unsigned char n_data[] =
+        {0x00, 0x95, 0x78, 0x21, 0xe0, 0xca, 0x94, 0x6c, 0x0b, 0x86, 0x2a, 0x01,
+         0xde, 0xd9, 0xab, 0xee, 0x88, 0x4a, 0x27, 0x4f, 0xcc, 0x5f, 0xf1, 0x71,
+         0xe1, 0x0b, 0xc3, 0xd1, 0x88, 0x76, 0xf0, 0x83, 0x03, 0x93, 0x7e, 0x39,
+         0xfa, 0x47, 0x89, 0x34, 0x27, 0x18, 0x19, 0x97, 0xfc, 0xd4, 0xfe, 0xe5,
+         0x8a, 0xa9, 0x11, 0x83, 0xb5, 0x15, 0x4a, 0x29, 0xa6, 0xa6, 0xd0, 0x6e,
+         0x0c, 0x7f, 0x61, 0x8f, 0x7e, 0x7c, 0xfb, 0xfc, 0x04, 0x8b, 0xca, 0x44,
+         0xf8, 0x59, 0x0b, 0x22, 0x6f, 0x3f, 0x92, 0x23, 0x98, 0xb5, 0xc8, 0xf7,
+         0xff, 0xf7, 0xac, 0x6b, 0x36, 0xb3, 0xaf, 0x39, 0xde, 0x66, 0x38, 0x51,
+         0x9f, 0xbe, 0xe2, 0xfc, 0xe4, 0x6f, 0x1a, 0x0f, 0x7a, 0xde, 0x7f, 0x0f,
+         0x4e, 0xbc, 0xed, 0xa2, 0x99, 0xc5, 0xd1, 0xbf, 0x8f, 0xba, 0x92, 0x91,
+         0xe4, 0x00, 0x91, 0xbb, 0x67, 0x36, 0x7d, 0x00, 0x50, 0xda, 0x28, 0x38,
+         0xdc, 0x9f, 0xfe, 0x3f, 0x24, 0x5a, 0x0d, 0xe1, 0x8d, 0xe9, 0x45, 0x2c,
+         0xd7, 0xf2, 0x67, 0x8c, 0x0c, 0x6e, 0xdb, 0xc8, 0x8b, 0x6b, 0x38, 0x30,
+         0x21, 0x94, 0xc0, 0xe3, 0xd7, 0xe0, 0x23, 0xd3, 0xd4, 0xfa, 0xdb, 0xb9,
+         0xfe, 0x1a, 0xcc, 0xc9, 0x79, 0x19, 0x35, 0x18, 0x42, 0x30, 0xc4, 0xb5,
+         0x92, 0x33, 0x1e, 0xd4, 0xc4, 0xc0, 0x9d, 0x55, 0x37, 0xd4, 0xef, 0x54,
+         0x71, 0x81, 0x09, 0x15, 0xdb, 0x11, 0x38, 0x6b, 0x35, 0x93, 0x11, 0xdc,
+         0xb1, 0x6c, 0xd6, 0xa4, 0x37, 0x84, 0xf3, 0xb2, 0x2f, 0x1b, 0xd6, 0x05,
+         0x9f, 0x0e, 0x5c, 0x98, 0x29, 0x2f, 0x95, 0xb6, 0x55, 0xbd, 0x24, 0x44,
+         0xc5, 0xc8, 0xa2, 0x76, 0x1e, 0xf8, 0x82, 0x8a, 0xdf, 0x34, 0x72, 0x7e,
+         0xdd, 0x65, 0x4b, 0xfc, 0x6c, 0x1c, 0x96, 0x70, 0xe2, 0x69, 0xb5, 0x12,
+         0x1b, 0x59, 0x67, 0x14, 0x9d};
+    static unsigned char e_data[] = {0x01, 0x00, 0x01};
+    static unsigned char d_data[] =
+        {0x64, 0x57, 0x4d, 0x86, 0xf6, 0xf8, 0x44, 0xc0, 0x47, 0xc5, 0x13, 0x94,
+         0x63, 0x54, 0x84, 0xc1, 0x81, 0xe6, 0x7a, 0x2f, 0x9d, 0x89, 0x1d, 0x06,
+         0x13, 0x3b, 0xd6, 0x02, 0x62, 0xb6, 0x7b, 0x7d, 0x7f, 0x1a, 0x92, 0x19,
+         0x6e, 0xc4, 0xb0, 0xfa, 0x3d, 0xb7, 0x90, 0xcc, 0xee, 0xc0, 0x5f, 0xa0,
+         0x82, 0x77, 0x7b, 0x8f, 0xa9, 0x47, 0x2c, 0x46, 0xf0, 0x5d, 0xa4, 0x43,
+         0x47, 0x90, 0x5b, 0x20, 0x73, 0x0f, 0x46, 0xd4, 0x56, 0x73, 0xe7, 0x71,
+         0x41, 0x75, 0xb4, 0x1c, 0x32, 0xf5, 0x0c, 0x68, 0x8c, 0x40, 0xea, 0x1c,
+         0x30, 0x12, 0xa2, 0x65, 0x02, 0x27, 0x98, 0x4e, 0x0a, 0xbf, 0x2b, 0x72,
+         0xb2, 0x5c, 0xe3, 0xbe, 0x3e, 0xc7, 0xdb, 0x9b, 0xa2, 0x4a, 0x90, 0xc0,
+         0xa7, 0xb0, 0x00, 0xf1, 0x6a, 0xff, 0xa3, 0x77, 0xf7, 0x71, 0xa2, 0x41,
+         0xe9, 0x6e, 0x7c, 0x38, 0x24, 0x46, 0xd5, 0x5c, 0x49, 0x2a, 0xe6, 0xee,
+         0x27, 0x4b, 0x2e, 0x6f, 0x16, 0x54, 0x2d, 0x37, 0x36, 0x01, 0x39, 0x2b,
+         0x23, 0x4b, 0xb4, 0x65, 0x25, 0x4d, 0x7f, 0x72, 0x20, 0x7f, 0x5d, 0xec,
+         0x50, 0xba, 0xbb, 0xaa, 0x9c, 0x3c, 0x1d, 0xa1, 0x40, 0x2c, 0x6a, 0x8b,
+         0x5f, 0x2e, 0xe0, 0xa6, 0xf7, 0x9e, 0x03, 0xb5, 0x44, 0x5f, 0x74, 0xc7,
+         0x9f, 0x89, 0x2b, 0x71, 0x2f, 0x66, 0x9f, 0x03, 0x6c, 0x96, 0xd0, 0x23,
+         0x36, 0x4d, 0xa1, 0xf0, 0x82, 0xcc, 0x43, 0xe7, 0x08, 0x93, 0x40, 0x18,
+         0xc0, 0x39, 0x73, 0x83, 0xe2, 0xec, 0x9b, 0x81, 0x9d, 0x4c, 0x86, 0xaa,
+         0x59, 0xa8, 0x67, 0x1c, 0x80, 0xdc, 0x6f, 0x7f, 0x23, 0x6b, 0x7d, 0x2c,
+         0x56, 0x99, 0xa0, 0x89, 0x7e, 0xdb, 0x8b, 0x7a, 0xaa, 0x03, 0x8e, 0x8e,
+         0x8e, 0x3a, 0x58, 0xb4, 0x03, 0x6b, 0x65, 0xfa, 0x92, 0x0a, 0x96, 0x93,
+         0xa6, 0x07, 0x60, 0x01};
+     static unsigned char p_data[] =
+        {0x06, 0x55, 0x7f, 0xbd, 0xfd, 0xa8, 0x4c, 0x94, 0x5e, 0x10, 0x8a, 0x54,
+         0x37, 0xf3, 0x64, 0x37, 0x3a, 0xca, 0x18, 0x1b, 0xdd, 0x71, 0xa5, 0x94,
+         0xc9, 0x31, 0x59, 0xa5, 0x89, 0xe9, 0xc4, 0xba, 0x55, 0x90, 0x6d, 0x9c,
+         0xcc, 0x52, 0x5d, 0x44, 0xa8, 0xbc, 0x2b, 0x3b, 0x8c, 0xbd, 0x96, 0xfa,
+         0xcd, 0x54, 0x63, 0xe3, 0xc8, 0xfe, 0x5e, 0xc6, 0x73, 0x98, 0x14, 0x7a,
+         0x54, 0x0e, 0xe7, 0x75, 0x49, 0x93, 0x20, 0x33, 0x17, 0xa9, 0x34, 0xa8,
+         0xee, 0xaf, 0x3a, 0xcc, 0xf5, 0x69, 0xfc, 0x30, 0x1a, 0xdf, 0x49, 0x61,
+         0xa4, 0xd1};
+    static unsigned char p2_data[] =
+        {0x03, 0xe2, 0x41, 0x3d, 0xb1, 0xdd, 0xad, 0xd7, 0x3b, 0xf8, 0xab, 0x32,
+         0x27, 0x8b, 0xac, 0x95, 0xc0, 0x1a, 0x3f, 0x80, 0x8e, 0x21, 0xa9, 0xb8,
+         0xa2, 0xed, 0xcf, 0x97, 0x5c, 0x61, 0x10, 0x94, 0x1b, 0xd0, 0xbe, 0x88,
+         0xc2, 0xa7, 0x20, 0xe5, 0xa5, 0xc2, 0x7a, 0x7e, 0xf0, 0xd1, 0xe4, 0x13,
+         0x75, 0xb9, 0x62, 0x90, 0xf1, 0xc3, 0x5b, 0x8c, 0xe9, 0xa9, 0x5b, 0xb7,
+         0x6d, 0xdc, 0xcd, 0x12, 0xea, 0x97, 0x05, 0x04, 0x25, 0x2a, 0x93, 0xd1,
+         0x4e, 0x05, 0x1a, 0x50, 0xa2, 0x67, 0xb8, 0x4b, 0x09, 0x15, 0x65, 0x6c,
+         0x66, 0x2d};
+    static unsigned char q_data[] =
+        {0x06, 0x13, 0x74, 0x6e, 0xde, 0x7c, 0x33, 0xc2, 0xe7, 0x05, 0x2c, 0xeb,
+         0x25, 0x7d, 0x4a, 0x07, 0x7e, 0x03, 0xcf, 0x6a, 0x23, 0x36, 0x25, 0x23,
+         0xf6, 0x5d, 0xde, 0xa3, 0x0f, 0x82, 0xe6, 0x4b, 0xec, 0x39, 0xbf, 0x37,
+         0x1f, 0x4f, 0x56, 0x1e, 0xd8, 0x62, 0x32, 0x5c, 0xf5, 0x37, 0x75, 0x20,
+         0xe2, 0x7e, 0x56, 0x82, 0xc6, 0x35, 0xd3, 0x4d, 0xfa, 0x6c, 0xc3, 0x93,
+         0xf0, 0x60, 0x53, 0x78, 0x95, 0xee, 0xf9, 0x8b, 0x2c, 0xaf, 0xb1, 0x47,
+         0x5c, 0x29, 0x0d, 0x2a, 0x47, 0x7f, 0xd0, 0x7a, 0x4e, 0x26, 0x7b, 0x47,
+         0xfb, 0x61};
+    static unsigned char dmp1_data[] =
+        {0x01, 0x13, 0x3a, 0x1f, 0x91, 0x92, 0xa3, 0x8c, 0xfb, 0x7a, 0x6b, 0x40,
+         0x68, 0x4e, 0xd3, 0xcf, 0xdc, 0x16, 0xb9, 0x88, 0xe1, 0x49, 0x8d, 0x05,
+         0x78, 0x30, 0xfc, 0x3a, 0x70, 0xf2, 0x51, 0x06, 0x1f, 0xc7, 0xe8, 0x13,
+         0x19, 0x4b, 0x51, 0xb1, 0x79, 0xc2, 0x96, 0xc4, 0x00, 0xdb, 0x9d, 0x68,
+         0xec, 0xb9, 0x4a, 0x4b, 0x3b, 0xae, 0x91, 0x7f, 0xb5, 0xd7, 0x36, 0x82,
+         0x9d, 0x09, 0xfa, 0x97, 0x99, 0xe9, 0x73, 0x29, 0xb8, 0xf6, 0x6b, 0x8d,
+         0xd1, 0x15, 0xc5, 0x31, 0x4c, 0xe6, 0xb4, 0x7b, 0xa5, 0xd4, 0x08, 0xac,
+         0x9e, 0x41};
+    static unsigned char dmq1_data[] =
+        {0x05, 0xcd, 0x33, 0xc2, 0xdd, 0x3b, 0xb8, 0xec, 0xe4, 0x4c, 0x03, 0xcc,
+         0xef, 0xba, 0x07, 0x22, 0xca, 0x47, 0x77, 0x18, 0x40, 0x50, 0xe5, 0xfb,
+         0xc5, 0xb5, 0x71, 0xed, 0x3e, 0xd5, 0x5d, 0x72, 0xa7, 0x37, 0xa8, 0x86,
+         0x48, 0xa6, 0x27, 0x74, 0x42, 0x66, 0xd8, 0xf1, 0xfb, 0xcf, 0x1d, 0x4e,
+         0xee, 0x15, 0x76, 0x23, 0x5e, 0x81, 0x6c, 0xa7, 0x2b, 0x74, 0x08, 0xf7,
+         0x4c, 0x71, 0x9d, 0xa2, 0x29, 0x7f, 0xca, 0xd5, 0x02, 0x31, 0x2c, 0x54,
+         0x18, 0x02, 0xb6, 0xa8, 0x65, 0x26, 0xfc, 0xf8, 0x9b, 0x80, 0x90, 0xfc,
+         0x75, 0x61};
+    static unsigned char iqmp_data[] =
+        {0x05, 0x78, 0xf8, 0xdd, 0x1c, 0x6f, 0x3d, 0xaf, 0x53, 0x84, 0x32, 0xa9,
+         0x35, 0x52, 0xf3, 0xd0, 0x4d, 0xf8, 0x09, 0x85, 0x3d, 0x72, 0x20, 0x8b,
+         0x47, 0xba, 0xc8, 0xce, 0xac, 0xd9, 0x76, 0x90, 0x05, 0x88, 0x63, 0x8a,
+         0x10, 0x2b, 0xcd, 0xd3, 0xbe, 0x8c, 0x16, 0x60, 0x6a, 0xfd, 0xce, 0xc7,
+         0x9f, 0xfa, 0xbb, 0xe3, 0xa6, 0xde, 0xc2, 0x8f, 0x1d, 0x25, 0xdc, 0x41,
+         0xcb, 0xa4, 0xeb, 0x76, 0xc9, 0xdc, 0x8e, 0x49, 0x0e, 0xe4, 0x7c, 0xd2,
+         0xd5, 0x6e, 0x26, 0x3c, 0x0b, 0xd3, 0xc5, 0x20, 0x4e, 0x4b, 0xb6, 0xf7,
+         0xae, 0xef};
+    static unsigned char exp3_data[] =
+        {0x02, 0x7d, 0x16, 0x24, 0xfc, 0x35, 0xf9, 0xd0, 0xb3, 0x02, 0xf2, 0x5f,
+         0xde, 0xeb, 0x27, 0x19, 0x85, 0xd0, 0xcb, 0xe4, 0x0a, 0x2f, 0x13, 0xdb,
+         0xd5, 0xba, 0xe0, 0x8c, 0x32, 0x8b, 0x97, 0xdd, 0xef, 0xbc, 0xe0, 0x7a,
+         0x2d, 0x90, 0x7e, 0x09, 0xe9, 0x1f, 0x26, 0xf2, 0xf4, 0x48, 0xea, 0x06,
+         0x76, 0x26, 0xe6, 0x3b, 0xce, 0x4e, 0xc9, 0xf9, 0x0f, 0x38, 0x90, 0x26,
+         0x87, 0x65, 0x36, 0x9a, 0xea, 0x6a, 0xfe, 0xb1, 0xdb, 0x46, 0xdf, 0x14,
+         0xfd, 0x13, 0x53, 0xfb, 0x5b, 0x35, 0x6e, 0xe7, 0xd5, 0xd8, 0x39, 0xf7,
+         0x2d, 0xb9};
+    static unsigned char coeff2_data[] =
+        {0x01, 0xba, 0x66, 0x0a, 0xa2, 0x86, 0xc0, 0x57, 0x7f, 0x4e, 0x68, 0xb1,
+         0x86, 0x63, 0x23, 0x5b, 0x0e, 0xeb, 0x93, 0x42, 0xd1, 0xaa, 0x15, 0x13,
+         0xcc, 0x29, 0x71, 0x8a, 0xb0, 0xe0, 0xc9, 0x67, 0xde, 0x1a, 0x7c, 0x1a,
+         0xef, 0xa7, 0x08, 0x85, 0xb3, 0xae, 0x98, 0x99, 0xde, 0xaf, 0x09, 0x38,
+         0xfc, 0x46, 0x29, 0x5f, 0x4f, 0x7e, 0x01, 0x6c, 0x50, 0x13, 0x95, 0x91,
+         0x4c, 0x0f, 0x00, 0xba, 0xca, 0x40, 0xa3, 0xd0, 0x58, 0xb6, 0x62, 0x4c,
+         0xd1, 0xb6, 0xd3, 0x29, 0x5d, 0x82, 0xb3, 0x3d, 0x61, 0xbe, 0x5d, 0xf0,
+         0x4b, 0xf4};
+
+    if (!TEST_ptr(bld = OSSL_PARAM_BLD_new())
+        || !TEST_ptr(n = BN_bin2bn(n_data, sizeof(n_data), NULL))
+        || !TEST_ptr(e = BN_bin2bn(e_data, sizeof(e_data), NULL))
+        || !TEST_ptr(d = BN_bin2bn(d_data, sizeof(d_data), NULL))
+        || !TEST_ptr(p = BN_bin2bn(p_data, sizeof(p_data), NULL))
+        || !TEST_ptr(q = BN_bin2bn(q_data, sizeof(q_data), NULL))
+        || !TEST_ptr(p2 = BN_bin2bn(p2_data, sizeof(p2_data), NULL))
+        || !TEST_ptr(exp3 = BN_bin2bn(exp3_data, sizeof(exp3_data), NULL))
+        || !TEST_ptr(coeff2 = BN_bin2bn(coeff2_data, sizeof(coeff2_data), NULL))
+        || !TEST_ptr(dmp1 = BN_bin2bn(dmp1_data, sizeof(dmp1_data), NULL))
+        || !TEST_ptr(dmq1 = BN_bin2bn(dmq1_data, sizeof(dmq1_data), NULL))
+        || !TEST_ptr(iqmp = BN_bin2bn(iqmp_data, sizeof(iqmp_data), NULL))
+        || !TEST_true(OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_N, n))
+        || !TEST_true(OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_E, e))
+        || !TEST_true(OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_D, d))
+        || !TEST_true(OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_FACTOR1,
+                                             p))
+        || !TEST_true(OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_FACTOR2,
+                                             q))
+        || !TEST_true(OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_FACTOR3,
+                                             p2))
+        || !TEST_true(OSSL_PARAM_BLD_push_int(bld,
+                                              OSSL_PKEY_PARAM_RSA_DERIVE_FROM_PQ, 1))
+        || !TEST_ptr(fromdata_params = OSSL_PARAM_BLD_to_param(bld)))
+        goto err;
+
+    cdata[0].pname = OSSL_PKEY_PARAM_RSA_EXPONENT1;
+    cdata[0].comparebn = dmp1;
+    cdata[1].pname = OSSL_PKEY_PARAM_RSA_EXPONENT2;
+    cdata[1].comparebn = dmq1;
+    cdata[2].pname = OSSL_PKEY_PARAM_RSA_COEFFICIENT1;
+    cdata[2].comparebn = iqmp;
+    cdata[3].pname = OSSL_PKEY_PARAM_RSA_EXPONENT3;
+    cdata[3].comparebn = exp3;
+    cdata[4].pname = OSSL_PKEY_PARAM_RSA_COEFFICIENT2;
+    cdata[4].comparebn = coeff2;
+    cdata[5].pname = OSSL_PKEY_PARAM_RSA_N;
+    cdata[5].comparebn = n;
+    cdata[6].pname = OSSL_PKEY_PARAM_RSA_E;
+    cdata[6].comparebn = e;
+    cdata[7].pname = OSSL_PKEY_PARAM_RSA_D;
+    cdata[7].comparebn = d;
+    cdata[8].pname = OSSL_PKEY_PARAM_RSA_FACTOR1;
+    cdata[8].comparebn = p;
+    cdata[9].pname = OSSL_PKEY_PARAM_RSA_FACTOR2;
+    cdata[9].comparebn = q;
+    cdata[10].pname = OSSL_PKEY_PARAM_RSA_FACTOR3;
+    cdata[10].comparebn = p2;
+    cdata[11].pname = NULL;
+    cdata[11].comparebn = NULL;
+
+    ret = do_fromdata_rsa_derive(fromdata_params, cdata, 2048, 112, 256);
+
+err:
+    BN_free(n);
+    BN_free(e);
+    BN_free(d);
+    BN_free(p);
+    BN_free(p2);
+    BN_free(q);
+    BN_free(dmp1);
+    BN_free(dmq1);
+    BN_free(iqmp);
+    BN_free(exp3);
+    BN_free(coeff2);
+    OSSL_PARAM_BLD_free(bld);
+    return ret;
+}
+
 static int test_evp_pkey_get_bn_param_large(void)
 static int test_evp_pkey_get_bn_param_large(void)
 {
 {
     int ret = 0;
     int ret = 0;
@@ -459,7 +851,7 @@ static int test_evp_pkey_get_bn_param_large(void)
     static const unsigned char e_data[] = {
     static const unsigned char e_data[] = {
         0x1, 0x00, 0x01
         0x1, 0x00, 0x01
     };
     };
-    static const unsigned char d_data[]= {
+    static const unsigned char d_data[] = {
        0x99, 0x33, 0x13, 0x7b
        0x99, 0x33, 0x13, 0x7b
     };
     };
 
 
@@ -1765,6 +2157,8 @@ int setup_tests(void)
     ADD_TEST(test_evp_pkey_ctx_dup_kdf);
     ADD_TEST(test_evp_pkey_ctx_dup_kdf);
     ADD_TEST(test_evp_pkey_get_bn_param_large);
     ADD_TEST(test_evp_pkey_get_bn_param_large);
     ADD_TEST(test_fromdata_rsa);
     ADD_TEST(test_fromdata_rsa);
+    ADD_TEST(test_fromdata_rsa_derive_from_pq_sp800);
+    ADD_TEST(test_fromdata_rsa_derive_from_pq_multiprime);
 #ifndef OPENSSL_NO_DH
 #ifndef OPENSSL_NO_DH
     ADD_TEST(test_fromdata_dh_fips186_4);
     ADD_TEST(test_fromdata_dh_fips186_4);
     ADD_TEST(test_fromdata_dh_named_group);
     ADD_TEST(test_fromdata_dh_named_group);

+ 1 - 0
util/perl/OpenSSL/paramnames.pm

@@ -348,6 +348,7 @@ my %params = (
     'PKEY_PARAM_RSA_MASKGENFUNC' =>      '*PKEY_PARAM_MASKGENFUNC',
     'PKEY_PARAM_RSA_MASKGENFUNC' =>      '*PKEY_PARAM_MASKGENFUNC',
     'PKEY_PARAM_RSA_MGF1_DIGEST' =>      '*PKEY_PARAM_MGF1_DIGEST',
     'PKEY_PARAM_RSA_MGF1_DIGEST' =>      '*PKEY_PARAM_MGF1_DIGEST',
     'PKEY_PARAM_RSA_PSS_SALTLEN' =>      "saltlen",
     'PKEY_PARAM_RSA_PSS_SALTLEN' =>      "saltlen",
+    'PKEY_PARAM_RSA_DERIVE_FROM_PQ'    =>     "rsa-derive-from-pq",
 
 
 # EC, X25519 and X448 Key generation parameters
 # EC, X25519 and X448 Key generation parameters
     'PKEY_PARAM_DHKEM_IKM' =>        "dhkem-ikm",
     'PKEY_PARAM_DHKEM_IKM' =>        "dhkem-ikm",