@@ -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 *
@@ -1218,22 +1246,193 @@ static int wp_rsa_validate(const wp_Rsa* rsa, int selection, int checkType)
12181246 else
12191247#endif
12201248 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 )) {
1249+ if (mp_iszero ((mp_int * )& rsa -> key .d ) ||
1250+ (mp_cmp ((mp_int * )& rsa -> key .d , (mp_int * )& rsa -> key .n ) != MP_LT )) {
12231251 ok = 0 ;
12241252 }
12251253 }
12261254 else if (checkPub ) {
1227- if (mp_iseven (& rsa -> key .e ) || mp_iszero ((mp_int * )& rsa -> key .e ) ||
1228- mp_isone (& rsa -> key .e )) {
1255+ int nbits = mp_count_bits ((mp_int * )& rsa -> key .n );
1256+ mp_int res ;
1257+
1258+ if (mp_iszero ((mp_int * )& rsa -> key .e ) || mp_iszero ((mp_int * )& rsa -> key .n ) ||
1259+ mp_isone ((mp_int * )& rsa -> key .e ) || mp_isone ((mp_int * )& rsa -> key .n ) ||
1260+ mp_iseven ((mp_int * )& rsa -> key .e ) || mp_iseven ((mp_int * )& rsa -> key .n )) {
1261+ ok = 0 ;
1262+ }
1263+
1264+ if (ok && nbits > OPENSSL_RSA_MAX_MODULUS_BITS ) {
1265+ ok = 0 ;
1266+ }
1267+
1268+ #ifdef HAVE_FIPS
1269+ if (ok && (wolfProvider_GetFipsChecks () & WP_FIPS_CHECK_RSA_KEY_SIZE )) {
1270+ /* n must have at least 112 bits of security, and
1271+ * 2^16 < e < 2^256 */
1272+ int ebits = mp_count_bits ((mp_int * )& rsa -> key .e );
1273+ if (nbits < 2048 || ebits <= 16 || ebits >= 257 ) {
1274+ ok = 0 ;
1275+ }
1276+ }
1277+ #endif
1278+
1279+ /* Reject a modulus that shares a small prime factor */
1280+ if (ok && mp_init (& res ) != MP_OKAY ) {
1281+ ok = 0 ;
1282+ }
1283+ else if (ok ) {
1284+ unsigned int prime ;
1285+ for (prime = 0 ; prime < VALIDATE_PRIMES_SIZE ; prime ++ ) {
1286+ if (mp_set_int (& res , validate_primes [prime ]) != MP_OKAY ||
1287+ mp_mod ((mp_int * )& rsa -> key .n , & res , & res ) != MP_OKAY ||
1288+ mp_iszero (& res )) {
1289+ ok = 0 ;
1290+ break ;
1291+ }
1292+ }
1293+
1294+ mp_clear (& res );
1295+ }
1296+
1297+ #if (!defined(WOLFSSL_SP_MATH ) && !defined(WOLFSSL_SP_MATH_ALL )) || \
1298+ defined(WOLFSSL_SP_PRIME_GEN )
1299+ /* Reject prime n since it is not composite.
1300+ * Note this does not catch a prime power (p^k), which OSSL
1301+ * also rejects. */
1302+ if (ok ) {
1303+ int isPrime = MP_NO ;
1304+ if (mp_prime_is_prime ((mp_int * )& rsa -> key .n , 5 , & isPrime ) != MP_OKAY ||
1305+ isPrime == MP_YES ) {
1306+ ok = 0 ;
1307+ }
1308+ }
1309+ #endif
1310+ }
1311+
1312+ WOLFPROV_LEAVE (WP_LOG_COMP_RSA , __FILE__ ":" WOLFPROV_STRINGIZE (__LINE__ ), ok );
1313+ return ok ;
1314+ }
1315+
1316+ /**
1317+ * Copy an unsigned value from an OSSL param into a provided RSA key parameter.
1318+ * Negative values result in an error.
1319+ *
1320+ * @param [out] mp RSA key parameter.
1321+ * @param [in] param Parameter to copy value from.
1322+ * @return Size of mp in bits on success.
1323+ * @return -1 on failure.
1324+ */
1325+ static int wp_rsa_import_store_unsigned (mp_int * mp , const OSSL_PARAM * param )
1326+ {
1327+ int ok ;
1328+ int bits = -1 ;
1329+ const unsigned char * data ;
1330+
1331+ WOLFPROV_ENTER (WP_LOG_COMP_RSA , "wp_rsa_import_store_unsigned" );
1332+
1333+ #if OPENSSL_VERSION_NUMBER <= 0x30100080L
1334+ ok = param -> data != NULL && param -> data_type == OSSL_PARAM_UNSIGNED_INTEGER ;
1335+ #else
1336+ ok = param -> data != NULL && (param -> data_type == OSSL_PARAM_INTEGER ||
1337+ param -> data_type == OSSL_PARAM_UNSIGNED_INTEGER );
1338+ #endif /* OPENSSL_VERSION_NUMBER <= 3.1.8 */
1339+
1340+ if (ok && !wp_mp_read_unsigned_bin_le (mp , param -> data , param -> data_size )) {
1341+ WOLFPROV_MSG (WP_LOG_COMP_RSA ,
1342+ "Failed to read %s from parameters" , param -> key );
1343+ ok = 0 ;
1344+ }
1345+
1346+ if (ok ) {
1347+ bits = mp_count_bits (mp );
1348+ }
1349+
1350+ /* OSSL accepts negative values for these params, but this doesn't work well
1351+ * in wolfProvider so reject it. */
1352+ if (ok && param -> data_type == OSSL_PARAM_INTEGER ) {
1353+ data = (const unsigned char * )param -> data ;
1354+ /* Assume little-endian when determining sign */
1355+ if (param -> data_size > 0 && (data [param -> data_size - 1 ] & 0x80 ) != 0 ) {
1356+ WOLFPROV_MSG (WP_LOG_COMP_RSA , "Negative value for %s rejected" ,
1357+ param -> key );
12291358 ok = 0 ;
12301359 }
12311360 }
12321361
12331362 WOLFPROV_LEAVE (WP_LOG_COMP_RSA , __FILE__ ":" WOLFPROV_STRINGIZE (__LINE__ ), ok );
1363+ return ok ? bits : -1 ;
1364+ }
1365+
1366+ /**
1367+ * Based on the index into wp_rsa_param_key, increment the appropriate counter.
1368+ *
1369+ * @param [in] index Index associated with wp_rsa_param_key.
1370+ * @param [out] primes Count of prime parameters.
1371+ * @param [out] exps Count of exponent parameters.
1372+ * @param [out] coeffs Count of coefficient parameters.
1373+ */
1374+ static void wp_rsa_import_increment_crt_counts (int index , int * primes ,
1375+ int * exps , int * coeffs )
1376+ {
1377+ if (index >= WP_RSA_PARAM_KEY_FACTOR_INDEX1 &&
1378+ index <= WP_RSA_PARAM_KEY_FACTOR_INDEX2 ) {
1379+ * primes += 1 ;
1380+ }
1381+ else if (index >= WP_RSA_PARAM_KEY_EXPONENT_INDEX1 &&
1382+ index <= WP_RSA_PARAM_KEY_EXPONENT_INDEX2 ) {
1383+ * exps += 1 ;
1384+ }
1385+ else if (index == WP_RSA_PARAM_KEY_COEFFICIENT_INDEX ) {
1386+ * coeffs += 1 ;
1387+ }
1388+ }
1389+
1390+ /**
1391+ * Verify the RSA CRT parameters supplied for a private key import are complete.
1392+ *
1393+ * @param [in] primes Count of prime factor parameters provided.
1394+ * @param [in] exps Count of CRT exponent parameters provided.
1395+ * @param [in] coeffs Count of CRT coefficient parameters provided.
1396+ * @return 1 when the CRT parameters are acceptable
1397+ * @return 0 when the provided factors/CRT parameters are incomplete or
1398+ * unsupported.
1399+ */
1400+ static int wp_rsa_import_verify_crt (int primes , int exps , int coeffs )
1401+ {
1402+ int ok = 1 ;
1403+
1404+ (void )exps ;
1405+ (void )coeffs ;
1406+
1407+ if (primes > 0 ) {
1408+ #if (OPENSSL_VERSION_NUMBER < 0x30100040L && OPENSSL_VERSION_NUMBER > 0x30000120L ) \
1409+ || (OPENSSL_VERSION_NUMBER < 0x300000C0L )
1410+ if (primes < 2 || primes != exps || primes != coeffs + 1 ) {
1411+ #else
1412+ if (primes < 2 ) {
1413+ #endif /* (Ver < 3.1.4 && Ver > 3.0.18) || (Ver < 3.0.12) */
1414+ WOLFPROV_MSG (WP_LOG_COMP_RSA ,
1415+ "RSA factors provided but CRT parameters incomplete" );
1416+ ok = 0 ;
1417+ }
1418+ /* TODO: multi-prime checks */
1419+ else if (primes > 2 ) {
1420+ WOLFPROV_MSG (WP_LOG_COMP_RSA , "RSA multi-prime import failed" );
1421+ ok = 0 ;
1422+ }
1423+ }
1424+
12341425 return ok ;
12351426}
12361427
1428+ #if OPENSSL_VERSION_NUMBER >= 0x30400000L
1429+ #define wp_rsa_import_check_bits (bits , nbits ) \
1430+ ((bits) != -1 && (bits) <= (nbits))
1431+ #else
1432+ #define wp_rsa_import_check_bits (bits , nbits ) \
1433+ ((bits) != -1)
1434+ #endif /* OPENSSL_VERSION_NUMBER >= 3.4.0 */
1435+
12371436/**
12381437 * Import the key data into RSA key object from parameters.
12391438 *
@@ -1251,20 +1450,39 @@ static int wp_rsa_import_key_data(wp_Rsa* rsa, const OSSL_PARAM params[],
12511450 int cnt = 0 ;
12521451 mp_int * mp = NULL ;
12531452 const OSSL_PARAM * p = NULL ;
1453+ const OSSL_PARAM * n ;
1454+ const OSSL_PARAM * e ;
1455+ const OSSL_PARAM * d = NULL ;
1456+ int bits ;
1457+ int nbits ;
1458+ int primes = 0 ;
1459+ int exps = 0 ;
1460+ int coeffs = 0 ;
12541461
12551462 WOLFPROV_ENTER (WP_LOG_COMP_RSA , "wp_rsa_import_key_data" );
12561463
12571464 /* N and E params are the only ones required by OSSL, so match that.
12581465 * 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 ) {
1466+ if ((n = OSSL_PARAM_locate_const (params , OSSL_PKEY_PARAM_RSA_N )) == NULL ||
1467+ n -> data == NULL ||
1468+ (e = OSSL_PARAM_locate_const (params , OSSL_PKEY_PARAM_RSA_E )) == NULL ||
1469+ e -> data == NULL ) {
12611470 WOLFPROV_MSG (WP_LOG_COMP_RSA , "Param N or E is missing" );
12621471 ok = 0 ;
12631472 }
1473+ if (ok && priv ) {
1474+ d = OSSL_PARAM_locate_const (params , OSSL_PKEY_PARAM_RSA_D );
1475+ }
12641476
12651477 if (ok ) {
1266- cnt = wp_params_count (params );
12671478 rsa -> key .type = priv ? RSA_PRIVATE : RSA_PUBLIC ;
1479+ mp = & rsa -> key .n ;
1480+ nbits = wp_rsa_import_store_unsigned (mp , n );
1481+ ok = nbits != -1 ;
1482+ }
1483+
1484+ if (ok && d != NULL ) {
1485+ cnt = wp_params_count (params );
12681486
12691487 for (i = 0 ; i < cnt ; i ++ ) {
12701488 /* Use the table to look up the offset in the rsa struct */
@@ -1273,6 +1491,8 @@ static int wp_rsa_import_key_data(wp_Rsa* rsa, const OSSL_PARAM params[],
12731491 for (j = 0 ; j < (int )ARRAY_SIZE (wp_rsa_param_key ); j ++ ) {
12741492 if (XSTRCMP (p -> key , wp_rsa_param_key [j ]) == 0 ) {
12751493 index = j ;
1494+ wp_rsa_import_increment_crt_counts (index , & primes , & exps ,
1495+ & coeffs );
12761496 break ;
12771497 }
12781498 }
@@ -1284,21 +1504,30 @@ static int wp_rsa_import_key_data(wp_Rsa* rsa, const OSSL_PARAM params[],
12841504 }
12851505
12861506 /* Read the value into the rsa struct */
1287- if (ok ) {
1507+ if (ok && p != n ) {
12881508 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 );
1292- ok = 0 ;
1293- }
1509+ bits = wp_rsa_import_store_unsigned (mp , p );
1510+ ok = wp_rsa_import_check_bits (bits , nbits );
12941511 }
12951512 }
1513+
1514+ /* Verify the received RSA CRT parameters */
1515+ if (ok ) {
1516+ ok = wp_rsa_import_verify_crt (primes , exps , coeffs );
1517+ }
1518+ }
1519+ else if (ok && d == NULL ) {
1520+ mp = & rsa -> key .e ;
1521+ bits = wp_rsa_import_store_unsigned (mp , e );
1522+ ok = wp_rsa_import_check_bits (bits , nbits );
12961523 }
12971524
12981525 WOLFPROV_LEAVE (WP_LOG_COMP_RSA , __FILE__ ":" WOLFPROV_STRINGIZE (__LINE__ ), ok );
12991526 return ok ;
13001527}
13011528
1529+ #undef wp_rsa_import_check_bits
1530+
13021531/**
13031532 * Import the key into RSA key object from parameters.
13041533 *
0 commit comments