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;