Skip to content

Commit 599d02e

Browse files
committed
Add bounds checks for MP integer size in SizeASN_Items
1 parent abfff1e commit 599d02e

3 files changed

Lines changed: 140 additions & 1 deletion

File tree

tests/api/test_rsa.c

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1152,3 +1152,130 @@ 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(WOLFSSL_ASN_TEMPLATE) && \
1163+
(defined(WOLFSSL_KEY_GEN) || defined(WOLFSSL_KEY_TO_DER)) && \
1164+
!defined(_WIN32)
1165+
#include <sys/mman.h>
1166+
RsaKey key;
1167+
int i;
1168+
int derRet;
1169+
int crafted_used;
1170+
int top_bits;
1171+
mp_digit top_digit;
1172+
mp_digit* crafted_dp = MAP_FAILED;
1173+
size_t dp_bytes = 0;
1174+
1175+
int orig_used = 0;
1176+
int orig_alloc = 0;
1177+
int orig_sign = 0;
1178+
mp_digit* orig_dp = NULL;
1179+
1180+
mp_int* fields[8];
1181+
1182+
XMEMSET(&key, 0, sizeof(key));
1183+
1184+
/* Find 'used' count that makes (used-1)*DIGIT_BIT + top_bits = -48
1185+
* as signed int, causing mp_unsigned_bin_size to return -6. */
1186+
{
1187+
unsigned int target = 0xFFFFFFD0u; /* -48 as unsigned 32-bit */
1188+
int found = 0;
1189+
1190+
crafted_used = 0;
1191+
top_bits = 0;
1192+
top_digit = 0;
1193+
1194+
for (top_bits = 1; top_bits < DIGIT_BIT; top_bits++) {
1195+
unsigned int base = target - (unsigned int)top_bits;
1196+
if (base % (unsigned int)DIGIT_BIT == 0) {
1197+
crafted_used = (int)(base / (unsigned int)DIGIT_BIT) + 1;
1198+
top_digit = (mp_digit)1 << (top_bits - 1);
1199+
found = 1;
1200+
break;
1201+
}
1202+
}
1203+
if (!found) {
1204+
return TEST_SKIPPED;
1205+
}
1206+
}
1207+
1208+
dp_bytes = (size_t)crafted_used * sizeof(mp_digit);
1209+
1210+
ExpectIntEQ(wc_InitRsaKey(&key, HEAP_HINT), 0);
1211+
1212+
/* Set up dummy RSA private key fields. */
1213+
key.type = RSA_PRIVATE;
1214+
fields[0] = &key.n;
1215+
fields[1] = &key.e;
1216+
fields[2] = &key.d;
1217+
fields[3] = &key.p;
1218+
fields[4] = &key.q;
1219+
fields[5] = &key.dP;
1220+
fields[6] = &key.dQ;
1221+
fields[7] = &key.u;
1222+
1223+
for (i = 0; i < 8; i++) {
1224+
if (EXPECT_SUCCESS()) {
1225+
ExpectIntEQ(mp_init(fields[i]), 0);
1226+
mp_set(fields[i], 0x42);
1227+
}
1228+
}
1229+
1230+
if (EXPECT_SUCCESS()) {
1231+
orig_used = key.p.used;
1232+
orig_alloc = key.p.alloc;
1233+
orig_sign = key.p.sign;
1234+
orig_dp = key.p.dp;
1235+
}
1236+
1237+
/* Sparse mmap — only the page at dp[used-1] is faulted in. */
1238+
if (EXPECT_SUCCESS()) {
1239+
crafted_dp = (mp_digit*)mmap(NULL, dp_bytes,
1240+
PROT_READ | PROT_WRITE,
1241+
MAP_PRIVATE | MAP_ANONYMOUS
1242+
#ifdef MAP_NORESERVE
1243+
| MAP_NORESERVE
1244+
#endif
1245+
, -1, 0);
1246+
}
1247+
if (crafted_dp == MAP_FAILED) {
1248+
key.p.dp = orig_dp;
1249+
key.p.used = orig_used;
1250+
key.p.alloc = orig_alloc;
1251+
key.p.sign = orig_sign;
1252+
DoExpectIntEQ(wc_FreeRsaKey(&key), 0);
1253+
return TEST_SKIPPED;
1254+
}
1255+
1256+
crafted_dp[crafted_used - 1] = top_digit;
1257+
1258+
key.p.dp = crafted_dp;
1259+
key.p.used = crafted_used;
1260+
key.p.alloc = crafted_used;
1261+
key.p.sign = 0; /* MP_ZPOS */
1262+
1263+
/* Should return an error, not a bogus small size. */
1264+
derRet = wc_RsaKeyToDer(&key, NULL, 0);
1265+
ExpectIntLT(derRet, 0);
1266+
1267+
/* Restore key.p before cleanup. */
1268+
key.p.dp = orig_dp;
1269+
key.p.used = orig_used;
1270+
key.p.alloc = orig_alloc;
1271+
key.p.sign = orig_sign;
1272+
1273+
if (crafted_dp != MAP_FAILED) {
1274+
munmap(crafted_dp, dp_bytes);
1275+
}
1276+
1277+
DoExpectIntEQ(wc_FreeRsaKey(&key), 0);
1278+
#endif
1279+
return EXPECT_RESULT();
1280+
} /* END test_wc_RsaKeyToDer_SizeOverflow */
1281+

tests/api/test_rsa.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ int test_wc_RsaEncryptSize(void);
4242
int test_wc_RsaSSL_SignVerify(void);
4343
int test_wc_RsaFlattenPublicKey(void);
4444
int test_wc_RsaDecrypt_BoundsCheck(void);
45+
int test_wc_RsaKeyToDer_SizeOverflow(void);
4546

4647
#define TEST_RSA_DECLS \
4748
TEST_DECL_GROUP("rsa", test_wc_InitRsaKey), \
@@ -61,6 +62,7 @@ int test_wc_RsaDecrypt_BoundsCheck(void);
6162
TEST_DECL_GROUP("rsa", test_wc_RsaEncryptSize), \
6263
TEST_DECL_GROUP("rsa", test_wc_RsaSSL_SignVerify), \
6364
TEST_DECL_GROUP("rsa", test_wc_RsaFlattenPublicKey), \
64-
TEST_DECL_GROUP("rsa", test_wc_RsaDecrypt_BoundsCheck)
65+
TEST_DECL_GROUP("rsa", test_wc_RsaDecrypt_BoundsCheck), \
66+
TEST_DECL_GROUP("rsa", test_wc_RsaKeyToDer_SizeOverflow)
6567

6668
#endif /* WOLFCRYPT_TEST_RSA_H */

wolfcrypt/src/asn.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -864,8 +864,18 @@ int SizeASN_Items(const ASNItem* asn, ASNSetData *data, int count,
864864
case ASN_DATA_TYPE_MP:
865865
/* Calculate the size of the MP integer data. */
866866
length = mp_unsigned_bin_size(data[i].data.mp);
867+
if (length < 0) {
868+
return ASN_PARSE_E;
869+
}
867870
length += mp_leading_bit(data[i].data.mp) ? 1 : 0;
871+
if (length < 0) {
872+
return ASN_PARSE_E;
873+
}
868874
len = (word32)SizeASNHeader((word32)length) + (word32)length;
875+
/* Check for overflow: header + length must not wrap word32. */
876+
if (len < (word32)length) {
877+
return ASN_PARSE_E;
878+
}
869879
break;
870880

871881
case ASN_DATA_TYPE_REPLACE_BUFFER:

0 commit comments

Comments
 (0)