Reject iterations=0 when calling PKCS5_PBKDF2_HMAC(). BUG=https://crbug.com/534961 Change-Id: I69e2434bf8d5564711863c393ee3bafe3763cf24 Reviewed-on: https://boringssl-review.googlesource.com/5932 Reviewed-by: Adam Langley <agl@google.com>
diff --git a/crypto/evp/pbkdf.c b/crypto/evp/pbkdf.c index be6ed86..b06b922 100644 --- a/crypto/evp/pbkdf.c +++ b/crypto/evp/pbkdf.c
@@ -123,6 +123,22 @@ p += cplen; } HMAC_CTX_cleanup(&hctx_tpl); + + // RFC 2898 describes iterations (c) as being a "positive integer", so a + // value of 0 is an error. + // + // Unfortunatley not all consumers of PKCS5_PBKDF2_HMAC() check their return + // value, expecting it to succeed and unconditonally using |out_key|. + // As a precaution for such callsites in external code, the old behavior + // of iterations < 1 being treated as iterations == 1 is preserved, but + // additionally an error result is returned. + // + // TODO(eroman): Figure out how to remove this compatibility hack, or change + // the default to something more sensible like 2048. + if (iterations == 0) { + return 0; + } + return 1; }
diff --git a/crypto/evp/pbkdf_test.cc b/crypto/evp/pbkdf_test.cc index ae2f405..a39189f 100644 --- a/crypto/evp/pbkdf_test.cc +++ b/crypto/evp/pbkdf_test.cc
@@ -149,6 +149,44 @@ return true; } +// Tests key derivation using iterations=0. +// +// RFC 2898 defines the iteration count (c) as a "positive integer". So doing a +// key derivation with iterations=0 is ill-defined and should result in a +// failure. +static bool TestZeroIterations() { + static const char kPassword[] = "password"; + const size_t password_len = strlen(kPassword); + static const uint8_t kSalt[] = {1, 2, 3, 4}; + const size_t salt_len = sizeof(kSalt); + const EVP_MD *digest = EVP_sha1(); + + uint8_t key[10] = {0}; + const size_t key_len = sizeof(key); + + // Verify that calling with iterations=1 works. + if (!PKCS5_PBKDF2_HMAC(kPassword, password_len, kSalt, salt_len, + 1 /* iterations */, digest, key_len, key)) { + fprintf(stderr, "PBKDF2 failed with iterations=1\n"); + return false; + } + + // Flip the first key byte (so can later test if it got set). + const uint8_t expected_first_byte = key[0]; + key[0] = ~key[0]; + + // However calling it with iterations=0 fails. + if (PKCS5_PBKDF2_HMAC(kPassword, password_len, kSalt, salt_len, + 0 /* iterations */, digest, key_len, key)) { + fprintf(stderr, "PBKDF2 returned zero with iterations=0\n"); + return false; + } + + // For backwards compatibility, the iterations == 0 case still fills in + // the out key. + return key[0] == expected_first_byte; +} + int main(void) { CRYPTO_library_init(); ERR_load_crypto_strings(); @@ -173,6 +211,11 @@ return 1; } + if (!TestZeroIterations()) { + fprintf(stderr, "TestZeroIterations failed\n"); + return 1; + } + printf("PASS\n"); ERR_free_strings(); return 0;