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().

This commit is contained in:
Daniel Pouzzner
2026-05-01 13:29:16 -05:00
parent c4be7f3f59
commit f248b272db
5 changed files with 186 additions and 16 deletions
-1
View File
@@ -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
+50 -3
View File
@@ -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);
+24 -8
View File
@@ -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;
}
+97
View File
@@ -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)
+15 -4
View File
@@ -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);