From 982f4fd2efcc8d592dbcd786005c2717208c96c9 Mon Sep 17 00:00:00 2001 From: sebastian-carpenter Date: Tue, 16 Dec 2025 17:40:19 -0700 Subject: [PATCH] rsa key validation test and additions to rsa fromdata test updated rsa_key_import / validate to survive new tests --- src/wp_rsa_kmgmt.c | 209 +++++++++++++++++++++++++-- test/test_rsa.c | 342 ++++++++++++++++++++++++++++++++++++++++++++- test/unit.c | 1 + test/unit.h | 1 + 4 files changed, 536 insertions(+), 17 deletions(-) diff --git a/src/wp_rsa_kmgmt.c b/src/wp_rsa_kmgmt.c index c66a5dee..951d6576 100644 --- a/src/wp_rsa_kmgmt.c +++ b/src/wp_rsa_kmgmt.c @@ -191,6 +191,11 @@ static const char* wp_rsa_param_key[WP_RSA_PARAM_NUMS_CNT] = { OSSL_PKEY_PARAM_RSA_EXPONENT1, OSSL_PKEY_PARAM_RSA_EXPONENT2, OSSL_PKEY_PARAM_RSA_COEFFICIENT1 }; +#define WP_RSA_PARAM_KEY_FACTOR_INDEX1 3 +#define WP_RSA_PARAM_KEY_FACTOR_INDEX2 4 +#define WP_RSA_PARAM_KEY_EXPONENT_INDEX1 5 +#define WP_RSA_PARAM_KEY_EXPONENT_INDEX2 6 +#define WP_RSA_PARAM_KEY_COEFFICIENT_INDEX 7 /** * RSA PSS parameters. @@ -1074,6 +1079,27 @@ static int wp_rsa_match(const wp_Rsa* rsa1, const wp_Rsa* rsa2, int selection) return ok; } +#define VALIDATE_PRIMES_SIZE 133 +static const mp_digit validate_primes[VALIDATE_PRIMES_SIZE] = { + 0x0002, 0x0003, 0x0005, 0x0007, 0x000B, 0x000D, 0x0011, 0x0013, + 0x0017, 0x001D, 0x001F, 0x0025, 0x0029, 0x002B, 0x002F, 0x0035, + 0x003B, 0x003D, 0x0043, 0x0047, 0x0049, 0x004F, 0x0053, 0x0059, + 0x0061, 0x0065, 0x0067, 0x006B, 0x006D, 0x0071, 0x007F, 0x0083, + 0x0089, 0x008B, 0x0095, 0x0097, 0x009D, 0x00A3, 0x00A7, 0x00AD, + 0x00B3, 0x00B5, 0x00BF, 0x00C1, 0x00C5, 0x00C7, 0x00D3, 0x00DF, + 0x00E3, 0x00E5, 0x00E9, 0x00EF, 0x00F1, 0x00FB, 0x0101, 0x0107, + 0x010D, 0x010F, 0x0115, 0x0119, 0x011B, 0x0125, 0x0133, 0x0137, + 0x0139, 0x013D, 0x014B, 0x0151, 0x015B, 0x015D, 0x0161, 0x0167, + 0x016F, 0x0175, 0x017B, 0x017F, 0x0185, 0x018D, 0x0191, 0x0199, + 0x01A3, 0x01A5, 0x01AF, 0x01B1, 0x01B7, 0x01BB, 0x01C1, 0x01C9, + 0x01CD, 0x01CF, 0x01D3, 0x01DF, 0x01E7, 0x01EB, 0x01F3, 0x01F7, + 0x01FD, 0x0209, 0x020B, 0x021D, 0x0223, 0x022D, 0x0233, 0x0239, + 0x023B, 0x0241, 0x024B, 0x0251, 0x0257, 0x0259, 0x025F, 0x0265, + 0x0269, 0x026B, 0x0277, 0x0281, 0x0283, 0x0287, 0x028D, 0x0293, + 0x0295, 0x02A1, 0x02A5, 0x02AB, 0x02B3, 0x02BD, 0x02C5, 0x02CF, + 0x02D7, 0x02DD, 0x02E3, 0x02E7, 0x02EF, +}; + /** * Validate the RSA key. * @@ -1106,21 +1132,137 @@ static int wp_rsa_validate(const wp_Rsa* rsa, int selection, int checkType) else #endif if (checkPriv) { - if (mp_isone(&rsa->key.d) || mp_iszero((mp_int*)&rsa->key.d) || + if (mp_iszero((mp_int*)&rsa->key.d) || (mp_cmp((mp_int*)&rsa->key.d, (mp_int*)&rsa->key.n) != MP_LT)) { ok = 0; } } else if (checkPub) { - if (mp_iseven(&rsa->key.e) || mp_iszero((mp_int*)&rsa->key.e) || - mp_isone(&rsa->key.e)) { + int prime; + mp_int res; + + if (mp_iszero((mp_int*)&rsa->key.e) || mp_iszero((mp_int*)&rsa->key.n) || + mp_isone((mp_int*)&rsa->key.e) || mp_isone((mp_int*)&rsa->key.n) || + mp_iseven((mp_int*)&rsa->key.e) || mp_iseven((mp_int*)&rsa->key.n)) { ok = 0; } + + if (ok && mp_init(&res) != MP_OKAY) { + ok = 0; + } + else if (ok) { + for(prime = 0; prime < VALIDATE_PRIMES_SIZE; prime++) { + if (mp_set_int(&res, validate_primes[prime]) != MP_OKAY || + mp_mod((mp_int*)&rsa->key.n, &res, &res) != MP_OKAY || + mp_iszero(&res)) { + ok = 0; + break; + } + } + + mp_clear(&res); + } + } + + WOLFPROV_LEAVE(WP_LOG_COMP_RSA, __FILE__ ":" WOLFPROV_STRINGIZE(__LINE__), ok); + return ok; +} + +/** + * Copy an unsigned value from an OSSL param into a provided RSA key parameter. + * + * @param [out] mp RSA key parameter. + * @param [in] param Parameter to copy value from. + * @return Size of mp in bits as unsigned integer on success. + * @return -1 on failure. + */ +static int wp_rsa_import_store_unsigned(mp_int* mp, const OSSL_PARAM* param) +{ + int ok; + int bits = -1; + + WOLFPROV_ENTER(WP_LOG_COMP_RSA, "wp_rsa_import_store_unsigned"); + +#if OPENSSL_VERSION_NUMBER <= 0x30100080L + ok = param->data != NULL && param->data_type == OSSL_PARAM_UNSIGNED_INTEGER; +#else + ok = param->data != NULL && (param->data_type == OSSL_PARAM_INTEGER || + param->data_type == OSSL_PARAM_UNSIGNED_INTEGER); +#endif /* OPENSSL_VERSION_NUMBER <= 3.1.8 */ + + if (ok && !wp_mp_read_unsigned_bin_le(mp, param->data, param->data_size)) { + WOLFPROV_MSG(WP_LOG_COMP_RSA, + "Failed to read %s from parameters", param->key); + ok = 0; + } + + if (ok) { + bits = mp_count_bits(mp); + } + + /* Negative values are accepted by OSSL, for now just set to 0. + * Note that bits of signed value (as unsigned) are returned */ + if (ok && param->data_type == OSSL_PARAM_INTEGER) { + ok = mp_set(mp, 0) == MP_OKAY; + bits -= 8; } WOLFPROV_LEAVE(WP_LOG_COMP_RSA, __FILE__ ":" WOLFPROV_STRINGIZE(__LINE__), ok); + return bits; +} + +/** + * Based on the index into wp_rsa_param_key, increment the appropriate counter. + * + * @param [in] index Index associated with wp_rsa_param_key. + * @param [out] primes Count of prime parameters. + * @param [out] exps Count of exponent parameters. + * @param [out] coeffs Count of coefficient parameters. + */ +#if OPENSSL_VERSION_NUMBER < 0x30300000L +static void wp_rsa_import_increment_crt_counts(int index, int *primes, + int *exps, int *coeffs) +{ + if (index >= WP_RSA_PARAM_KEY_FACTOR_INDEX1 && + index <= WP_RSA_PARAM_KEY_FACTOR_INDEX2) { + *primes += 1; + } + else if (index >= WP_RSA_PARAM_KEY_EXPONENT_INDEX1 && + index <= WP_RSA_PARAM_KEY_EXPONENT_INDEX2) { + *exps += 1; + } + else if (index == WP_RSA_PARAM_KEY_COEFFICIENT_INDEX) { + *coeffs += 1; + } +} + +static int wp_rsa_import_verify_crt(int primes, int exps, int coeffs) +{ + int ok = 1; + + (void)exps; + (void)coeffs; + + if (primes > 0) { +#if (OPENSSL_VERSION_NUMBER < 0x30100040L && OPENSSL_VERSION_NUMBER > 0x30000120L) \ + || (OPENSSL_VERSION_NUMBER < 0x300000C0L) + if (primes < 2 || primes != exps || primes != coeffs + 1) { +#else + if (primes < 2) { +#endif /* (Ver < 3.1.4 && Ver > 3.0.18) || (Ver < 3.0.12) */ + WOLFPROV_MSG(WP_LOG_COMP_RSA, + "RSA factors provided but CRT parameters incomplete"); + ok = 0; + } + /* TODO: multi-prime checks */ + else if (primes > 2) { + ok = 0; + } + } + return ok; } +#endif /* OPENSSL_VERSION_NUMBER < 3.3.0 */ /** * Import the key data into RSA key object from parameters. @@ -1139,18 +1281,39 @@ static int wp_rsa_import_key_data(wp_Rsa* rsa, const OSSL_PARAM params[], int cnt = 0; mp_int* mp = NULL; const OSSL_PARAM* p = NULL; + const OSSL_PARAM* n; + const OSSL_PARAM* e; + const OSSL_PARAM* d = NULL; + int bits; + int nbits; +#if OPENSSL_VERSION_NUMBER < 0x30300000L + int primes = 0; + int exps = 0; + int coeffs = 0; +#endif /* OPENSSL_VERSION_NUMBER < 3.3.0 */ WOLFPROV_ENTER(WP_LOG_COMP_RSA, "wp_rsa_import_key_data"); /* N and E params are the only ones required by OSSL, so match that. * See ossl_rsa_fromdata() and RSA_set0_key() in OpenSSL. */ - if (OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_RSA_N) == NULL || - OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_RSA_E) == NULL) { + if ((n = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_RSA_N)) == NULL || + n->data == NULL || + (e = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_RSA_E)) == NULL || + e->data == NULL) { WOLFPROV_MSG(WP_LOG_COMP_RSA, "Param N or E is missing"); ok = 0; } + if (ok && priv) { + d = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_RSA_D); + } if (ok) { + mp = &rsa->key.n; + nbits = wp_rsa_import_store_unsigned(mp, n); + ok = nbits != -1; + } + + if (ok && d != NULL) { cnt = wp_params_count(params); rsa->key.type = priv ? RSA_PRIVATE : RSA_PUBLIC; @@ -1160,7 +1323,11 @@ static int wp_rsa_import_key_data(wp_Rsa* rsa, const OSSL_PARAM params[], index = -1; for (j = 0; j < (int)ARRAY_SIZE(wp_rsa_param_key); j++) { if (XSTRNCMP(p->key, wp_rsa_param_key[j], XSTRLEN(p->key)) == 0) { - index = j; + index = j; +#if OPENSSL_VERSION_NUMBER < 0x30300000L + wp_rsa_import_increment_crt_counts(index, &primes, &exps, + &coeffs); +#endif /* OPENSSL_VERSION_NUMBER < 3.3.0 */ break; } } @@ -1172,15 +1339,33 @@ static int wp_rsa_import_key_data(wp_Rsa* rsa, const OSSL_PARAM params[], } /* Read the value into the rsa struct */ - if (ok) { + if (ok && p != n) { mp = (mp_int*)(((byte*)&rsa->key) + wp_rsa_offset[index]); - if (!wp_mp_read_unsigned_bin_le(mp, p->data, p->data_size)) { - WOLFPROV_MSG(WP_LOG_COMP_RSA, - "Failed to read %s from parameters", p->key); - ok = 0; - } + bits = wp_rsa_import_store_unsigned(mp, p); +#if OPENSSL_VERSION_NUMBER >= 0x30400000L + ok = bits != -1 && bits <= nbits; +#else + ok = bits != -1; +#endif /* OPENSSL_VERSION_NUMBER >= 3.4.0 */ } } + +/* TODO: need to generate exponents and coefficients beforehand + * in newer versions, so skip check for now */ +#if OPENSSL_VERSION_NUMBER < 0x30300000L + if (ok) { + ok = wp_rsa_import_verify_crt(primes, exps, coeffs); + } +#endif /* OPENSSL_VERSION_NUMBER < 3.3.0 */ + } + else if (ok && d == NULL) { + mp = &rsa->key.e; + bits = wp_rsa_import_store_unsigned(mp, e); +#if OPENSSL_VERSION_NUMBER >= 0x30400000L + ok = bits != -1 && bits <= nbits; +#else + ok = bits != -1; +#endif /* OPENSSL_VERSION_NUMBER >= 3.4.0 */ } WOLFPROV_LEAVE(WP_LOG_COMP_RSA, __FILE__ ":" WOLFPROV_STRINGIZE(__LINE__), ok); diff --git a/test/test_rsa.c b/test/test_rsa.c index c7042121..f49834de 100644 --- a/test/test_rsa.c +++ b/test/test_rsa.c @@ -24,6 +24,7 @@ #include #include +#include #ifdef WP_HAVE_RSA @@ -1379,12 +1380,17 @@ int test_rsa_fromdata(void* data) #ifdef EVP_PKEY_PRIVATE_KEY EVP_PKEY_PRIVATE_KEY, /* added in 3.0.12 and 3.1.4 */ #endif + EVP_PKEY_KEY_PARAMETERS, }; /* Parameter data fields */ unsigned long rsa_n = 0xbc747fc5; unsigned long rsa_e = 0x10001; unsigned long rsa_d = 0x7b133399; +#if OPENSSL_VERSION_NUMBER >= 0x30200010L + unsigned long rsa_p = 0x397; + unsigned long rsa_q = 0x3a9; +#endif const char *foo = "some string"; size_t foo_l = strlen(foo); const char bar[] = "some other string"; @@ -1441,6 +1447,40 @@ int test_rsa_fromdata(void* data) { "bar", OSSL_PARAM_UTF8_STRING, (void *)&bar, sizeof(bar) - 1, 0 }, OSSL_PARAM_END }; +#if OPENSSL_VERSION_NUMBER >= 0x30200010L + OSSL_PARAM params_null[] = { + OSSL_PARAM_ulong("n", NULL), + OSSL_PARAM_ulong("e", NULL), + OSSL_PARAM_ulong("d", NULL), + { "foo", OSSL_PARAM_UTF8_PTR, NULL, foo_l, 0 }, + { "bar", OSSL_PARAM_UTF8_STRING, NULL, sizeof(bar) - 1, 0 }, + OSSL_PARAM_END + }; + OSSL_PARAM params_nedpq[] = { + OSSL_PARAM_ulong("n", &rsa_n), + OSSL_PARAM_ulong("e", &rsa_e), + OSSL_PARAM_ulong("d", &rsa_d), + OSSL_PARAM_ulong("rsa-factor1", &rsa_p), + OSSL_PARAM_ulong("rsa-factor2", &rsa_q), + OSSL_PARAM_END + }; + OSSL_PARAM params_nepq_null[] = { + OSSL_PARAM_ulong("n", &rsa_n), + OSSL_PARAM_ulong("e", &rsa_e), + OSSL_PARAM_ulong("d", NULL), + OSSL_PARAM_ulong("rsa-factor1", &rsa_p), + OSSL_PARAM_ulong("rsa-factor2", &rsa_q), + OSSL_PARAM_END + }; + OSSL_PARAM params_nedq_null[] = { + OSSL_PARAM_ulong("n", &rsa_n), + OSSL_PARAM_ulong("e", &rsa_e), + OSSL_PARAM_ulong("d", &rsa_d), + OSSL_PARAM_ulong("rsa-factor1", NULL), + OSSL_PARAM_ulong("rsa-factor2", &rsa_q), + OSSL_PARAM_END + }; +#endif /* OPENSSL_VERSION_NUMBER >= 3.2.1 */ OSSL_PARAM* params_table[] = { params_none, params_n, @@ -1452,13 +1492,19 @@ int test_rsa_fromdata(void* data) params_ned, params_extra_ulong, params_extra_str, +#if OPENSSL_VERSION_NUMBER >= 0x30200010L + params_null, + params_nedpq, + params_nepq_null, + params_nedq_null, +#endif /* OPENSSL_VERSION_NUMBER >= 3.2.1 */ }; for (unsigned i = 0; i < ARRAY_SIZE(selections); i++) { for (unsigned j = 0; j < ARRAY_SIZE(params_table); j++) { - int status_wolf = EVP_PKEY_fromdata(ctx_wolf, &pkey_wolf, + int status_wolf = EVP_PKEY_fromdata(ctx_wolf, &pkey_wolf, selections[i], ¶ms_table[j][0]); - int status_ossl = EVP_PKEY_fromdata(ctx_ossl, &pkey_ossl, + int status_ossl = EVP_PKEY_fromdata(ctx_ossl, &pkey_ossl, selections[i], ¶ms_table[j][0]); if (status_wolf != status_ossl) { @@ -1470,16 +1516,16 @@ int test_rsa_fromdata(void* data) else if (status_wolf == 1) { PRINT_MSG("EVP_PKEY_fromdata (wolf) succeeded for " "selection %d (0x%08X) and params %d", - i, selections[i], j); + i, selections[i], j); if (EVP_PKEY_cmp(pkey_wolf, pkey_ossl) != 1) { PRINT_MSG("EVP_PKEY_cmp failed for selection %d " - "(0x%08X)", i, selections[i]); + "(0x%08X)", i, selections[i]); err = 1; } if (EVP_PKEY_cmp_parameters(pkey_wolf, pkey_ossl) != 1) { PRINT_MSG("EVP_PKEY_cmp_parameters failed for " - "selection %d (0x%08X)", i, selections[i]); + "selection %d (0x%08X)", i, selections[i]); err = 1; } } @@ -1867,4 +1913,290 @@ int test_rsa_null_init(void* data) return err; } +static int test_rsa_key_integrity_helper(OSSL_PARAM_BLD* bld, BIGNUM* p, + BIGNUM* q, BIGNUM* n, BIGNUM* e, BIGNUM* d) +{ + int err = 0; + int ossl_err; + int wolf_err; + OSSL_PARAM* params = NULL; + EVP_PKEY_CTX* ctx_ossl = NULL; + EVP_PKEY_CTX* ctx_wolf = NULL; + EVP_PKEY* pkey_ossl = NULL; + EVP_PKEY* pkey_wolf = NULL; + + if (err == 0) { + err |= OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_FACTOR1, p) != 1; + err |= OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_FACTOR2, q) != 1; + err |= OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_N, n) != 1; + err |= OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_E, e) != 1; + err |= OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_D, d) != 1; + } + if (err == 0) { + params = OSSL_PARAM_BLD_to_param(bld); + err = params == NULL; + } + + if (err == 0) { + ctx_ossl = EVP_PKEY_CTX_new_from_name(osslLibCtx, "RSA", NULL); + ctx_wolf = EVP_PKEY_CTX_new_from_name(wpLibCtx, "RSA", NULL); + err = (ctx_ossl == NULL) || (ctx_wolf == NULL); + } + if (err == 0) { + err |= EVP_PKEY_fromdata_init(ctx_ossl) != 1; + err |= EVP_PKEY_fromdata_init(ctx_wolf) != 1; + } + if (err == 0) { + ossl_err = EVP_PKEY_fromdata(ctx_ossl, &pkey_ossl, EVP_PKEY_KEYPAIR, + params); + wolf_err = EVP_PKEY_fromdata(ctx_wolf, &pkey_wolf, EVP_PKEY_KEYPAIR, + params); + + if (ossl_err != wolf_err) { + PRINT_MSG("EVP_PKEY_fromdata status mismatch (OSSL %d WOLF %d)", + ossl_err, wolf_err); + err = 1; + } + else if (wolf_err == 0) { + PRINT_MSG("Successfully failed to load keys"); + err = -1; + } + else if (wolf_err == 1) { + if (EVP_PKEY_eq(pkey_ossl, pkey_wolf) != 1 || + EVP_PKEY_parameters_eq(pkey_ossl, pkey_wolf) != 1) { + PRINT_MSG("EVP_PKEY_eq / EVP_PKEY_parameters_eq failed"); + err = 1; + } + } + } + + if (err == 0) { + EVP_PKEY_CTX_free(ctx_ossl); + EVP_PKEY_CTX_free(ctx_wolf); + ctx_ossl = EVP_PKEY_CTX_new_from_pkey(osslLibCtx, pkey_ossl, NULL); + ctx_wolf = EVP_PKEY_CTX_new_from_pkey(wpLibCtx, pkey_wolf, NULL); + err = (ctx_ossl == NULL) || (ctx_wolf == NULL); + } + if (err == 0) { + ossl_err = EVP_PKEY_param_check(ctx_ossl) != 1; + wolf_err = EVP_PKEY_param_check(ctx_wolf) != 1; + if (ossl_err != wolf_err){ + PRINT_MSG("param_check err: OSSL %d WOLF %d", ossl_err, wolf_err); + err |= 1; + } + + ossl_err = EVP_PKEY_public_check(ctx_ossl) != 1; + wolf_err = EVP_PKEY_public_check(ctx_wolf) != 1; + if (ossl_err != wolf_err){ + PRINT_MSG("public_check err: OSSL %d WOLF %d", ossl_err, wolf_err); + err |= 1; + } + + ossl_err = EVP_PKEY_private_check(ctx_ossl) != 1; + wolf_err = EVP_PKEY_private_check(ctx_wolf) != 1; + if (ossl_err != wolf_err){ + PRINT_MSG("private_check err: OSSL %d WOLF %d", ossl_err, wolf_err); + err |= 1; + } + +#ifdef WOLFSSL_RSA_KEY_CHECK + ossl_err = EVP_PKEY_pairwise_check(ctx_ossl) != 1; + wolf_err = EVP_PKEY_pairwise_check(ctx_wolf) != 1; + if (ossl_err != wolf_err){ + PRINT_MSG("pairwise_check err: OSSL %d WOLF %d", ossl_err, wolf_err); + err |= 1; + } +#endif + } + + EVP_PKEY_free(pkey_ossl); + EVP_PKEY_free(pkey_wolf); + EVP_PKEY_CTX_free(ctx_ossl); + EVP_PKEY_CTX_free(ctx_wolf); + OSSL_PARAM_free(params); + + return err == 1; +} + +int test_rsa_key_integrity(void* data) +{ + (void)data; + int err = 0; + PKCS8_PRIV_KEY_INFO* p8inf = NULL; + OSSL_PARAM_BLD* bld = NULL; + EVP_PKEY* pkey = NULL; + BIGNUM* p = NULL; + BIGNUM* q = NULL; + BIGNUM* n = NULL; + BIGNUM* e = NULL; + BIGNUM* d = NULL; + BIGNUM* bignums[5]; + size_t loop; + BIGNUM* num = NULL; + BN_ULONG num_val; + const unsigned char* pder = rsa_key_der_2048_pkcs8; + + /* steal parameters from existing key so they can be fuzzed */ + p8inf = d2i_PKCS8_PRIV_KEY_INFO(NULL, (const unsigned char **)&pder, + sizeof(rsa_key_der_2048_pkcs8)); + err = p8inf == NULL; + if (err == 0) { + pkey = EVP_PKCS82PKEY_ex(p8inf, osslLibCtx, NULL); + err = pkey == NULL; + } + if (err == 0) { + err |= EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_RSA_FACTOR1, &p) != 1; + err |= EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_RSA_FACTOR2, &q) != 1; + err |= EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_RSA_N, &n) != 1; + err |= EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_RSA_E, &e) != 1; + err |= EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_RSA_D, &d) != 1; + err |= (p == NULL) || (q == NULL) || (n == NULL) || + (e == NULL) || (d == NULL); + bignums[0] = p; + bignums[1] = q; + bignums[2] = n; + bignums[3] = e; + bignums[4] = d; + } + if (err == 0) { + bld = OSSL_PARAM_BLD_new(); + err = bld == NULL; + } + + if (err == 0) { + PRINT_MSG("Validate with valid key"); + err = test_rsa_key_integrity_helper(bld, p, q, n, e, d); + } + + if (err == 0) { + num = BN_new(); + err = num == NULL; + } + + for (loop = 0; err == 0 && loop < ARRAY_SIZE(bignums); loop++) { + switch (loop) { + case 0: + PRINT_MSG("Validate with p -= 1, 2"); + break; + case 1: + PRINT_MSG("Validate with q -= 1, 2"); + break; + case 2: + PRINT_MSG("Validate with n -= 1, 2"); + break; + case 3: + PRINT_MSG("Validate with e -= 1, 2"); + break; + case 4: + PRINT_MSG("Validate with d -= 1, 2"); + break; + default: + err = 1; + break; + } + + for (num_val = 1; err == 0 && num_val <= 2; num_val++) { + BIGNUM* bignum = bignums[loop]; + err = BN_set_word(num, num_val) != 1; + + PRINT_MSG("%lu:", num_val); + if (BN_sub(bignum, bignum, num) != 1) { + err = 1; + } + else if ((err = test_rsa_key_integrity_helper(bld, p, q, n, e, d)) == 0) { + err = BN_add(bignum, bignum, num) != 1; + } + } + } + + for (loop = 0; err == 0 && loop < ARRAY_SIZE(bignums); loop++) { + switch (loop) { + case 0: + PRINT_MSG("Validate with p = 0, 1, 2, 3"); + break; + case 1: + PRINT_MSG("Validate with q = 0, 1, 2, 3"); + break; + case 2: + PRINT_MSG("Validate with n = 0, 1, 2, 3"); + break; + case 3: + PRINT_MSG("Validate with e = 0, 1, 2, 3"); + break; + case 4: + PRINT_MSG("Validate with d = 0, 1, 2, 3"); + break; + default: + err = 1; + break; + } + + for (num_val = 0; err == 0 && num_val <= 3; num_val++) { + BIGNUM* bignum = bignums[loop]; + err = BN_copy(num, bignum) == NULL; + + PRINT_MSG("%lu:", num_val); + if (err != 1 && (BN_set_word(bignum, num_val) != 1 || + test_rsa_key_integrity_helper(bld, p, q, n, e, d) != 0)) { + err = 1; + } + if (err != 1 && BN_copy(bignum, num) == NULL) { + err = 1; + } + } + } + +/* OSSL_PARAM_BLD does not support negative BN's until after 3.1.8 */ +#if OPENSSL_VERSION_NUMBER > 0x30100080L + for (loop = 0; err == 0 && loop < ARRAY_SIZE(bignums); loop++) { + switch (loop) { + case 0: + PRINT_MSG("Validate with negative p"); + break; + case 1: + PRINT_MSG("Validate with negative q"); + break; + case 2: + PRINT_MSG("Validate with negative n"); + break; + case 3: + PRINT_MSG("Validate with negative e"); + break; + case 4: + PRINT_MSG("Validate with negative d"); + break; + default: + err = 1; + break; + } + + BIGNUM* bignum = bignums[loop]; + BN_set_negative(bignum, 1); + + if (err != 1 && test_rsa_key_integrity_helper(bld, p, q, n, e, d) != 0) { + err = 1; + } + + BN_set_negative(bignum, 0); + } +#endif /* OPENSSL_VERSION_NUMBER > 3.1.8 */ + + if (err == 0) { + PRINT_MSG("Validate with NULL values"); + err = test_rsa_key_integrity_helper(bld, NULL, NULL, NULL, NULL, NULL); + } + + BN_free(p); + BN_free(q); + BN_free(n); + BN_free(e); + BN_free(d); + BN_free(num); + PKCS8_PRIV_KEY_INFO_free(p8inf); + EVP_PKEY_free(pkey); + OSSL_PARAM_BLD_free(bld); + + return err; +} + #endif /* WP_HAVE_RSA */ diff --git a/test/unit.c b/test/unit.c index bac908ac..4d740831 100644 --- a/test/unit.c +++ b/test/unit.c @@ -308,6 +308,7 @@ TEST_CASE test_case[] = { TEST_DECL(test_rsa_fromdata, NULL), TEST_DECL(test_rsa_decode, NULL), TEST_DECL(test_rsa_null_init, NULL), + TEST_DECL(test_rsa_key_integrity, NULL), #endif /* WP_HAVE_RSA */ #ifdef WP_HAVE_EC_P192 #ifdef WP_HAVE_ECKEYGEN diff --git a/test/unit.h b/test/unit.h index 20f66af4..9cd7c7d6 100644 --- a/test/unit.h +++ b/test/unit.h @@ -268,6 +268,7 @@ int test_rsa_load_cert(void* data); int test_rsa_fromdata(void* data); int test_rsa_decode(void* data); int test_rsa_null_init(void* data); +int test_rsa_key_integrity(void* data); #endif /* WP_HAVE_RSA */ #ifdef WP_HAVE_DH