@@ -203,6 +203,11 @@ static const char* wp_rsa_param_key[WP_RSA_PARAM_NUMS_CNT] = {
203203 OSSL_PKEY_PARAM_RSA_EXPONENT1 , OSSL_PKEY_PARAM_RSA_EXPONENT2 ,
204204 OSSL_PKEY_PARAM_RSA_COEFFICIENT1
205205};
206+ #define WP_RSA_PARAM_KEY_FACTOR_INDEX1 3
207+ #define WP_RSA_PARAM_KEY_FACTOR_INDEX2 4
208+ #define WP_RSA_PARAM_KEY_EXPONENT_INDEX1 5
209+ #define WP_RSA_PARAM_KEY_EXPONENT_INDEX2 6
210+ #define WP_RSA_PARAM_KEY_COEFFICIENT_INDEX 7
206211
207212/**
208213 * RSA PSS parameters.
@@ -1186,6 +1191,29 @@ static int wp_rsa_match(const wp_Rsa* rsa1, const wp_Rsa* rsa2, int selection)
11861191 return ok ;
11871192}
11881193
1194+ /* These are the primes used by OSSL to reject an RSA N with a small GCD */
1195+ static const mp_digit validate_primes [] = {
1196+ 0x0002 , 0x0003 , 0x0005 , 0x0007 , 0x000B , 0x000D , 0x0011 , 0x0013 ,
1197+ 0x0017 , 0x001D , 0x001F , 0x0025 , 0x0029 , 0x002B , 0x002F , 0x0035 ,
1198+ 0x003B , 0x003D , 0x0043 , 0x0047 , 0x0049 , 0x004F , 0x0053 , 0x0059 ,
1199+ 0x0061 , 0x0065 , 0x0067 , 0x006B , 0x006D , 0x0071 , 0x007F , 0x0083 ,
1200+ 0x0089 , 0x008B , 0x0095 , 0x0097 , 0x009D , 0x00A3 , 0x00A7 , 0x00AD ,
1201+ 0x00B3 , 0x00B5 , 0x00BF , 0x00C1 , 0x00C5 , 0x00C7 , 0x00D3 , 0x00DF ,
1202+ 0x00E3 , 0x00E5 , 0x00E9 , 0x00EF , 0x00F1 , 0x00FB , 0x0101 , 0x0107 ,
1203+ 0x010D , 0x010F , 0x0115 , 0x0119 , 0x011B , 0x0125 , 0x0133 , 0x0137 ,
1204+ 0x0139 , 0x013D , 0x014B , 0x0151 , 0x015B , 0x015D , 0x0161 , 0x0167 ,
1205+ 0x016F , 0x0175 , 0x017B , 0x017F , 0x0185 , 0x018D , 0x0191 , 0x0199 ,
1206+ 0x01A3 , 0x01A5 , 0x01AF , 0x01B1 , 0x01B7 , 0x01BB , 0x01C1 , 0x01C9 ,
1207+ 0x01CD , 0x01CF , 0x01D3 , 0x01DF , 0x01E7 , 0x01EB , 0x01F3 , 0x01F7 ,
1208+ 0x01FD , 0x0209 , 0x020B , 0x021D , 0x0223 , 0x022D , 0x0233 , 0x0239 ,
1209+ 0x023B , 0x0241 , 0x024B , 0x0251 , 0x0257 , 0x0259 , 0x025F , 0x0265 ,
1210+ 0x0269 , 0x026B , 0x0277 , 0x0281 , 0x0283 , 0x0287 , 0x028D , 0x0293 ,
1211+ 0x0295 , 0x02A1 , 0x02A5 , 0x02AB , 0x02B3 , 0x02BD , 0x02C5 , 0x02CF ,
1212+ 0x02D7 , 0x02DD , 0x02E3 , 0x02E7 , 0x02EF ,
1213+ };
1214+ #define VALIDATE_PRIMES_SIZE XELEM_CNT(validate_primes)
1215+ wc_static_assert (VALIDATE_PRIMES_SIZE == 133 );
1216+
11891217/**
11901218 * Validate the RSA key.
11911219 *
@@ -1214,26 +1242,229 @@ static int wp_rsa_validate(const wp_Rsa* rsa, int selection, int checkType)
12141242 WOLFPROV_MSG_DEBUG_RETCODE (WP_LOG_LEVEL_DEBUG , "wc_CheckRsaKey" , rc );
12151243 ok = 0 ;
12161244 }
1245+
1246+ /* wc_CheckRsaKey runs the private key operation and only bounds-checks
1247+ * d (d < n). Check d*e = 1 mod lcm(p-1,q-1) to match OSSL */
1248+ if (ok && !mp_iszero ((mp_int * )& rsa -> key .p ) &&
1249+ !mp_iszero ((mp_int * )& rsa -> key .q )) {
1250+ mp_int m ; /* p-1 then q-1 */
1251+ mp_int r ; /* d*e mod m */
1252+
1253+ if (mp_init (& m ) != MP_OKAY ) {
1254+ ok = 0 ;
1255+ }
1256+ else if (mp_init (& r ) != MP_OKAY ) {
1257+ mp_clear (& m );
1258+ ok = 0 ;
1259+ }
1260+ else {
1261+ if (mp_sub_d ((mp_int * )& rsa -> key .p , 1 , & m ) != MP_OKAY ||
1262+ mp_mulmod ((mp_int * )& rsa -> key .d , (mp_int * )& rsa -> key .e ,
1263+ & m , & r ) != MP_OKAY || !mp_isone (& r )) {
1264+ ok = 0 ;
1265+ }
1266+ else if (mp_sub_d ((mp_int * )& rsa -> key .q , 1 , & m ) != MP_OKAY ||
1267+ mp_mulmod ((mp_int * )& rsa -> key .d , (mp_int * )& rsa -> key .e ,
1268+ & m , & r ) != MP_OKAY || !mp_isone (& r )) {
1269+ ok = 0 ;
1270+ }
1271+ mp_forcezero (& r );
1272+ mp_forcezero (& m );
1273+ mp_clear (& r );
1274+ mp_clear (& m );
1275+ }
1276+ }
12171277 }
12181278 else
12191279#endif
12201280 if (checkPriv ) {
1221- if (mp_isone ( & rsa -> key . d ) || mp_iszero ((mp_int * )& rsa -> key .d ) ||
1222- (mp_cmp ((mp_int * )& rsa -> key .d , (mp_int * )& rsa -> key .n ) != MP_LT )) {
1281+ if (mp_iszero ((mp_int * )& rsa -> key .d ) ||
1282+ (mp_cmp ((mp_int * )& rsa -> key .d , (mp_int * )& rsa -> key .n ) != MP_LT )) {
12231283 ok = 0 ;
12241284 }
12251285 }
12261286 else if (checkPub ) {
1227- if (mp_iseven (& rsa -> key .e ) || mp_iszero ((mp_int * )& rsa -> key .e ) ||
1228- mp_isone (& rsa -> key .e )) {
1287+ int nbits = mp_count_bits ((mp_int * )& rsa -> key .n );
1288+ mp_int res ;
1289+
1290+ if (mp_iszero ((mp_int * )& rsa -> key .e ) || mp_iszero ((mp_int * )& rsa -> key .n ) ||
1291+ mp_isone ((mp_int * )& rsa -> key .e ) || mp_isone ((mp_int * )& rsa -> key .n ) ||
1292+ mp_iseven ((mp_int * )& rsa -> key .e ) || mp_iseven ((mp_int * )& rsa -> key .n )) {
1293+ ok = 0 ;
1294+ }
1295+
1296+ if (ok && nbits > OPENSSL_RSA_MAX_MODULUS_BITS ) {
1297+ ok = 0 ;
1298+ }
1299+
1300+ #ifdef HAVE_FIPS
1301+ if (ok && (wolfProvider_GetFipsChecks () & WP_FIPS_CHECK_RSA_KEY_SIZE )) {
1302+ /* n must have at least 112 bits of security, and
1303+ * 2^16 < e < 2^256 */
1304+ int ebits = mp_count_bits ((mp_int * )& rsa -> key .e );
1305+ if (nbits < 2048 || ebits <= 16 || ebits >= 257 ) {
1306+ ok = 0 ;
1307+ }
1308+ }
1309+ #endif
1310+
1311+ /* Reject a modulus that shares a small prime factor */
1312+ if (ok && mp_init (& res ) != MP_OKAY ) {
1313+ ok = 0 ;
1314+ }
1315+ else if (ok ) {
1316+ unsigned int prime ;
1317+ for (prime = 0 ; prime < VALIDATE_PRIMES_SIZE ; prime ++ ) {
1318+ if (mp_set_int (& res , validate_primes [prime ]) != MP_OKAY ||
1319+ mp_mod ((mp_int * )& rsa -> key .n , & res , & res ) != MP_OKAY ||
1320+ mp_iszero (& res )) {
1321+ ok = 0 ;
1322+ break ;
1323+ }
1324+ }
1325+
1326+ mp_clear (& res );
1327+ }
1328+
1329+ #if (!defined(WOLFSSL_SP_MATH ) && !defined(WOLFSSL_SP_MATH_ALL )) || \
1330+ defined(WOLFSSL_SP_PRIME_GEN )
1331+ /* Reject prime n since it is not composite.
1332+ * Note this does not catch a prime power (p^k), which OSSL
1333+ * also rejects. */
1334+ if (ok ) {
1335+ int isPrime = MP_NO ;
1336+ if (mp_prime_is_prime ((mp_int * )& rsa -> key .n , 5 , & isPrime ) != MP_OKAY ||
1337+ isPrime == MP_YES ) {
1338+ ok = 0 ;
1339+ }
1340+ }
1341+ #endif
1342+ }
1343+
1344+ WOLFPROV_LEAVE (WP_LOG_COMP_RSA , __FILE__ ":" WOLFPROV_STRINGIZE (__LINE__ ), ok );
1345+ return ok ;
1346+ }
1347+
1348+ /**
1349+ * Copy an unsigned value from an OSSL param into a provided RSA key parameter.
1350+ * Negative values result in an error.
1351+ *
1352+ * @param [out] mp RSA key parameter.
1353+ * @param [in] param Parameter to copy value from.
1354+ * @return Size of mp in bits on success.
1355+ * @return -1 on failure.
1356+ */
1357+ static int wp_rsa_import_store_unsigned (mp_int * mp , const OSSL_PARAM * param )
1358+ {
1359+ int ok ;
1360+ int bits = -1 ;
1361+ const unsigned char * data ;
1362+
1363+ WOLFPROV_ENTER (WP_LOG_COMP_RSA , "wp_rsa_import_store_unsigned" );
1364+
1365+ #if OPENSSL_VERSION_NUMBER <= 0x30100080L
1366+ ok = param -> data != NULL && param -> data_type == OSSL_PARAM_UNSIGNED_INTEGER ;
1367+ #else
1368+ ok = param -> data != NULL && (param -> data_type == OSSL_PARAM_INTEGER ||
1369+ param -> data_type == OSSL_PARAM_UNSIGNED_INTEGER );
1370+ #endif /* OPENSSL_VERSION_NUMBER <= 3.1.8 */
1371+
1372+ if (ok && !wp_mp_read_unsigned_bin_le (mp , param -> data , param -> data_size )) {
1373+ WOLFPROV_MSG (WP_LOG_COMP_RSA ,
1374+ "Failed to read %s from parameters" , param -> key );
1375+ ok = 0 ;
1376+ }
1377+
1378+ if (ok ) {
1379+ bits = mp_count_bits (mp );
1380+ }
1381+
1382+ /* OSSL accepts negative values for these params, but this doesn't work well
1383+ * in wolfProvider so reject it. */
1384+ if (ok && param -> data_type == OSSL_PARAM_INTEGER ) {
1385+ data = (const unsigned char * )param -> data ;
1386+ /* Assume little-endian when determining sign */
1387+ if (param -> data_size > 0 && (data [param -> data_size - 1 ] & 0x80 ) != 0 ) {
1388+ WOLFPROV_MSG (WP_LOG_COMP_RSA , "Negative value for %s rejected" ,
1389+ param -> key );
12291390 ok = 0 ;
12301391 }
12311392 }
12321393
12331394 WOLFPROV_LEAVE (WP_LOG_COMP_RSA , __FILE__ ":" WOLFPROV_STRINGIZE (__LINE__ ), ok );
1395+ return ok ? bits : -1 ;
1396+ }
1397+
1398+ /**
1399+ * Based on the index into wp_rsa_param_key, increment the appropriate counter.
1400+ *
1401+ * @param [in] index Index associated with wp_rsa_param_key.
1402+ * @param [out] primes Count of prime parameters.
1403+ * @param [out] exps Count of exponent parameters.
1404+ * @param [out] coeffs Count of coefficient parameters.
1405+ */
1406+ static void wp_rsa_import_increment_crt_counts (int index , int * primes ,
1407+ int * exps , int * coeffs )
1408+ {
1409+ if (index >= WP_RSA_PARAM_KEY_FACTOR_INDEX1 &&
1410+ index <= WP_RSA_PARAM_KEY_FACTOR_INDEX2 ) {
1411+ * primes += 1 ;
1412+ }
1413+ else if (index >= WP_RSA_PARAM_KEY_EXPONENT_INDEX1 &&
1414+ index <= WP_RSA_PARAM_KEY_EXPONENT_INDEX2 ) {
1415+ * exps += 1 ;
1416+ }
1417+ else if (index == WP_RSA_PARAM_KEY_COEFFICIENT_INDEX ) {
1418+ * coeffs += 1 ;
1419+ }
1420+ }
1421+
1422+ /**
1423+ * Verify the RSA CRT parameters supplied for a private key import are complete.
1424+ *
1425+ * @param [in] primes Count of prime factor parameters provided.
1426+ * @param [in] exps Count of CRT exponent parameters provided.
1427+ * @param [in] coeffs Count of CRT coefficient parameters provided.
1428+ * @return 1 when the CRT parameters are acceptable
1429+ * @return 0 when the provided factors/CRT parameters are incomplete or
1430+ * unsupported.
1431+ */
1432+ static int wp_rsa_import_verify_crt (int primes , int exps , int coeffs )
1433+ {
1434+ int ok = 1 ;
1435+
1436+ (void )exps ;
1437+ (void )coeffs ;
1438+
1439+ if (primes > 0 ) {
1440+ #if (OPENSSL_VERSION_NUMBER < 0x30100040L && OPENSSL_VERSION_NUMBER > 0x30000120L ) \
1441+ || (OPENSSL_VERSION_NUMBER < 0x300000C0L )
1442+ if (primes < 2 || primes != exps || primes != coeffs + 1 ) {
1443+ #else
1444+ if (primes < 2 ) {
1445+ #endif /* (Ver < 3.1.4 && Ver > 3.0.18) || (Ver < 3.0.12) */
1446+ WOLFPROV_MSG (WP_LOG_COMP_RSA ,
1447+ "RSA factors provided but CRT parameters incomplete" );
1448+ ok = 0 ;
1449+ }
1450+ /* TODO: multi-prime checks */
1451+ else if (primes > 2 ) {
1452+ WOLFPROV_MSG (WP_LOG_COMP_RSA , "RSA multi-prime import failed" );
1453+ ok = 0 ;
1454+ }
1455+ }
1456+
12341457 return ok ;
12351458}
12361459
1460+ #if OPENSSL_VERSION_NUMBER >= 0x30400000L
1461+ #define wp_rsa_import_check_bits (bits , nbits ) \
1462+ ((bits) != -1 && (bits) <= (nbits))
1463+ #else
1464+ #define wp_rsa_import_check_bits (bits , nbits ) \
1465+ ((bits) != -1)
1466+ #endif /* OPENSSL_VERSION_NUMBER >= 3.4.0 */
1467+
12371468/**
12381469 * Import the key data into RSA key object from parameters.
12391470 *
@@ -1251,20 +1482,39 @@ static int wp_rsa_import_key_data(wp_Rsa* rsa, const OSSL_PARAM params[],
12511482 int cnt = 0 ;
12521483 mp_int * mp = NULL ;
12531484 const OSSL_PARAM * p = NULL ;
1485+ const OSSL_PARAM * n ;
1486+ const OSSL_PARAM * e ;
1487+ const OSSL_PARAM * d = NULL ;
1488+ int bits = -1 ;
1489+ int nbits = -1 ;
1490+ int primes = 0 ;
1491+ int exps = 0 ;
1492+ int coeffs = 0 ;
12541493
12551494 WOLFPROV_ENTER (WP_LOG_COMP_RSA , "wp_rsa_import_key_data" );
12561495
12571496 /* N and E params are the only ones required by OSSL, so match that.
12581497 * See ossl_rsa_fromdata() and RSA_set0_key() in OpenSSL. */
1259- if (OSSL_PARAM_locate_const (params , OSSL_PKEY_PARAM_RSA_N ) == NULL ||
1260- OSSL_PARAM_locate_const (params , OSSL_PKEY_PARAM_RSA_E ) == NULL ) {
1498+ if ((n = OSSL_PARAM_locate_const (params , OSSL_PKEY_PARAM_RSA_N )) == NULL ||
1499+ n -> data == NULL ||
1500+ (e = OSSL_PARAM_locate_const (params , OSSL_PKEY_PARAM_RSA_E )) == NULL ||
1501+ e -> data == NULL ) {
12611502 WOLFPROV_MSG (WP_LOG_COMP_RSA , "Param N or E is missing" );
12621503 ok = 0 ;
12631504 }
1505+ if (ok && priv ) {
1506+ d = OSSL_PARAM_locate_const (params , OSSL_PKEY_PARAM_RSA_D );
1507+ }
12641508
12651509 if (ok ) {
1266- cnt = wp_params_count (params );
12671510 rsa -> key .type = priv ? RSA_PRIVATE : RSA_PUBLIC ;
1511+ mp = & rsa -> key .n ;
1512+ nbits = wp_rsa_import_store_unsigned (mp , n );
1513+ ok = nbits != -1 ;
1514+ }
1515+
1516+ if (ok && d != NULL ) {
1517+ cnt = wp_params_count (params );
12681518
12691519 for (i = 0 ; i < cnt ; i ++ ) {
12701520 /* Use the table to look up the offset in the rsa struct */
@@ -1273,6 +1523,8 @@ static int wp_rsa_import_key_data(wp_Rsa* rsa, const OSSL_PARAM params[],
12731523 for (j = 0 ; j < (int )ARRAY_SIZE (wp_rsa_param_key ); j ++ ) {
12741524 if (XSTRCMP (p -> key , wp_rsa_param_key [j ]) == 0 ) {
12751525 index = j ;
1526+ wp_rsa_import_increment_crt_counts (index , & primes , & exps ,
1527+ & coeffs );
12761528 break ;
12771529 }
12781530 }
@@ -1284,21 +1536,39 @@ static int wp_rsa_import_key_data(wp_Rsa* rsa, const OSSL_PARAM params[],
12841536 }
12851537
12861538 /* Read the value into the rsa struct */
1287- if (ok ) {
1539+ if (ok && p != n ) {
12881540 mp = (mp_int * )(((byte * )& rsa -> key ) + wp_rsa_offset [index ]);
1289- if (!wp_mp_read_unsigned_bin_le (mp , p -> data , p -> data_size )) {
1290- WOLFPROV_MSG (WP_LOG_COMP_RSA ,
1291- "Failed to read %s from parameters" , p -> key );
1541+ bits = wp_rsa_import_store_unsigned (mp , p );
1542+ ok = wp_rsa_import_check_bits (bits , nbits );
1543+
1544+ /* Reject a provided CRT parameter (dP, dQ, u) that is zero.
1545+ * The key validation could otherwise pass with a 0 param */
1546+ if (ok && (((index >= WP_RSA_PARAM_KEY_EXPONENT_INDEX1 &&
1547+ index <= WP_RSA_PARAM_KEY_EXPONENT_INDEX2 ) ||
1548+ index == WP_RSA_PARAM_KEY_COEFFICIENT_INDEX ) &&
1549+ mp_iszero (mp ))) {
12921550 ok = 0 ;
12931551 }
12941552 }
12951553 }
1554+
1555+ /* Verify the received RSA CRT parameters */
1556+ if (ok ) {
1557+ ok = wp_rsa_import_verify_crt (primes , exps , coeffs );
1558+ }
1559+ }
1560+ else if (ok && d == NULL ) {
1561+ mp = & rsa -> key .e ;
1562+ bits = wp_rsa_import_store_unsigned (mp , e );
1563+ ok = wp_rsa_import_check_bits (bits , nbits );
12961564 }
12971565
12981566 WOLFPROV_LEAVE (WP_LOG_COMP_RSA , __FILE__ ":" WOLFPROV_STRINGIZE (__LINE__ ), ok );
12991567 return ok ;
13001568}
13011569
1570+ #undef wp_rsa_import_check_bits
1571+
13021572/**
13031573 * Import the key into RSA key object from parameters.
13041574 *
0 commit comments