From 3f6c8316c7d387743082144f3e5da25aef92fc75 Mon Sep 17 00:00:00 2001 From: Anthony Hu Date: Mon, 23 Mar 2026 15:49:24 -0400 Subject: [PATCH 01/14] 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(). --- wolfcrypt/src/pwdbased.c | 15 +++++ wolfcrypt/test/test.c | 124 +++++++++++++++++++++++++++++++++++ wolfssl/wolfcrypt/pwdbased.h | 9 +++ 3 files changed, 148 insertions(+) diff --git a/wolfcrypt/src/pwdbased.c b/wolfcrypt/src/pwdbased.c index 996011d51df..c8b1ad68278 100644 --- a/wolfcrypt/src/pwdbased.c +++ b/wolfcrypt/src/pwdbased.c @@ -82,6 +82,11 @@ int wc_PBKDF1_ex(byte* key, int keyLen, byte* iv, int ivLen, if (iterations <= 0) iterations = 1; + if (iterations > WC_PBKDF_MAX_ITERATIONS) { + WOLFSSL_MSG("PBKDF1 iteration count exceeds WC_PBKDF_MAX_ITERATIONS"); + return BAD_FUNC_ARG; + } + hashT = wc_HashTypeConvert(hashType); err = wc_HashGetDigestSize(hashT); if (err < 0) @@ -218,6 +223,11 @@ int wc_PBKDF2_ex(byte* output, const byte* passwd, int pLen, const byte* salt, if (iterations <= 0) iterations = 1; + if (iterations > WC_PBKDF_MAX_ITERATIONS) { + WOLFSSL_MSG("PBKDF2 iteration count exceeds WC_PBKDF_MAX_ITERATIONS"); + return BAD_FUNC_ARG; + } + hashT = wc_HashTypeConvert(hashType); hLen = wc_HashGetDigestSize(hashT); if (hLen < 0) @@ -406,6 +416,11 @@ int wc_PKCS12_PBKDF_ex(byte* output, const byte* passwd, int passLen, if (iterations <= 0) iterations = 1; + if (iterations > WC_PBKDF_MAX_ITERATIONS) { + WOLFSSL_MSG("PKCS12 PBKDF iteration count exceeds WC_PBKDF_MAX_ITERATIONS"); + return BAD_FUNC_ARG; + } + hashT = wc_HashTypeConvert(hashType); ret = wc_HashGetDigestSize(hashT); if (ret < 0) diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 280776ca654..5229ffcb82b 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -31960,6 +31960,20 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t pkcs12_pbkdf_test(void) if (XMEMCMP(derived, verify2, 24) != 0) return WC_TEST_RET_ENC_NC; + /* Test iteration cap: iterations above WC_PBKDF_MAX_ITERATIONS must be + * rejected to prevent CPU exhaustion DoS via crafted PKCS#12 files. */ + ret = wc_PKCS12_PBKDF_ex(derived, passwd2, sizeof(passwd2), salt2, 8, + WC_PBKDF_MAX_ITERATIONS + 1, kLen, WC_SHA256, + id, HEAP_HINT); + if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + return WC_TEST_RET_ENC_NC; + + ret = wc_PKCS12_PBKDF_ex(derived, passwd2, sizeof(passwd2), salt2, 8, + WC_PBKDF_MAX_ITERATIONS, kLen, WC_SHA256, + id, HEAP_HINT); + if (ret < 0) + return WC_TEST_RET_ENC_EC(ret); + return 0; } #endif /* HAVE_PKCS12 && !NO_SHA256 */ @@ -31991,6 +32005,13 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t pbkdf2_test(void) if (XMEMCMP(derived, verify, sizeof(verify)) != 0) return WC_TEST_RET_ENC_NC; + /* Test iteration cap */ + ret = wc_PBKDF2_ex(derived, (byte*)passwd, (int)XSTRLEN(passwd), + salt, (int)sizeof(salt), WC_PBKDF_MAX_ITERATIONS + 1, + kLen, WC_SHA256, HEAP_HINT, devId); + if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + return WC_TEST_RET_ENC_NC; + return 0; } @@ -32022,6 +32043,13 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t pbkdf1_test(void) if (XMEMCMP(derived, verify, sizeof(verify)) != 0) return WC_TEST_RET_ENC_NC; + /* Test iteration cap */ + ret = wc_PBKDF1_ex(derived, kLen, NULL, 0, (byte*)passwd, + (int)XSTRLEN(passwd), salt, (int)sizeof(salt), + WC_PBKDF_MAX_ITERATIONS + 1, WC_SHA, HEAP_HINT); + if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + return WC_TEST_RET_ENC_NC; + return 0; } #endif /* HAVE_PBKDF2 && !NO_SHA */ @@ -32046,6 +32074,52 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t pwdbased_test(void) if (ret != 0) return ret; #endif +#ifdef HAVE_PKCS12 + /* Test that a crafted PKCS#12 with INT_MAX MAC iterations is rejected + * immediately rather than hanging in DoPKCS12Hash(). */ + { + static const byte evil_p12[] = { + 0x30, 0x58, 0x02, 0x01, 0x03, 0x30, 0x1e, 0x06, + 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, + 0x07, 0x01, 0xa0, 0x11, 0x04, 0x0f, 0x30, 0x0d, + 0x30, 0x0b, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x07, 0x01, 0x30, 0x33, 0x30, + 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, + 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x08, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x02, 0x04, 0x7f, 0xff, + 0xff, 0xff + }; + WC_PKCS12* evilPkcs12 = wc_PKCS12_new_ex(HEAP_HINT); + if (evilPkcs12 == NULL) + return WC_TEST_RET_ENC_EC(MEMORY_E); + + ret = wc_d2i_PKCS12(evil_p12, (word32)sizeof(evil_p12), evilPkcs12); + if (ret == 0) { + byte* evilKey = NULL; + byte* evilCert = NULL; + word32 evilKeySz = 0, evilCertSz = 0; + WC_DerCertList* evilCa = NULL; + + ret = wc_PKCS12_parse(evilPkcs12, "test", &evilKey, &evilKeySz, + &evilCert, &evilCertSz, &evilCa); + XFREE(evilKey, HEAP_HINT, DYNAMIC_TYPE_PKCS); + XFREE(evilCert, HEAP_HINT, DYNAMIC_TYPE_PKCS); + if (evilCa) + wc_FreeCertList(evilCa, HEAP_HINT); + wc_PKCS12_free(evilPkcs12); + /* Parse must fail (iteration cap), not succeed or hang */ + if (ret == 0) + return WC_TEST_RET_ENC_NC; + } + else { + wc_PKCS12_free(evilPkcs12); + } + ret = 0; + } +#endif /* HAVE_PKCS12 */ #ifdef HAVE_SCRYPT ret = scrypt_test(); if (ret != 0) @@ -32145,6 +32219,56 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t pkcs12_test(void) goto out; } + /* Test that a crafted PKCS#12 with INT_MAX MAC iterations is rejected + * immediately rather than hanging in DoPKCS12Hash(). This is a 90-byte + * minimal PKCS#12 with mac->itt = 0x7FFFFFFF (2,147,483,647). */ + { + static const byte evil_p12[] = { + 0x30, 0x58, 0x02, 0x01, 0x03, 0x30, 0x1e, 0x06, + 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, + 0x07, 0x01, 0xa0, 0x11, 0x04, 0x0f, 0x30, 0x0d, + 0x30, 0x0b, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x07, 0x01, 0x30, 0x33, 0x30, + 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, + 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x08, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x02, 0x04, 0x7f, 0xff, + 0xff, 0xff + }; + WC_PKCS12* evilPkcs12 = wc_PKCS12_new_ex(HEAP_HINT); + if (evilPkcs12 == NULL) { + ret = WC_TEST_RET_ENC_EC(MEMORY_E); + goto out; + } + ret = wc_d2i_PKCS12(evil_p12, (word32)sizeof(evil_p12), evilPkcs12); + if (ret != 0) { + wc_PKCS12_free(evilPkcs12); + ret = WC_TEST_RET_ENC_EC(ret); + goto out; + } + { + byte* evilKey = NULL; + byte* evilCert = NULL; + word32 evilKeySz = 0, evilCertSz = 0; + WC_DerCertList* evilCa = NULL; + ret = wc_PKCS12_parse(evilPkcs12, "test", &evilKey, &evilKeySz, + &evilCert, &evilCertSz, &evilCa); + XFREE(evilKey, HEAP_HINT, DYNAMIC_TYPE_PKCS); + XFREE(evilCert, HEAP_HINT, DYNAMIC_TYPE_PKCS); + if (evilCa) + wc_FreeCertList(evilCa, HEAP_HINT); + } + wc_PKCS12_free(evilPkcs12); + /* Must have been rejected (not hung) */ + if (ret == 0) { + ret = WC_TEST_RET_ENC_NC; + goto out; + } + ret = 0; /* rejection is the expected outcome */ + } + out: if (derCaListOut) diff --git a/wolfssl/wolfcrypt/pwdbased.h b/wolfssl/wolfcrypt/pwdbased.h index fc2eddfd8dd..fb56df627f4 100644 --- a/wolfssl/wolfcrypt/pwdbased.h +++ b/wolfssl/wolfcrypt/pwdbased.h @@ -35,6 +35,15 @@ extern "C" { #endif +/* Maximum allowed PBKDF iteration count to prevent CPU exhaustion DoS. + * Attacker-controlled PKCS#12 files can specify iterations up to INT_MAX + * (2,147,483,647) in the MAC data, causing hours of CPU time. + * Override by defining WC_PBKDF_MAX_ITERATIONS before including this header. + * Normal PKCS#12 files use 1,000–10,000 iterations. */ +#ifndef WC_PBKDF_MAX_ITERATIONS + #define WC_PBKDF_MAX_ITERATIONS 100000 +#endif + #if FIPS_VERSION3_GE(6,0,0) extern const unsigned int wolfCrypt_FIPS_pbkdf_ro_sanity[2]; WOLFSSL_LOCAL int wolfCrypt_FIPS_PBKDF_sanity(void); From 685a6fee6db1529d908b9b71b57a704a90a7ba99 Mon Sep 17 00:00:00 2001 From: Anthony Hu Date: Thu, 26 Mar 2026 23:34:14 -0400 Subject: [PATCH 02/14] simplify the tests. --- wolfcrypt/test/test.c | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 5229ffcb82b..aec1bac63f1 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -31960,20 +31960,6 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t pkcs12_pbkdf_test(void) if (XMEMCMP(derived, verify2, 24) != 0) return WC_TEST_RET_ENC_NC; - /* Test iteration cap: iterations above WC_PBKDF_MAX_ITERATIONS must be - * rejected to prevent CPU exhaustion DoS via crafted PKCS#12 files. */ - ret = wc_PKCS12_PBKDF_ex(derived, passwd2, sizeof(passwd2), salt2, 8, - WC_PBKDF_MAX_ITERATIONS + 1, kLen, WC_SHA256, - id, HEAP_HINT); - if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) - return WC_TEST_RET_ENC_NC; - - ret = wc_PKCS12_PBKDF_ex(derived, passwd2, sizeof(passwd2), salt2, 8, - WC_PBKDF_MAX_ITERATIONS, kLen, WC_SHA256, - id, HEAP_HINT); - if (ret < 0) - return WC_TEST_RET_ENC_EC(ret); - return 0; } #endif /* HAVE_PKCS12 && !NO_SHA256 */ @@ -32005,13 +31991,6 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t pbkdf2_test(void) if (XMEMCMP(derived, verify, sizeof(verify)) != 0) return WC_TEST_RET_ENC_NC; - /* Test iteration cap */ - ret = wc_PBKDF2_ex(derived, (byte*)passwd, (int)XSTRLEN(passwd), - salt, (int)sizeof(salt), WC_PBKDF_MAX_ITERATIONS + 1, - kLen, WC_SHA256, HEAP_HINT, devId); - if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) - return WC_TEST_RET_ENC_NC; - return 0; } @@ -32043,13 +32022,6 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t pbkdf1_test(void) if (XMEMCMP(derived, verify, sizeof(verify)) != 0) return WC_TEST_RET_ENC_NC; - /* Test iteration cap */ - ret = wc_PBKDF1_ex(derived, kLen, NULL, 0, (byte*)passwd, - (int)XSTRLEN(passwd), salt, (int)sizeof(salt), - WC_PBKDF_MAX_ITERATIONS + 1, WC_SHA, HEAP_HINT); - if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) - return WC_TEST_RET_ENC_NC; - return 0; } #endif /* HAVE_PBKDF2 && !NO_SHA */ From 421826ed18bdffa53960047ebb35a6b3cdd15751 Mon Sep 17 00:00:00 2001 From: Anthony Hu Date: Fri, 27 Mar 2026 00:32:40 -0400 Subject: [PATCH 03/14] better macro gating in tests --- wolfcrypt/test/test.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index aec1bac63f1..bd78b16358c 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -32046,7 +32046,8 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t pwdbased_test(void) if (ret != 0) return ret; #endif -#ifdef HAVE_PKCS12 +#if defined(HAVE_PKCS12) && !defined(NO_ASN) && !defined(NO_PWDBASED) && \ + !defined(NO_HMAC) && !defined(NO_CERTS) /* Test that a crafted PKCS#12 with INT_MAX MAC iterations is rejected * immediately rather than hanging in DoPKCS12Hash(). */ { @@ -32091,7 +32092,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t pwdbased_test(void) } ret = 0; } -#endif /* HAVE_PKCS12 */ +#endif /* HAVE_PKCS12 && !NO_ASN && !NO_PWDBASED && !NO_HMAC && !NO_CERTS */ #ifdef HAVE_SCRYPT ret = scrypt_test(); if (ret != 0) From 0e7a094e83db8034933d0376d090add2e1bc83f2 Mon Sep 17 00:00:00 2001 From: Anthony Hu Date: Wed, 1 Apr 2026 17:24:59 -0400 Subject: [PATCH 04/14] get rid of bad tests --- wolfcrypt/test/test.c | 97 ------------------------------------------- 1 file changed, 97 deletions(-) diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index bd78b16358c..280776ca654 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -32046,53 +32046,6 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t pwdbased_test(void) if (ret != 0) return ret; #endif -#if defined(HAVE_PKCS12) && !defined(NO_ASN) && !defined(NO_PWDBASED) && \ - !defined(NO_HMAC) && !defined(NO_CERTS) - /* Test that a crafted PKCS#12 with INT_MAX MAC iterations is rejected - * immediately rather than hanging in DoPKCS12Hash(). */ - { - static const byte evil_p12[] = { - 0x30, 0x58, 0x02, 0x01, 0x03, 0x30, 0x1e, 0x06, - 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, - 0x07, 0x01, 0xa0, 0x11, 0x04, 0x0f, 0x30, 0x0d, - 0x30, 0x0b, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, - 0xf7, 0x0d, 0x01, 0x07, 0x01, 0x30, 0x33, 0x30, - 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, - 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x04, 0x08, 0x41, 0x41, 0x41, 0x41, - 0x41, 0x41, 0x41, 0x41, 0x02, 0x04, 0x7f, 0xff, - 0xff, 0xff - }; - WC_PKCS12* evilPkcs12 = wc_PKCS12_new_ex(HEAP_HINT); - if (evilPkcs12 == NULL) - return WC_TEST_RET_ENC_EC(MEMORY_E); - - ret = wc_d2i_PKCS12(evil_p12, (word32)sizeof(evil_p12), evilPkcs12); - if (ret == 0) { - byte* evilKey = NULL; - byte* evilCert = NULL; - word32 evilKeySz = 0, evilCertSz = 0; - WC_DerCertList* evilCa = NULL; - - ret = wc_PKCS12_parse(evilPkcs12, "test", &evilKey, &evilKeySz, - &evilCert, &evilCertSz, &evilCa); - XFREE(evilKey, HEAP_HINT, DYNAMIC_TYPE_PKCS); - XFREE(evilCert, HEAP_HINT, DYNAMIC_TYPE_PKCS); - if (evilCa) - wc_FreeCertList(evilCa, HEAP_HINT); - wc_PKCS12_free(evilPkcs12); - /* Parse must fail (iteration cap), not succeed or hang */ - if (ret == 0) - return WC_TEST_RET_ENC_NC; - } - else { - wc_PKCS12_free(evilPkcs12); - } - ret = 0; - } -#endif /* HAVE_PKCS12 && !NO_ASN && !NO_PWDBASED && !NO_HMAC && !NO_CERTS */ #ifdef HAVE_SCRYPT ret = scrypt_test(); if (ret != 0) @@ -32192,56 +32145,6 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t pkcs12_test(void) goto out; } - /* Test that a crafted PKCS#12 with INT_MAX MAC iterations is rejected - * immediately rather than hanging in DoPKCS12Hash(). This is a 90-byte - * minimal PKCS#12 with mac->itt = 0x7FFFFFFF (2,147,483,647). */ - { - static const byte evil_p12[] = { - 0x30, 0x58, 0x02, 0x01, 0x03, 0x30, 0x1e, 0x06, - 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, - 0x07, 0x01, 0xa0, 0x11, 0x04, 0x0f, 0x30, 0x0d, - 0x30, 0x0b, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, - 0xf7, 0x0d, 0x01, 0x07, 0x01, 0x30, 0x33, 0x30, - 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, - 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x04, 0x08, 0x41, 0x41, 0x41, 0x41, - 0x41, 0x41, 0x41, 0x41, 0x02, 0x04, 0x7f, 0xff, - 0xff, 0xff - }; - WC_PKCS12* evilPkcs12 = wc_PKCS12_new_ex(HEAP_HINT); - if (evilPkcs12 == NULL) { - ret = WC_TEST_RET_ENC_EC(MEMORY_E); - goto out; - } - ret = wc_d2i_PKCS12(evil_p12, (word32)sizeof(evil_p12), evilPkcs12); - if (ret != 0) { - wc_PKCS12_free(evilPkcs12); - ret = WC_TEST_RET_ENC_EC(ret); - goto out; - } - { - byte* evilKey = NULL; - byte* evilCert = NULL; - word32 evilKeySz = 0, evilCertSz = 0; - WC_DerCertList* evilCa = NULL; - ret = wc_PKCS12_parse(evilPkcs12, "test", &evilKey, &evilKeySz, - &evilCert, &evilCertSz, &evilCa); - XFREE(evilKey, HEAP_HINT, DYNAMIC_TYPE_PKCS); - XFREE(evilCert, HEAP_HINT, DYNAMIC_TYPE_PKCS); - if (evilCa) - wc_FreeCertList(evilCa, HEAP_HINT); - } - wc_PKCS12_free(evilPkcs12); - /* Must have been rejected (not hung) */ - if (ret == 0) { - ret = WC_TEST_RET_ENC_NC; - goto out; - } - ret = 0; /* rejection is the expected outcome */ - } - out: if (derCaListOut) From 79b4efb9ead00e57a53b7b48d9afb6de2abff5cc Mon Sep 17 00:00:00 2001 From: Anthony Hu Date: Wed, 8 Apr 2026 17:55:53 -0400 Subject: [PATCH 05/14] Limit was too low --- wolfssl/wolfcrypt/pwdbased.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wolfssl/wolfcrypt/pwdbased.h b/wolfssl/wolfcrypt/pwdbased.h index fb56df627f4..dbb084358d0 100644 --- a/wolfssl/wolfcrypt/pwdbased.h +++ b/wolfssl/wolfcrypt/pwdbased.h @@ -41,7 +41,7 @@ * Override by defining WC_PBKDF_MAX_ITERATIONS before including this header. * Normal PKCS#12 files use 1,000–10,000 iterations. */ #ifndef WC_PBKDF_MAX_ITERATIONS - #define WC_PBKDF_MAX_ITERATIONS 100000 + #define WC_PBKDF_MAX_ITERATIONS 2000000 #endif #if FIPS_VERSION3_GE(6,0,0) From e0e6610503d17f68d5d9ebffbc810773cd36e8a8 Mon Sep 17 00:00:00 2001 From: Anthony Hu Date: Fri, 10 Apr 2026 10:45:15 -0400 Subject: [PATCH 06/14] Comment fixup. --- wolfssl/wolfcrypt/pwdbased.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wolfssl/wolfcrypt/pwdbased.h b/wolfssl/wolfcrypt/pwdbased.h index dbb084358d0..8a59f30e86c 100644 --- a/wolfssl/wolfcrypt/pwdbased.h +++ b/wolfssl/wolfcrypt/pwdbased.h @@ -39,7 +39,7 @@ * Attacker-controlled PKCS#12 files can specify iterations up to INT_MAX * (2,147,483,647) in the MAC data, causing hours of CPU time. * Override by defining WC_PBKDF_MAX_ITERATIONS before including this header. - * Normal PKCS#12 files use 1,000–10,000 iterations. */ + * Normal p12 files use 1k to 10k iterations. */ #ifndef WC_PBKDF_MAX_ITERATIONS #define WC_PBKDF_MAX_ITERATIONS 2000000 #endif From 1cd9caca020fc5e2118a17af4a5ceaeab9a558ae Mon Sep 17 00:00:00 2001 From: Anthony Hu Date: Fri, 10 Apr 2026 12:58:40 -0400 Subject: [PATCH 07/14] Line length fixup and repro in second impl. --- wolfcrypt/src/pwdbased.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/wolfcrypt/src/pwdbased.c b/wolfcrypt/src/pwdbased.c index c8b1ad68278..0c30c0e22b8 100644 --- a/wolfcrypt/src/pwdbased.c +++ b/wolfcrypt/src/pwdbased.c @@ -417,7 +417,8 @@ int wc_PKCS12_PBKDF_ex(byte* output, const byte* passwd, int passLen, iterations = 1; if (iterations > WC_PBKDF_MAX_ITERATIONS) { - WOLFSSL_MSG("PKCS12 PBKDF iteration count exceeds WC_PBKDF_MAX_ITERATIONS"); + WOLFSSL_MSG("PKCS12 PBKDF iteration count exceeds " + "WC_PBKDF_MAX_ITERATIONS"); return BAD_FUNC_ARG; } @@ -649,6 +650,12 @@ int wc_PKCS12_PBKDF_ex(byte* output, const byte* passwd, int passLen, iterations = 1; } + if (iterations > WC_PBKDF_MAX_ITERATIONS) { + WOLFSSL_MSG("PKCS12 PBKDF iteration count exceeds " + "WC_PBKDF_MAX_ITERATIONS"); + return BAD_FUNC_ARG; + } + /* u = hash output size. */ hashT = wc_HashTypeConvert(hashType); ret = wc_HashGetDigestSize(hashT); From 03ef4562e841025af1482d91900aa8bd98f4fb47 Mon Sep 17 00:00:00 2001 From: Anthony Hu Date: Wed, 15 Apr 2026 17:36:33 -0400 Subject: [PATCH 08/14] Known macros --- .wolfssl_known_macro_extras | 1 + 1 file changed, 1 insertion(+) diff --git a/.wolfssl_known_macro_extras b/.wolfssl_known_macro_extras index 4db52f1914b..ef25bf38b22 100644 --- a/.wolfssl_known_macro_extras +++ b/.wolfssl_known_macro_extras @@ -667,6 +667,7 @@ WC_NO_ASYNC_SLEEP WC_NO_RNG_SIMPLE WC_NO_STATIC_ASSERT WC_NO_VERBOSE_RNG +WC_PBKDF_MAX_ITERATIONS WC_PKCS11_FIND_WITH_ID_ONLY WC_PKCS12_PBKDF_USING_MP_API WC_PROTECT_ENCRYPTED_MEM From c4be7f3f59fcda7074aa44067b0050c4a4d8ac16 Mon Sep 17 00:00:00 2001 From: Anthony Hu Date: Tue, 28 Apr 2026 11:19:50 -0400 Subject: [PATCH 09/14] API Docs --- doc/dox_comments/header_files/pwdbased.h | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/doc/dox_comments/header_files/pwdbased.h b/doc/dox_comments/header_files/pwdbased.h index 25870b835bf..d2418b96b5c 100644 --- a/doc/dox_comments/header_files/pwdbased.h +++ b/doc/dox_comments/header_files/pwdbased.h @@ -174,7 +174,8 @@ int wc_PKCS12_PBKDF(byte* output, const byte* passwd, int passLen, \brief Extended version of PBKDF1 with heap hint. \return 0 on success - \return BAD_FUNC_ARG on invalid arguments + \return BAD_FUNC_ARG on invalid arguments or iterations is greater than + WC_PBKDF_MAX_ITERATIONS \return MEMORY_E on memory allocation error \param key Output key buffer @@ -209,7 +210,8 @@ int wc_PBKDF1_ex(byte* key, int keyLen, byte* iv, int ivLen, \brief Extended version of PBKDF2 with heap hint and device ID. \return 0 on success - \return BAD_FUNC_ARG on invalid arguments + \return BAD_FUNC_ARG on invalid arguments or iterations is greater than + WC_PBKDF_MAX_ITERATIONS \return MEMORY_E on memory allocation error \param output Output key buffer @@ -244,7 +246,8 @@ int wc_PBKDF2_ex(byte* output, const byte* passwd, int pLen, \brief Extended version of PKCS12_PBKDF with heap hint. \return 0 on success - \return BAD_FUNC_ARG on invalid arguments + \return BAD_FUNC_ARG on invalid arguments or iterations is greater than + WC_PBKDF_MAX_ITERATIONS \return MEMORY_E on memory allocation error \param output Output key buffer From f248b272db07a9ecdc579fa6a9406314c0f66be7 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Fri, 1 May 2026 13:29:16 -0500 Subject: [PATCH 10/14] rename WC_PBKDF_MAX_ITERATIONS to WC_PBKDF_DEFAULT_MAX_ITERATIONS, raise it to 10000000, add wc_PBKDF_max_iterations_set() and wc_PBKDF_max_iterations_get(), and restore new negative tests in pwdbased_test(). --- .wolfssl_known_macro_extras | 1 - doc/dox_comments/header_files/pwdbased.h | 53 ++++++++++++- wolfcrypt/src/pwdbased.c | 32 ++++++-- wolfcrypt/test/test.c | 97 ++++++++++++++++++++++++ wolfssl/wolfcrypt/pwdbased.h | 19 ++++- 5 files changed, 186 insertions(+), 16 deletions(-) diff --git a/.wolfssl_known_macro_extras b/.wolfssl_known_macro_extras index ef25bf38b22..4db52f1914b 100644 --- a/.wolfssl_known_macro_extras +++ b/.wolfssl_known_macro_extras @@ -667,7 +667,6 @@ WC_NO_ASYNC_SLEEP WC_NO_RNG_SIMPLE WC_NO_STATIC_ASSERT WC_NO_VERBOSE_RNG -WC_PBKDF_MAX_ITERATIONS WC_PKCS11_FIND_WITH_ID_ONLY WC_PKCS12_PBKDF_USING_MP_API WC_PROTECT_ENCRYPTED_MEM diff --git a/doc/dox_comments/header_files/pwdbased.h b/doc/dox_comments/header_files/pwdbased.h index d2418b96b5c..eef4955ee01 100644 --- a/doc/dox_comments/header_files/pwdbased.h +++ b/doc/dox_comments/header_files/pwdbased.h @@ -175,7 +175,7 @@ int wc_PKCS12_PBKDF(byte* output, const byte* passwd, int passLen, \return 0 on success \return BAD_FUNC_ARG on invalid arguments or iterations is greater than - WC_PBKDF_MAX_ITERATIONS + current_wc_pbkdf_max_iterations \return MEMORY_E on memory allocation error \param key Output key buffer @@ -200,6 +200,8 @@ int wc_PKCS12_PBKDF(byte* output, const byte* passwd, int passLen, \endcode \sa wc_PBKDF1 + \sa wc_PBKDF_max_iterations_set + \sa wc_PBKDF_max_iterations_get */ int wc_PBKDF1_ex(byte* key, int keyLen, byte* iv, int ivLen, const byte* passwd, int passwdLen, const byte* salt, int saltLen, @@ -211,7 +213,7 @@ int wc_PBKDF1_ex(byte* key, int keyLen, byte* iv, int ivLen, \return 0 on success \return BAD_FUNC_ARG on invalid arguments or iterations is greater than - WC_PBKDF_MAX_ITERATIONS + current_wc_pbkdf_max_iterations \return MEMORY_E on memory allocation error \param output Output key buffer @@ -236,6 +238,8 @@ int wc_PBKDF1_ex(byte* key, int keyLen, byte* iv, int ivLen, \endcode \sa wc_PBKDF2 + \sa wc_PBKDF_max_iterations_set + \sa wc_PBKDF_max_iterations_get */ int wc_PBKDF2_ex(byte* output, const byte* passwd, int pLen, const byte* salt, int sLen, int iterations, int kLen, @@ -247,7 +251,7 @@ int wc_PBKDF2_ex(byte* output, const byte* passwd, int pLen, \return 0 on success \return BAD_FUNC_ARG on invalid arguments or iterations is greater than - WC_PBKDF_MAX_ITERATIONS + current_wc_pbkdf_max_iterations \return MEMORY_E on memory allocation error \param output Output key buffer @@ -271,6 +275,8 @@ int wc_PBKDF2_ex(byte* output, const byte* passwd, int pLen, \endcode \sa wc_PKCS12_PBKDF + \sa wc_PBKDF_max_iterations_set + \sa wc_PBKDF_max_iterations_get */ int wc_PKCS12_PBKDF_ex(byte* output, const byte* passwd,int passLen, const byte* salt, int saltLen, int iterations, int kLen, @@ -341,3 +347,44 @@ int wc_scrypt(byte* output, const byte* passwd, int passLen, int wc_scrypt_ex(byte* output, const byte* passwd, int passLen, const byte* salt, int saltLen, word32 iterations, int blockSize, int parallel, int dkLen); + +/*! + \ingroup Password + \brief Set the current iteration limit for PBKDF. + + By default, the iteration limit is set to WC_PBKDF_DEFAULT_MAX_ITERATIONS, + which can be overridden at build time. This function allows runtime + override of the limit. + + Note that `wc_PBKDF_max_iterations_set()` has no provisions for thread + synchronization. Users should arrange to call it at startup or idle times, + when there are no other PBKDF calls in progress. + + \return Previous iteration limit on success + \return BAD_FUNC_ARG on invalid arguments + + \param iters The new iteration limit. + + _Example_ + \code + int prev_iter_limit = wc_PBKDF_max_iterations_set(100000000); + \endcode + + \sa wc_scrypt +*/ +int wc_PBKDF_max_iterations_set(int iters); + +/*! + \ingroup Password + \brief Get the current iteration limit for PBKDF. + + \return Current iteration limit + + _Example_ + \code + int cur_iter_limit = wc_PBKDF_max_iterations_get(); + \endcode + + \sa wc_scrypt +*/ +int wc_PBKDF_max_iterations_get(void); diff --git a/wolfcrypt/src/pwdbased.c b/wolfcrypt/src/pwdbased.c index 0c30c0e22b8..6737d8738cb 100644 --- a/wolfcrypt/src/pwdbased.c +++ b/wolfcrypt/src/pwdbased.c @@ -54,6 +54,22 @@ } #endif +static int current_wc_pbkdf_max_iterations = WC_PBKDF_DEFAULT_MAX_ITERATIONS; + +int wc_PBKDF_max_iterations_set(int iters) { + if (iters <= 0) + return BAD_FUNC_ARG; + else { + int prev = current_wc_pbkdf_max_iterations; + current_wc_pbkdf_max_iterations = iters; + return prev; + } +} + +int wc_PBKDF_max_iterations_get(void) { + return current_wc_pbkdf_max_iterations; +} + #ifdef HAVE_PBKDF1 /* PKCS#5 v1.5 with non standard extension to optionally derive the extra data (IV) */ @@ -82,8 +98,8 @@ int wc_PBKDF1_ex(byte* key, int keyLen, byte* iv, int ivLen, if (iterations <= 0) iterations = 1; - if (iterations > WC_PBKDF_MAX_ITERATIONS) { - WOLFSSL_MSG("PBKDF1 iteration count exceeds WC_PBKDF_MAX_ITERATIONS"); + if (iterations > current_wc_pbkdf_max_iterations) { + WOLFSSL_MSG("PBKDF1 iteration count exceeds current_wc_pbkdf_max_iterations"); return BAD_FUNC_ARG; } @@ -223,8 +239,8 @@ int wc_PBKDF2_ex(byte* output, const byte* passwd, int pLen, const byte* salt, if (iterations <= 0) iterations = 1; - if (iterations > WC_PBKDF_MAX_ITERATIONS) { - WOLFSSL_MSG("PBKDF2 iteration count exceeds WC_PBKDF_MAX_ITERATIONS"); + if (iterations > current_wc_pbkdf_max_iterations) { + WOLFSSL_MSG("PBKDF2 iteration count exceeds current_wc_pbkdf_max_iterations"); return BAD_FUNC_ARG; } @@ -416,9 +432,9 @@ int wc_PKCS12_PBKDF_ex(byte* output, const byte* passwd, int passLen, if (iterations <= 0) iterations = 1; - if (iterations > WC_PBKDF_MAX_ITERATIONS) { + if (iterations > current_wc_pbkdf_max_iterations) { WOLFSSL_MSG("PKCS12 PBKDF iteration count exceeds " - "WC_PBKDF_MAX_ITERATIONS"); + "current_wc_pbkdf_max_iterations"); return BAD_FUNC_ARG; } @@ -650,9 +666,9 @@ int wc_PKCS12_PBKDF_ex(byte* output, const byte* passwd, int passLen, iterations = 1; } - if (iterations > WC_PBKDF_MAX_ITERATIONS) { + if (iterations > current_wc_pbkdf_max_iterations) { WOLFSSL_MSG("PKCS12 PBKDF iteration count exceeds " - "WC_PBKDF_MAX_ITERATIONS"); + "current_wc_pbkdf_max_iterations"); return BAD_FUNC_ARG; } diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 280776ca654..bd78b16358c 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -32046,6 +32046,53 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t pwdbased_test(void) if (ret != 0) return ret; #endif +#if defined(HAVE_PKCS12) && !defined(NO_ASN) && !defined(NO_PWDBASED) && \ + !defined(NO_HMAC) && !defined(NO_CERTS) + /* Test that a crafted PKCS#12 with INT_MAX MAC iterations is rejected + * immediately rather than hanging in DoPKCS12Hash(). */ + { + static const byte evil_p12[] = { + 0x30, 0x58, 0x02, 0x01, 0x03, 0x30, 0x1e, 0x06, + 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, + 0x07, 0x01, 0xa0, 0x11, 0x04, 0x0f, 0x30, 0x0d, + 0x30, 0x0b, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x07, 0x01, 0x30, 0x33, 0x30, + 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, + 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x08, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x02, 0x04, 0x7f, 0xff, + 0xff, 0xff + }; + WC_PKCS12* evilPkcs12 = wc_PKCS12_new_ex(HEAP_HINT); + if (evilPkcs12 == NULL) + return WC_TEST_RET_ENC_EC(MEMORY_E); + + ret = wc_d2i_PKCS12(evil_p12, (word32)sizeof(evil_p12), evilPkcs12); + if (ret == 0) { + byte* evilKey = NULL; + byte* evilCert = NULL; + word32 evilKeySz = 0, evilCertSz = 0; + WC_DerCertList* evilCa = NULL; + + ret = wc_PKCS12_parse(evilPkcs12, "test", &evilKey, &evilKeySz, + &evilCert, &evilCertSz, &evilCa); + XFREE(evilKey, HEAP_HINT, DYNAMIC_TYPE_PKCS); + XFREE(evilCert, HEAP_HINT, DYNAMIC_TYPE_PKCS); + if (evilCa) + wc_FreeCertList(evilCa, HEAP_HINT); + wc_PKCS12_free(evilPkcs12); + /* Parse must fail (iteration cap), not succeed or hang */ + if (ret == 0) + return WC_TEST_RET_ENC_NC; + } + else { + wc_PKCS12_free(evilPkcs12); + } + ret = 0; + } +#endif /* HAVE_PKCS12 && !NO_ASN && !NO_PWDBASED && !NO_HMAC && !NO_CERTS */ #ifdef HAVE_SCRYPT ret = scrypt_test(); if (ret != 0) @@ -32145,6 +32192,56 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t pkcs12_test(void) goto out; } + /* Test that a crafted PKCS#12 with INT_MAX MAC iterations is rejected + * immediately rather than hanging in DoPKCS12Hash(). This is a 90-byte + * minimal PKCS#12 with mac->itt = 0x7FFFFFFF (2,147,483,647). */ + { + static const byte evil_p12[] = { + 0x30, 0x58, 0x02, 0x01, 0x03, 0x30, 0x1e, 0x06, + 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, + 0x07, 0x01, 0xa0, 0x11, 0x04, 0x0f, 0x30, 0x0d, + 0x30, 0x0b, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x07, 0x01, 0x30, 0x33, 0x30, + 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, + 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x08, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x02, 0x04, 0x7f, 0xff, + 0xff, 0xff + }; + WC_PKCS12* evilPkcs12 = wc_PKCS12_new_ex(HEAP_HINT); + if (evilPkcs12 == NULL) { + ret = WC_TEST_RET_ENC_EC(MEMORY_E); + goto out; + } + ret = wc_d2i_PKCS12(evil_p12, (word32)sizeof(evil_p12), evilPkcs12); + if (ret != 0) { + wc_PKCS12_free(evilPkcs12); + ret = WC_TEST_RET_ENC_EC(ret); + goto out; + } + { + byte* evilKey = NULL; + byte* evilCert = NULL; + word32 evilKeySz = 0, evilCertSz = 0; + WC_DerCertList* evilCa = NULL; + ret = wc_PKCS12_parse(evilPkcs12, "test", &evilKey, &evilKeySz, + &evilCert, &evilCertSz, &evilCa); + XFREE(evilKey, HEAP_HINT, DYNAMIC_TYPE_PKCS); + XFREE(evilCert, HEAP_HINT, DYNAMIC_TYPE_PKCS); + if (evilCa) + wc_FreeCertList(evilCa, HEAP_HINT); + } + wc_PKCS12_free(evilPkcs12); + /* Must have been rejected (not hung) */ + if (ret == 0) { + ret = WC_TEST_RET_ENC_NC; + goto out; + } + ret = 0; /* rejection is the expected outcome */ + } + out: if (derCaListOut) diff --git a/wolfssl/wolfcrypt/pwdbased.h b/wolfssl/wolfcrypt/pwdbased.h index 8a59f30e86c..171a22513c9 100644 --- a/wolfssl/wolfcrypt/pwdbased.h +++ b/wolfssl/wolfcrypt/pwdbased.h @@ -38,12 +38,23 @@ /* Maximum allowed PBKDF iteration count to prevent CPU exhaustion DoS. * Attacker-controlled PKCS#12 files can specify iterations up to INT_MAX * (2,147,483,647) in the MAC data, causing hours of CPU time. - * Override by defining WC_PBKDF_MAX_ITERATIONS before including this header. - * Normal p12 files use 1k to 10k iterations. */ -#ifndef WC_PBKDF_MAX_ITERATIONS - #define WC_PBKDF_MAX_ITERATIONS 2000000 + * Override by defining WC_PBKDF_DEFAULT_MAX_ITERATIONS before including + * this header, and override at runtime by calling + * wc_PBKDF_max_iterations_set() at application startup. + * + * Note that typical PKCS12 files use 1k to 10k iterations. + */ +#ifndef WC_PBKDF_DEFAULT_MAX_ITERATIONS + #define WC_PBKDF_DEFAULT_MAX_ITERATIONS 10000000 #endif +/* Note that wc_PBKDF_max_iterations_set() has no provisions for thread + * synchronization. Users should arrange to call it at startup or idle times, + * when there are no other PBKDF calls in progress. + */ +WOLFSSL_API int wc_PBKDF_max_iterations_set(int iters); +WOLFSSL_API int wc_PBKDF_max_iterations_get(void); + #if FIPS_VERSION3_GE(6,0,0) extern const unsigned int wolfCrypt_FIPS_pbkdf_ro_sanity[2]; WOLFSSL_LOCAL int wolfCrypt_FIPS_PBKDF_sanity(void); From 5b687baa94802ec1635e776cc1d03554a5981682 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Fri, 1 May 2026 15:20:02 -0500 Subject: [PATCH 11/14] wolfcrypt/test/test.c and wolfcrypt/test/test.h: * add correct gating around pbkdf1_test(), pkcs12_pbkdf_test(), and scrypt_test() prototypes; * add unit tests for wc_PBKDF_max_iterations_set() and wc_PBKDF_max_iterations_get() in pbkdf2_test(); * fix pkcs12_test() to skip the evilPkcs12 test if evil_p12 can't be parsed for any reason, mirroring the new stanza around evil_p12 in pwdbased_test(). --- wolfcrypt/test/test.c | 53 +++++++++++++++++++++++++++++++------------ wolfcrypt/test/test.h | 6 +++++ 2 files changed, 45 insertions(+), 14 deletions(-) diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index bd78b16358c..a3b25568e2d 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -917,12 +917,18 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t openSSL_evpMD_test(void); WOLFSSL_TEST_SUBROUTINE wc_test_ret_t openssl_evpSig_test(void); #endif +#if defined(HAVE_PBKDF1) && !defined(NO_SHA) WOLFSSL_TEST_SUBROUTINE wc_test_ret_t pbkdf1_test(void); +#endif +#if defined(HAVE_PKCS12) && !defined(NO_SHA256) WOLFSSL_TEST_SUBROUTINE wc_test_ret_t pkcs12_pbkdf_test(void); +#endif #if defined(HAVE_PBKDF2) && !defined(NO_SHA256) && !defined(NO_HMAC) WOLFSSL_TEST_SUBROUTINE wc_test_ret_t pbkdf2_test(void); #endif +#if !defined(NO_PWDBASED) && defined(HAVE_SCRYPT) WOLFSSL_TEST_SUBROUTINE wc_test_ret_t scrypt_test(void); +#endif #ifdef HAVE_ECC WOLFSSL_TEST_SUBROUTINE wc_test_ret_t ecc_test(void); #if defined(HAVE_ECC_ENCRYPT) && defined(HAVE_AES_CBC) && \ @@ -31991,7 +31997,31 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t pbkdf2_test(void) if (XMEMCMP(derived, verify, sizeof(verify)) != 0) return WC_TEST_RET_ENC_NC; - return 0; + { + int cur_pbkdf_limit = wc_PBKDF_max_iterations_set(iterations - 1); + if (cur_pbkdf_limit <= 0) + return WC_TEST_RET_ENC_EC(cur_pbkdf_limit); + ret = wc_PBKDF2_ex(derived, (byte*)passwd, (int)XSTRLEN(passwd), + salt, (int)sizeof(salt), iterations, + kLen, WC_SHA256, HEAP_HINT, devId); + if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + return WC_TEST_RET_ENC_EC(ret); + ret = wc_PBKDF_max_iterations_set(-1); + if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + return WC_TEST_RET_ENC_EC(ret); + ret = wc_PBKDF_max_iterations_set(0); + if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + return WC_TEST_RET_ENC_EC(ret); + ret = wc_PBKDF_max_iterations_get(); + if (ret != iterations - 1) + return WC_TEST_RET_ENC_NC; + ret = wc_PBKDF_max_iterations_set(cur_pbkdf_limit); + if (ret != iterations - 1) + return WC_TEST_RET_ENC_EC(ret); + ret = 0; + } + + return ret; } #endif /* HAVE_PBKDF2 && !NO_SHA256 && !NO_HMAC */ @@ -32216,12 +32246,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t pkcs12_test(void) goto out; } ret = wc_d2i_PKCS12(evil_p12, (word32)sizeof(evil_p12), evilPkcs12); - if (ret != 0) { - wc_PKCS12_free(evilPkcs12); - ret = WC_TEST_RET_ENC_EC(ret); - goto out; - } - { + if (ret == 0) { byte* evilKey = NULL; byte* evilCert = NULL; word32 evilKeySz = 0, evilCertSz = 0; @@ -32232,14 +32257,14 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t pkcs12_test(void) XFREE(evilCert, HEAP_HINT, DYNAMIC_TYPE_PKCS); if (evilCa) wc_FreeCertList(evilCa, HEAP_HINT); + wc_PKCS12_free(evilPkcs12); + /* Must have been rejected (not hung) */ + if (ret == 0) { + ret = WC_TEST_RET_ENC_NC; + goto out; + } + ret = 0; /* rejection is the expected outcome */ } - wc_PKCS12_free(evilPkcs12); - /* Must have been rejected (not hung) */ - if (ret == 0) { - ret = WC_TEST_RET_ENC_NC; - goto out; - } - ret = 0; /* rejection is the expected outcome */ } out: diff --git a/wolfcrypt/test/test.h b/wolfcrypt/test/test.h index 016ca6553a0..97be48121b7 100644 --- a/wolfcrypt/test/test.h +++ b/wolfcrypt/test/test.h @@ -266,12 +266,18 @@ extern WOLFSSL_TEST_SUBROUTINE wc_test_ret_t openSSL_evpMD_test(void); extern WOLFSSL_TEST_SUBROUTINE wc_test_ret_t openssl_evpSig_test(void); #endif +#if defined(HAVE_PBKDF1) && !defined(NO_SHA) extern WOLFSSL_TEST_SUBROUTINE wc_test_ret_t pbkdf1_test(void); +#endif +#if defined(HAVE_PKCS12) && !defined(NO_SHA256) extern WOLFSSL_TEST_SUBROUTINE wc_test_ret_t pkcs12_pbkdf_test(void); +#endif #if defined(HAVE_PBKDF2) && !defined(NO_SHA256) && !defined(NO_HMAC) extern WOLFSSL_TEST_SUBROUTINE wc_test_ret_t pbkdf2_test(void); #endif +#if !defined(NO_PWDBASED) && defined(HAVE_SCRYPT) extern WOLFSSL_TEST_SUBROUTINE wc_test_ret_t scrypt_test(void); +#endif #ifdef HAVE_ECC extern WOLFSSL_TEST_SUBROUTINE wc_test_ret_t ecc_test(void); #if defined(HAVE_ECC_ENCRYPT) && defined(HAVE_AES_CBC) && \ From b2a56e794706f72c3218d9d8f8d7a1130d225b89 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Thu, 7 May 2026 17:20:57 -0500 Subject: [PATCH 12/14] wolfcrypt/src/pwdbased.c: * fix typography of wc_PBKDF_max_iterations_set() and wc_PBKDF_max_iterations_get() (peer review). * refactor overflow prevention in wc_PKCS12_PBKDF_ex() to use WC_SAFE_SUM_UNSIGNED(). wolfcrypt/test/test.c: in pwdbased_test(), omit "INT_MAX MAC iterations" test if WOLFSSL_NO_MALLOC (uses wc_PKCS12_new_ex()). --- wolfcrypt/src/pwdbased.c | 26 +++++++++----------------- wolfcrypt/test/test.c | 5 +++-- 2 files changed, 12 insertions(+), 19 deletions(-) diff --git a/wolfcrypt/src/pwdbased.c b/wolfcrypt/src/pwdbased.c index 6737d8738cb..9f59568b575 100644 --- a/wolfcrypt/src/pwdbased.c +++ b/wolfcrypt/src/pwdbased.c @@ -56,7 +56,8 @@ static int current_wc_pbkdf_max_iterations = WC_PBKDF_DEFAULT_MAX_ITERATIONS; -int wc_PBKDF_max_iterations_set(int iters) { +int wc_PBKDF_max_iterations_set(int iters) +{ if (iters <= 0) return BAD_FUNC_ARG; else { @@ -66,7 +67,8 @@ int wc_PBKDF_max_iterations_set(int iters) { } } -int wc_PBKDF_max_iterations_get(void) { +int wc_PBKDF_max_iterations_get(void) +{ return current_wc_pbkdf_max_iterations; } @@ -475,22 +477,17 @@ int wc_PKCS12_PBKDF_ex(byte* output, const byte* passwd, int passLen, * must be 1 or greater here and is always 'true' */ pLen = v * (((word32)passLen + v - 1) / v); - /* Guard against overflow in iLen = sLen + pLen and totalLen = dLen + iLen. - * Individual sLen/pLen values fit in word32 (max 0x80000000 for INT_MAX - * inputs), but their sum can overflow. */ - if (sLen > 0xFFFFFFFFU - pLen) { + if (! WC_SAFE_SUM_UNSIGNED(word32, sLen, pLen, iLen)) { WC_FREE_VAR_EX(Ai, heap, DYNAMIC_TYPE_TMP_BUFFER); WC_FREE_VAR_EX(B, heap, DYNAMIC_TYPE_TMP_BUFFER); return BAD_FUNC_ARG; } - iLen = sLen + pLen; - if (iLen > 0xFFFFFFFFU - dLen) { + if (! WC_SAFE_SUM_UNSIGNED(word32, dLen, sLen, totalLen)) { WC_FREE_VAR_EX(Ai, heap, DYNAMIC_TYPE_TMP_BUFFER); WC_FREE_VAR_EX(B, heap, DYNAMIC_TYPE_TMP_BUFFER); return BAD_FUNC_ARG; } - totalLen = dLen + sLen + pLen; if (totalLen > sizeof(staticBuffer)) { buffer = (byte*)XMALLOC(totalLen, heap, DYNAMIC_TYPE_KEY); @@ -694,19 +691,14 @@ int wc_PKCS12_PBKDF_ex(byte* output, const byte* passwd, int passLen, /* RFC 7292 B.2 step 3: P = password repeated to ceil(passLen/v)*v bytes */ pLen = v * (((word32)passLen + v - 1) / v); - /* Guard against overflow in iLen = sLen + pLen and totalLen = v + iLen. - * Individual sLen/pLen values fit in word32 (max 0x80000000 for INT_MAX - * inputs), but their sum can overflow. */ - if (sLen > 0xFFFFFFFFU - pLen) { + /* RFC 7292 B.2 step 4: I = S || P */ + if (! WC_SAFE_SUM_UNSIGNED(word32, sLen, pLen, iLen)) { return BAD_FUNC_ARG; } - /* RFC 7292 B.2 step 4: I = S || P */ - iLen = sLen + pLen; - if (iLen > 0xFFFFFFFFU - v) { + if (! WC_SAFE_SUM_UNSIGNED(word32, v, iLen, totalLen)) { return BAD_FUNC_ARG; } - totalLen = v + iLen; nwc = v / (word32)sizeof(PKCS12_WORD); nBlocks = iLen / v; diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index a3b25568e2d..e5351fa02d7 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -32077,7 +32077,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t pwdbased_test(void) return ret; #endif #if defined(HAVE_PKCS12) && !defined(NO_ASN) && !defined(NO_PWDBASED) && \ - !defined(NO_HMAC) && !defined(NO_CERTS) + !defined(NO_HMAC) && !defined(NO_CERTS) && !defined(WOLFSSL_NO_MALLOC) /* Test that a crafted PKCS#12 with INT_MAX MAC iterations is rejected * immediately rather than hanging in DoPKCS12Hash(). */ { @@ -32122,7 +32122,8 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t pwdbased_test(void) } ret = 0; } -#endif /* HAVE_PKCS12 && !NO_ASN && !NO_PWDBASED && !NO_HMAC && !NO_CERTS */ +#endif /* HAVE_PKCS12 && !NO_ASN && !NO_PWDBASED && !NO_HMAC && !NO_CERTS && */ + /* !WOLFSSL_NO_MALLOC */ #ifdef HAVE_SCRYPT ret = scrypt_test(); if (ret != 0) From 0470910acbba678043d598b6c9a5c41379c67943 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Mon, 11 May 2026 16:23:39 -0500 Subject: [PATCH 13/14] wolfcrypt/test/test.c: fix unused-result warnings and unencoded result codes in pwdbased_test(). --- wolfcrypt/test/test.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index e5351fa02d7..1fb03095507 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -32100,7 +32100,9 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t pwdbased_test(void) return WC_TEST_RET_ENC_EC(MEMORY_E); ret = wc_d2i_PKCS12(evil_p12, (word32)sizeof(evil_p12), evilPkcs12); - if (ret == 0) { + if (ret != 0) + ret = WC_TEST_RET_ENC_EC(ret); + else { byte* evilKey = NULL; byte* evilCert = NULL; word32 evilKeySz = 0, evilCertSz = 0; @@ -32108,26 +32110,24 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t pwdbased_test(void) ret = wc_PKCS12_parse(evilPkcs12, "test", &evilKey, &evilKeySz, &evilCert, &evilCertSz, &evilCa); + /* Parse must fail (iteration cap), not succeed or hang */ + if (ret == 0) + ret = WC_TEST_RET_ENC_NC; + else + ret = 0; XFREE(evilKey, HEAP_HINT, DYNAMIC_TYPE_PKCS); XFREE(evilCert, HEAP_HINT, DYNAMIC_TYPE_PKCS); if (evilCa) wc_FreeCertList(evilCa, HEAP_HINT); - wc_PKCS12_free(evilPkcs12); - /* Parse must fail (iteration cap), not succeed or hang */ - if (ret == 0) - return WC_TEST_RET_ENC_NC; - } - else { - wc_PKCS12_free(evilPkcs12); } - ret = 0; + wc_PKCS12_free(evilPkcs12); + if (ret != 0) + return ret; } #endif /* HAVE_PKCS12 && !NO_ASN && !NO_PWDBASED && !NO_HMAC && !NO_CERTS && */ /* !WOLFSSL_NO_MALLOC */ #ifdef HAVE_SCRYPT ret = scrypt_test(); - if (ret != 0) - return ret; #endif return ret; } From 717ce03614b79dc478e3ebfae764680ace280b87 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Mon, 11 May 2026 16:24:09 -0500 Subject: [PATCH 14/14] .wolfssl_known_macro_extras: clean up unneeded and out-of-order ents. --- .wolfssl_known_macro_extras | 29 ++++------------------------- 1 file changed, 4 insertions(+), 25 deletions(-) diff --git a/.wolfssl_known_macro_extras b/.wolfssl_known_macro_extras index 4db52f1914b..74386583cf2 100644 --- a/.wolfssl_known_macro_extras +++ b/.wolfssl_known_macro_extras @@ -30,8 +30,8 @@ ARDUINO_UNOR4_WIFI ASN_DUMP_OID ASN_TEMPLATE_SKIP_ISCA_CHECK ATCAPRINTF -ATCA_HAL_I2C ATCA_ENABLE_DEPRECATED +ATCA_HAL_I2C ATCA_TFLEX_SUPPORT ATECC_DEV_TYPE AVR @@ -471,7 +471,6 @@ NO_WOLFSSL_RENESAS_FSPSM_AES NO_WOLFSSL_RENESAS_FSPSM_HASH NO_WOLFSSL_RENESAS_TSIP_CRYPT_AES NO_WOLFSSL_SHA256 -NO_WOLFSSL_SHA256_INTERLEAVE NO_WOLFSSL_SHA512_INTERLEAVE NO_WOLFSSL_SKIP_TRAILING_PAD NO_WOLFSSL_SMALL_STACK_STATIC @@ -628,7 +627,6 @@ USS_API WC_AESXTS_STREAM_NO_REQUEST_ACCOUNTING WC_AES_BS_WORD_SIZE WC_AES_GCM_DEC_AUTH_EARLY -WC_ALLOW_ECC_ZERO_HASH WC_ASN_HASH_SHA256 WC_ASN_RUNTIME_DATE_CHECK_CONTROL WC_ASYNC_ENABLE_ECC_KEYGEN @@ -653,8 +651,6 @@ WC_ASYNC_NO_SHA512 WC_ASYNC_NO_X25519 WC_ASYNC_THREAD_BIND WC_CACHE_RESISTANT_BASE64_TABLE -WC_DILITHIUM_CACHE_PRIV_VECTORS -WC_DILITHIUM_CACHE_PUB_VECTORS WC_DILITHIUM_FIXED_ARRAY WC_DISABLE_RADIX_ZERO_PAD WC_FLAG_DONT_USE_AESNI @@ -719,11 +715,9 @@ WOLFSSL_ASN_EXTRA WOLFSSL_ASN_TEMPLATE_NEED_SET_INT32 WOLFSSL_ASN_TEMPLATE_TYPE_CHECK WOLFSSL_ATECC508 -WOLFSSL_ATECC508A_NOIDLE WOLFSSL_ATECC508A_NOSOFTECC WOLFSSL_ATECC508A_TLS WOLFSSL_ATECC_ECDH_IOENC -WOLFSSL_ATECC_NO_ECDH_ENC WOLFSSL_ATECC_RNG WOLFSSL_ATECC_TFLXTLS WOLFSSL_ATECC_TNGTLS @@ -744,15 +738,11 @@ WOLFSSL_CLIENT_EXAMPLE WOLFSSL_CONTIKI WOLFSSL_CRL_ALLOW_MISSING_CDP WOLFSSL_DILITHIUM_ASSIGN_KEY -WOLFSSL_DILITHIUM_NO_ASN1 WOLFSSL_DILITHIUM_NO_CHECK_KEY WOLFSSL_DILITHIUM_NO_MAKE WOLFSSL_DILITHIUM_REVERSE_HASH_OID WOLFSSL_DILITHIUM_SIGN_CHECK_W0 WOLFSSL_DILITHIUM_SIGN_CHECK_Y -WOLFSSL_DILITHIUM_SIGN_SMALL_MEM_PRECALC -WOLFSSL_DILITHIUM_SIGN_SMALL_MEM_PRECALC_A -WOLFSSL_DILITHIUM_SMALL_MEM_POLY64 WOLFSSL_DISABLE_EARLY_SANITY_CHECKS WOLFSSL_DRBG_SHA256 WOLFSSL_DTLS_DISALLOW_FUTURE @@ -809,10 +799,8 @@ WOLFSSL_LINUXKM_USE_GET_RANDOM_USER_KRETPROBE WOLFSSL_LINUXKM_USE_MUTEXES WOLFSSL_LMS_CACHE_BITS WOLFSSL_LMS_FULL_HASH -WOLFSSL_LMS_LARGE_CACHES WOLFSSL_LMS_MAX_HEIGHT WOLFSSL_LMS_MAX_LEVELS -WOLFSSL_LMS_NO_SIG_CACHE WOLFSSL_LMS_ROOT_LEVELS WOLFSSL_LPC43xx WOLFSSL_MAKE_SYSTEM_NAME_LINUX @@ -869,16 +857,10 @@ WOLFSSL_NO_TICKET_EXPIRE WOLFSSL_NO_TRUSTED_CERTS_VERIFY WOLFSSL_NO_WORD64_OPS WOLFSSL_NO_XOR_OPS -WOLFSSL_NXP_LPC55S6X -WOLFSSL_NXP_CASPER -WOLFSSL_NXP_CASPER_ECC_MULMOD -WOLFSSL_NXP_CASPER_ECC_MUL2ADD -WOLFSSL_NXP_CASPER_RSA_PUB_EXPTMOD -WOLFSSL_NXP_HASHCRYPT -WOLFSSL_NXP_HASHCRYPT_AES -WOLFSSL_NXP_HASHCRYPT_SHA -WOLFSSL_NXP_RNG_1 WOLFSSL_NRF51_AES +WOLFSSL_NXP_CASPER_ECC_MUL2ADD +WOLFSSL_NXP_CASPER_ECC_MULMOD +WOLFSSL_NXP_LPC55S6X WOLFSSL_OLDTLS_AEAD_CIPHERSUITES WOLFSSL_OLD_SET_CURVES_LIST WOLFSSL_OLD_TIMINGPADVERIFY @@ -971,10 +953,8 @@ WOLFSSL_USE_FLASHMEM WOLFSSL_USE_FORCE_ZERO WOLFSSL_USE_OPTIONS_H WOLFSSL_VALIDATE_DH_KEYGEN -WOLFSSL_WC_LMS_SERIALIZE_STATE WOLFSSL_WC_SLHDSA_RECURSIVE WOLFSSL_WC_XMSS_NO_SHA256 -WOLFSSL_WC_XMSS_NO_SHAKE256 WOLFSSL_WICED_PSEUDO_UNIX_EPOCH_TIME WOLFSSL_X509_STORE_ALLOW_NON_CA_INTERMEDIATE WOLFSSL_X509_STORE_CERTS @@ -983,7 +963,6 @@ WOLFSSL_XFREE_NO_NULLNESS_CHECK WOLFSSL_XILINX_CRYPTO_OLD WOLFSSL_XILINX_PATCH WOLFSSL_XIL_MSG_NO_SLEEP -WOLFSSL_XMSS_LARGE_SECRET_KEY WOLFSSL_ZEPHYR WOLF_ALLOW_BUILTIN WOLF_CRYPTO_CB_CMD