@@ -1152,3 +1152,117 @@ int test_wc_RsaDecrypt_BoundsCheck(void)
11521152 return EXPECT_RESULT ();
11531153} /* END test_wc_RsaDecryptBoundsCheck */
11541154
1155+ /*
1156+ * Test wc_RsaKeyToDer with an mp_int large enough to wrap size calculations.
1157+ */
1158+ int test_wc_RsaKeyToDer_SizeOverflow (void )
1159+ {
1160+ EXPECT_DECLS ;
1161+ #if !defined(NO_RSA ) && defined(USE_INTEGER_HEAP_MATH ) && \
1162+ !defined(USE_FAST_MATH ) && \
1163+ defined(WOLFSSL_ASN_TEMPLATE ) && defined(WOLFSSL_PUBLIC_MP ) && \
1164+ (defined(WOLFSSL_KEY_GEN ) || defined(WOLFSSL_KEY_TO_DER ))
1165+ RsaKey key ;
1166+ int i ;
1167+ int derRet ;
1168+ int crafted_used ;
1169+ int top_bits ;
1170+ mp_digit top_digit ;
1171+ mp_digit storage = 0 ; /* the only digit mp_count_bits ever reads */
1172+ mp_digit * fake_dp = NULL ;
1173+
1174+ int orig_used = 0 ;
1175+ int orig_alloc = 0 ;
1176+ int orig_sign = 0 ;
1177+ mp_digit * orig_dp = NULL ;
1178+
1179+ mp_int * fields [8 ];
1180+
1181+ XMEMSET (& key , 0 , sizeof (key ));
1182+
1183+ /* Skip on 32-bit: biasing dp by ~half the address space is unsafe. */
1184+ if (sizeof (void * ) < 8 ) {
1185+ return TEST_SKIPPED ;
1186+ }
1187+
1188+ /* Find 'used' count that makes (used-1)*DIGIT_BIT + top_bits = -48
1189+ * as signed int, causing mp_unsigned_bin_size to return -6. */
1190+ {
1191+ unsigned int target = 0xFFFFFFD0u ; /* -48 as unsigned 32-bit */
1192+ int found = 0 ;
1193+
1194+ crafted_used = 0 ;
1195+ top_bits = 0 ;
1196+ top_digit = 0 ;
1197+
1198+ for (top_bits = 1 ; top_bits < DIGIT_BIT ; top_bits ++ ) {
1199+ unsigned int base = target - (unsigned int )top_bits ;
1200+ if (base % (unsigned int )DIGIT_BIT == 0 ) {
1201+ crafted_used = (int )(base / (unsigned int )DIGIT_BIT ) + 1 ;
1202+ top_digit = (mp_digit )1 << (top_bits - 1 );
1203+ found = 1 ;
1204+ break ;
1205+ }
1206+ }
1207+ if (!found ) {
1208+ return TEST_SKIPPED ;
1209+ }
1210+ }
1211+
1212+ ExpectIntEQ (wc_InitRsaKey (& key , HEAP_HINT ), 0 );
1213+
1214+ /* Set up dummy RSA private key fields. */
1215+ key .type = RSA_PRIVATE ;
1216+ fields [0 ] = & key .n ;
1217+ fields [1 ] = & key .e ;
1218+ fields [2 ] = & key .d ;
1219+ fields [3 ] = & key .p ;
1220+ fields [4 ] = & key .q ;
1221+ fields [5 ] = & key .dP ;
1222+ fields [6 ] = & key .dQ ;
1223+ fields [7 ] = & key .u ;
1224+
1225+ for (i = 0 ; i < 8 ; i ++ ) {
1226+ if (EXPECT_SUCCESS ()) {
1227+ ExpectIntEQ (mp_init (fields [i ]), 0 );
1228+ mp_set (fields [i ], 0x42 );
1229+ }
1230+ }
1231+
1232+ if (EXPECT_SUCCESS ()) {
1233+ orig_used = key .p .used ;
1234+ orig_alloc = key .p .alloc ;
1235+ orig_sign = key .p .sign ;
1236+ orig_dp = key .p .dp ;
1237+ }
1238+
1239+ /* The vulnerable path (mp_unsigned_bin_size -> mp_count_bits, and
1240+ * mp_leading_bit) only reads dp[used-1]. Bias dp so that index
1241+ * (used-1) lands on our single real digit -- no giant allocation
1242+ * (and no mmap/VirtualAlloc) needed. */
1243+ if (EXPECT_SUCCESS ()) {
1244+ storage = top_digit ;
1245+ fake_dp = (mp_digit * )((wc_ptr_t )& storage
1246+ - (wc_ptr_t )(crafted_used - 1 ) * sizeof (mp_digit ));
1247+
1248+ key .p .dp = fake_dp ;
1249+ key .p .used = crafted_used ;
1250+ key .p .alloc = crafted_used ;
1251+ key .p .sign = 0 ; /* MP_ZPOS */
1252+ }
1253+
1254+ /* Should return an error, not a bogus small size. */
1255+ derRet = wc_RsaKeyToDer (& key , NULL , 0 );
1256+ ExpectIntLT (derRet , 0 );
1257+
1258+ /* Restore key.p before cleanup. */
1259+ key .p .dp = orig_dp ;
1260+ key .p .used = orig_used ;
1261+ key .p .alloc = orig_alloc ;
1262+ key .p .sign = orig_sign ;
1263+
1264+ DoExpectIntEQ (wc_FreeRsaKey (& key ), 0 );
1265+ #endif
1266+ return EXPECT_RESULT ();
1267+ } /* END test_wc_RsaKeyToDer_SizeOverflow */
1268+
0 commit comments