Skip to content

Commit 1bcbddc

Browse files
committed
Add upper limit to PBKDF iteration count
Add WC_PBKDF_MAX_ITERATIONS (default 100000) to cap the iteration count in wc_PBKDF1_ex(), wc_PBKDF2_ex(), and wc_PKCS12_PBKDF_ex().
1 parent b2f1c58 commit 1bcbddc

3 files changed

Lines changed: 148 additions & 0 deletions

File tree

wolfcrypt/src/pwdbased.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,11 @@ int wc_PBKDF1_ex(byte* key, int keyLen, byte* iv, int ivLen,
7979
if (iterations <= 0)
8080
iterations = 1;
8181

82+
if (iterations > WC_PBKDF_MAX_ITERATIONS) {
83+
WOLFSSL_MSG("PBKDF1 iteration count exceeds WC_PBKDF_MAX_ITERATIONS");
84+
return BAD_FUNC_ARG;
85+
}
86+
8287
hashT = wc_HashTypeConvert(hashType);
8388
err = wc_HashGetDigestSize(hashT);
8489
if (err < 0)
@@ -215,6 +220,11 @@ int wc_PBKDF2_ex(byte* output, const byte* passwd, int pLen, const byte* salt,
215220
if (iterations <= 0)
216221
iterations = 1;
217222

223+
if (iterations > WC_PBKDF_MAX_ITERATIONS) {
224+
WOLFSSL_MSG("PBKDF2 iteration count exceeds WC_PBKDF_MAX_ITERATIONS");
225+
return BAD_FUNC_ARG;
226+
}
227+
218228
hashT = wc_HashTypeConvert(hashType);
219229
hLen = wc_HashGetDigestSize(hashT);
220230
if (hLen < 0)
@@ -410,6 +420,11 @@ int wc_PKCS12_PBKDF_ex(byte* output, const byte* passwd, int passLen,
410420
if (iterations <= 0)
411421
iterations = 1;
412422

423+
if (iterations > WC_PBKDF_MAX_ITERATIONS) {
424+
WOLFSSL_MSG("PKCS12 PBKDF iteration count exceeds WC_PBKDF_MAX_ITERATIONS");
425+
return BAD_FUNC_ARG;
426+
}
427+
413428
hashT = wc_HashTypeConvert(hashType);
414429
ret = wc_HashGetDigestSize(hashT);
415430
if (ret < 0)

wolfcrypt/test/test.c

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30407,6 +30407,20 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t pkcs12_pbkdf_test(void)
3040730407
if (XMEMCMP(derived, verify2, 24) != 0)
3040830408
return WC_TEST_RET_ENC_NC;
3040930409

30410+
/* Test iteration cap: iterations above WC_PBKDF_MAX_ITERATIONS must be
30411+
* rejected to prevent CPU exhaustion DoS via crafted PKCS#12 files. */
30412+
ret = wc_PKCS12_PBKDF_ex(derived, passwd2, sizeof(passwd2), salt2, 8,
30413+
WC_PBKDF_MAX_ITERATIONS + 1, kLen, WC_SHA256,
30414+
id, HEAP_HINT);
30415+
if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG))
30416+
return WC_TEST_RET_ENC_NC;
30417+
30418+
ret = wc_PKCS12_PBKDF_ex(derived, passwd2, sizeof(passwd2), salt2, 8,
30419+
WC_PBKDF_MAX_ITERATIONS, kLen, WC_SHA256,
30420+
id, HEAP_HINT);
30421+
if (ret < 0)
30422+
return WC_TEST_RET_ENC_EC(ret);
30423+
3041030424
return 0;
3041130425
}
3041230426
#endif /* HAVE_PKCS12 && !NO_SHA256 */
@@ -30438,6 +30452,13 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t pbkdf2_test(void)
3043830452
if (XMEMCMP(derived, verify, sizeof(verify)) != 0)
3043930453
return WC_TEST_RET_ENC_NC;
3044030454

30455+
/* Test iteration cap */
30456+
ret = wc_PBKDF2_ex(derived, (byte*)passwd, (int)XSTRLEN(passwd),
30457+
salt, (int)sizeof(salt), WC_PBKDF_MAX_ITERATIONS + 1,
30458+
kLen, WC_SHA256, HEAP_HINT, devId);
30459+
if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG))
30460+
return WC_TEST_RET_ENC_NC;
30461+
3044130462
return 0;
3044230463

3044330464
}
@@ -30469,6 +30490,13 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t pbkdf1_test(void)
3046930490
if (XMEMCMP(derived, verify, sizeof(verify)) != 0)
3047030491
return WC_TEST_RET_ENC_NC;
3047130492

30493+
/* Test iteration cap */
30494+
ret = wc_PBKDF1_ex(derived, kLen, NULL, 0, (byte*)passwd,
30495+
(int)XSTRLEN(passwd), salt, (int)sizeof(salt),
30496+
WC_PBKDF_MAX_ITERATIONS + 1, WC_SHA, HEAP_HINT);
30497+
if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG))
30498+
return WC_TEST_RET_ENC_NC;
30499+
3047230500
return 0;
3047330501
}
3047430502
#endif /* HAVE_PBKDF2 && !NO_SHA */
@@ -30493,6 +30521,52 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t pwdbased_test(void)
3049330521
if (ret != 0)
3049430522
return ret;
3049530523
#endif
30524+
#ifdef HAVE_PKCS12
30525+
/* Test that a crafted PKCS#12 with INT_MAX MAC iterations is rejected
30526+
* immediately rather than hanging in DoPKCS12Hash(). */
30527+
{
30528+
static const byte evil_p12[] = {
30529+
0x30, 0x58, 0x02, 0x01, 0x03, 0x30, 0x1e, 0x06,
30530+
0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
30531+
0x07, 0x01, 0xa0, 0x11, 0x04, 0x0f, 0x30, 0x0d,
30532+
0x30, 0x0b, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
30533+
0xf7, 0x0d, 0x01, 0x07, 0x01, 0x30, 0x33, 0x30,
30534+
0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03,
30535+
0x02, 0x1a, 0x05, 0x00, 0x04, 0x14, 0x00, 0x00,
30536+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
30537+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
30538+
0x00, 0x00, 0x04, 0x08, 0x41, 0x41, 0x41, 0x41,
30539+
0x41, 0x41, 0x41, 0x41, 0x02, 0x04, 0x7f, 0xff,
30540+
0xff, 0xff
30541+
};
30542+
WC_PKCS12* evilPkcs12 = wc_PKCS12_new_ex(HEAP_HINT);
30543+
if (evilPkcs12 == NULL)
30544+
return WC_TEST_RET_ENC_EC(MEMORY_E);
30545+
30546+
ret = wc_d2i_PKCS12(evil_p12, (word32)sizeof(evil_p12), evilPkcs12);
30547+
if (ret == 0) {
30548+
byte* evilKey = NULL;
30549+
byte* evilCert = NULL;
30550+
word32 evilKeySz = 0, evilCertSz = 0;
30551+
WC_DerCertList* evilCa = NULL;
30552+
30553+
ret = wc_PKCS12_parse(evilPkcs12, "test", &evilKey, &evilKeySz,
30554+
&evilCert, &evilCertSz, &evilCa);
30555+
XFREE(evilKey, HEAP_HINT, DYNAMIC_TYPE_PKCS);
30556+
XFREE(evilCert, HEAP_HINT, DYNAMIC_TYPE_PKCS);
30557+
if (evilCa)
30558+
wc_FreeCertList(evilCa, HEAP_HINT);
30559+
wc_PKCS12_free(evilPkcs12);
30560+
/* Parse must fail (iteration cap), not succeed or hang */
30561+
if (ret == 0)
30562+
return WC_TEST_RET_ENC_NC;
30563+
}
30564+
else {
30565+
wc_PKCS12_free(evilPkcs12);
30566+
}
30567+
ret = 0;
30568+
}
30569+
#endif /* HAVE_PKCS12 */
3049630570
#ifdef HAVE_SCRYPT
3049730571
ret = scrypt_test();
3049830572
if (ret != 0)
@@ -30592,6 +30666,56 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t pkcs12_test(void)
3059230666
goto out;
3059330667
}
3059430668

30669+
/* Test that a crafted PKCS#12 with INT_MAX MAC iterations is rejected
30670+
* immediately rather than hanging in DoPKCS12Hash(). This is a 90-byte
30671+
* minimal PKCS#12 with mac->itt = 0x7FFFFFFF (2,147,483,647). */
30672+
{
30673+
static const byte evil_p12[] = {
30674+
0x30, 0x58, 0x02, 0x01, 0x03, 0x30, 0x1e, 0x06,
30675+
0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
30676+
0x07, 0x01, 0xa0, 0x11, 0x04, 0x0f, 0x30, 0x0d,
30677+
0x30, 0x0b, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
30678+
0xf7, 0x0d, 0x01, 0x07, 0x01, 0x30, 0x33, 0x30,
30679+
0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03,
30680+
0x02, 0x1a, 0x05, 0x00, 0x04, 0x14, 0x00, 0x00,
30681+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
30682+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
30683+
0x00, 0x00, 0x04, 0x08, 0x41, 0x41, 0x41, 0x41,
30684+
0x41, 0x41, 0x41, 0x41, 0x02, 0x04, 0x7f, 0xff,
30685+
0xff, 0xff
30686+
};
30687+
WC_PKCS12* evilPkcs12 = wc_PKCS12_new_ex(HEAP_HINT);
30688+
if (evilPkcs12 == NULL) {
30689+
ret = WC_TEST_RET_ENC_EC(MEMORY_E);
30690+
goto out;
30691+
}
30692+
ret = wc_d2i_PKCS12(evil_p12, (word32)sizeof(evil_p12), evilPkcs12);
30693+
if (ret != 0) {
30694+
wc_PKCS12_free(evilPkcs12);
30695+
ret = WC_TEST_RET_ENC_EC(ret);
30696+
goto out;
30697+
}
30698+
{
30699+
byte* evilKey = NULL;
30700+
byte* evilCert = NULL;
30701+
word32 evilKeySz = 0, evilCertSz = 0;
30702+
WC_DerCertList* evilCa = NULL;
30703+
ret = wc_PKCS12_parse(evilPkcs12, "test", &evilKey, &evilKeySz,
30704+
&evilCert, &evilCertSz, &evilCa);
30705+
XFREE(evilKey, HEAP_HINT, DYNAMIC_TYPE_PKCS);
30706+
XFREE(evilCert, HEAP_HINT, DYNAMIC_TYPE_PKCS);
30707+
if (evilCa)
30708+
wc_FreeCertList(evilCa, HEAP_HINT);
30709+
}
30710+
wc_PKCS12_free(evilPkcs12);
30711+
/* Must have been rejected (not hung) */
30712+
if (ret == 0) {
30713+
ret = WC_TEST_RET_ENC_NC;
30714+
goto out;
30715+
}
30716+
ret = 0; /* rejection is the expected outcome */
30717+
}
30718+
3059530719
out:
3059630720

3059730721
if (derCaListOut)

wolfssl/wolfcrypt/pwdbased.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,15 @@
3535
extern "C" {
3636
#endif
3737

38+
/* Maximum allowed PBKDF iteration count to prevent CPU exhaustion DoS.
39+
* Attacker-controlled PKCS#12 files can specify iterations up to INT_MAX
40+
* (2,147,483,647) in the MAC data, causing hours of CPU time.
41+
* Override by defining WC_PBKDF_MAX_ITERATIONS before including this header.
42+
* Normal PKCS#12 files use 1,000–10,000 iterations. */
43+
#ifndef WC_PBKDF_MAX_ITERATIONS
44+
#define WC_PBKDF_MAX_ITERATIONS 1000000
45+
#endif
46+
3847
#if FIPS_VERSION3_GE(6,0,0)
3948
extern const unsigned int wolfCrypt_FIPS_pbkdf_ro_sanity[2];
4049
WOLFSSL_LOCAL int wolfCrypt_FIPS_PBKDF_sanity(void);

0 commit comments

Comments
 (0)