Clean up PKCS5_PBKDF2_HMAC.
This was a mess. HMAC_CTX_copy_ex would avoid having to cleanup and init
the HMAC_CTX repeatedly, but even that is unnecessary. hctx_tpl was just
to reuse the key. Instead, HMAC_CTX already can be reset with the same
key. (Alas, with a slightly odd API, but so it goes.) Do that, and use
goto err to cleanup the error-handling.
Thanks to upstream's b98530d6e09f4cb34c791b8840e936c1fc1467cf for
drawing attention to this. (Though we've diverged significantly from
upstream with all the heap-allocated bits, so I didn't use the change
itself.)
While I'm here, tidy up some variable names and cite the newer RFC.
Change-Id: Ic1259f46b7c5a14dc341b8cee385be5508ac4daf
Reviewed-on: https://boringssl-review.googlesource.com/14605
Reviewed-by: Steven Valdez <svaldez@google.com>
Commit-Queue: Steven Valdez <svaldez@google.com>
CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
diff --git a/crypto/evp/pbkdf.c b/crypto/evp/pbkdf.c
index 1792cdc..daebb2d 100644
--- a/crypto/evp/pbkdf.c
+++ b/crypto/evp/pbkdf.c
@@ -65,83 +65,76 @@
int PKCS5_PBKDF2_HMAC(const char *password, size_t password_len,
const uint8_t *salt, size_t salt_len, unsigned iterations,
const EVP_MD *digest, size_t key_len, uint8_t *out_key) {
- uint8_t digest_tmp[EVP_MAX_MD_SIZE], *p, itmp[4];
- size_t cplen, mdlen, tkeylen, k;
- unsigned j;
+ /* See RFC 8018, section 5.2. */
+ int ret = 0;
+ size_t md_len = EVP_MD_size(digest);
uint32_t i = 1;
- HMAC_CTX hctx_tpl, hctx;
+ HMAC_CTX hctx;
+ HMAC_CTX_init(&hctx);
- mdlen = EVP_MD_size(digest);
- HMAC_CTX_init(&hctx_tpl);
- p = out_key;
- tkeylen = key_len;
- if (!HMAC_Init_ex(&hctx_tpl, password, password_len, digest, NULL)) {
- HMAC_CTX_cleanup(&hctx_tpl);
- return 0;
+ if (!HMAC_Init_ex(&hctx, password, password_len, digest, NULL)) {
+ goto err;
}
- while (tkeylen) {
- if (tkeylen > mdlen) {
- cplen = mdlen;
- } else {
- cplen = tkeylen;
+
+ while (key_len > 0) {
+ size_t todo = md_len;
+ if (todo > key_len) {
+ todo = key_len;
}
- /* We are unlikely to ever use more than 256 blocks (5120 bits!)
- * but just in case... */
- itmp[0] = (uint8_t)((i >> 24) & 0xff);
- itmp[1] = (uint8_t)((i >> 16) & 0xff);
- itmp[2] = (uint8_t)((i >> 8) & 0xff);
- itmp[3] = (uint8_t)(i & 0xff);
- if (!HMAC_CTX_copy(&hctx, &hctx_tpl)) {
- HMAC_CTX_cleanup(&hctx_tpl);
- return 0;
- }
- if (!HMAC_Update(&hctx, salt, salt_len) ||
- !HMAC_Update(&hctx, itmp, 4) ||
+
+ uint8_t i_buf[4];
+ i_buf[0] = (uint8_t)((i >> 24) & 0xff);
+ i_buf[1] = (uint8_t)((i >> 16) & 0xff);
+ i_buf[2] = (uint8_t)((i >> 8) & 0xff);
+ i_buf[3] = (uint8_t)(i & 0xff);
+
+ /* Compute U_1. */
+ uint8_t digest_tmp[EVP_MAX_MD_SIZE];
+ if (!HMAC_Init_ex(&hctx, NULL, 0, NULL, NULL) ||
+ !HMAC_Update(&hctx, salt, salt_len) ||
+ !HMAC_Update(&hctx, i_buf, 4) ||
!HMAC_Final(&hctx, digest_tmp, NULL)) {
- HMAC_CTX_cleanup(&hctx_tpl);
- HMAC_CTX_cleanup(&hctx);
- return 0;
+ goto err;
}
- HMAC_CTX_cleanup(&hctx);
- OPENSSL_memcpy(p, digest_tmp, cplen);
- for (j = 1; j < iterations; j++) {
- if (!HMAC_CTX_copy(&hctx, &hctx_tpl)) {
- HMAC_CTX_cleanup(&hctx_tpl);
- return 0;
- }
- if (!HMAC_Update(&hctx, digest_tmp, mdlen) ||
+
+ OPENSSL_memcpy(out_key, digest_tmp, todo);
+ for (unsigned j = 1; j < iterations; j++) {
+ /* Compute the remaining U_* values and XOR. */
+ if (!HMAC_Init_ex(&hctx, NULL, 0, NULL, NULL) ||
+ !HMAC_Update(&hctx, digest_tmp, md_len) ||
!HMAC_Final(&hctx, digest_tmp, NULL)) {
- HMAC_CTX_cleanup(&hctx_tpl);
- HMAC_CTX_cleanup(&hctx);
- return 0;
+ goto err;
}
- HMAC_CTX_cleanup(&hctx);
- for (k = 0; k < cplen; k++) {
- p[k] ^= digest_tmp[k];
+ for (size_t k = 0; k < todo; k++) {
+ out_key[k] ^= digest_tmp[k];
}
}
- tkeylen -= cplen;
+
+ key_len -= todo;
+ out_key += todo;
i++;
- 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.
+ /* RFC 8018 describes iterations (c) as being a "positive integer", so a
+ * value of 0 is an error.
+ *
+ * Unfortunately not all consumers of PKCS5_PBKDF2_HMAC() check their return
+ * value, expecting it to succeed and unconditionally 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;
+ goto err;
}
- return 1;
+ ret = 1;
+
+err:
+ HMAC_CTX_cleanup(&hctx);
+ return ret;
}
int PKCS5_PBKDF2_HMAC_SHA1(const char *password, size_t password_len,