diff --git a/.wolfssl_known_macro_extras b/.wolfssl_known_macro_extras index ef25bf38b2..4db52f1914 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 d2418b96b5..eef4955ee0 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 0c30c0e22b..6737d8738c 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 280776ca65..bd78b16358 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 8a59f30e86..171a22513c 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);