Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 53 additions & 3 deletions doc/dox_comments/header_files/pwdbased.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
current_wc_pbkdf_max_iterations
\return MEMORY_E on memory allocation error

\param key Output key buffer
Expand All @@ -199,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,
Expand All @@ -209,7 +212,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
current_wc_pbkdf_max_iterations
\return MEMORY_E on memory allocation error

\param output Output key buffer
Expand All @@ -234,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,
Expand All @@ -244,7 +250,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
current_wc_pbkdf_max_iterations
\return MEMORY_E on memory allocation error

\param output Output key buffer
Expand All @@ -268,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,
Expand Down Expand Up @@ -338,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);
38 changes: 38 additions & 0 deletions wolfcrypt/src/pwdbased.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) */
Expand All @@ -79,6 +95,11 @@ int wc_PBKDF1_ex(byte* key, int keyLen, byte* iv, int ivLen,
if (iterations <= 0)
iterations = 1;

if (iterations > current_wc_pbkdf_max_iterations) {
WOLFSSL_MSG("PBKDF1 iteration count exceeds current_wc_pbkdf_max_iterations");
return BAD_FUNC_ARG;
}

hashT = wc_HashTypeConvert(hashType);
err = wc_HashGetDigestSize(hashT);
if (err < 0)
Expand Down Expand Up @@ -215,6 +236,11 @@ int wc_PBKDF2_ex(byte* output, const byte* passwd, int pLen, const byte* salt,
if (iterations <= 0)
iterations = 1;

if (iterations > current_wc_pbkdf_max_iterations) {
WOLFSSL_MSG("PBKDF2 iteration count exceeds current_wc_pbkdf_max_iterations");
return BAD_FUNC_ARG;
}

hashT = wc_HashTypeConvert(hashType);
hLen = wc_HashGetDigestSize(hashT);
if (hLen < 0)
Expand Down Expand Up @@ -403,6 +429,12 @@ int wc_PKCS12_PBKDF_ex(byte* output, const byte* passwd, int passLen,
if (iterations <= 0)
iterations = 1;

if (iterations > current_wc_pbkdf_max_iterations) {
WOLFSSL_MSG("PKCS12 PBKDF iteration count exceeds "
"current_wc_pbkdf_max_iterations");
return BAD_FUNC_ARG;
}

hashT = wc_HashTypeConvert(hashType);
ret = wc_HashGetDigestSize(hashT);
if (ret < 0)
Comment thread
anhu marked this conversation as resolved.
Expand Down Expand Up @@ -611,6 +643,12 @@ int wc_PKCS12_PBKDF_ex(byte* output, const byte* passwd, int passLen,
iterations = 1;
}

if (iterations > current_wc_pbkdf_max_iterations) {
WOLFSSL_MSG("PKCS12 PBKDF iteration count exceeds "
"current_wc_pbkdf_max_iterations");
return BAD_FUNC_ARG;
}

/* u = hash output size. */
hashT = wc_HashTypeConvert(hashType);
ret = wc_HashGetDigestSize(hashT);
Expand Down
124 changes: 123 additions & 1 deletion wolfcrypt/test/test.c
Original file line number Diff line number Diff line change
Expand Up @@ -729,12 +729,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) && \
Expand Down Expand Up @@ -30789,7 +30795,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 */
Expand Down Expand Up @@ -30844,6 +30874,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)
Expand Down Expand Up @@ -30943,6 +31020,51 @@ 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) {
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)
Expand Down
6 changes: 6 additions & 0 deletions wolfcrypt/test/test.h
Original file line number Diff line number Diff line change
Expand Up @@ -262,12 +262,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) && \
Expand Down
20 changes: 20 additions & 0 deletions wolfssl/wolfcrypt/pwdbased.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,26 @@
extern "C" {
#endif

/* Maximum allowed PBKDF iteration count to prevent CPU exhaustion DoS.
Comment thread
dgarske marked this conversation as resolved.
* 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_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);
Expand Down
Loading