Reimplement PKCS#12 key derivation.
This is avoids pulling in BIGNUM for doing a straight-forward addition on a block-sized value, and avoids a ton of mallocs. It's also -Wconversion-clean, unlike the old one. In doing so, this replaces the HMAC_MAX_MD_CBLOCK with EVP_MAX_MD_BLOCK_SIZE. By having the maximum block size available, most of the temporary values in the key derivation don't need to be malloc'd. BUG=22 Change-Id: I940a62bba4ea32bf82b1190098f3bf185d4cc7fe Reviewed-on: https://boringssl-review.googlesource.com/7688 Reviewed-by: Steven Valdez <svaldez@google.com> Reviewed-by: David Benjamin <davidben@google.com>
This commit is contained in:
parent
0e21f41fe8
commit
582d2847ed
@ -59,6 +59,7 @@
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <openssl/digest.h>
|
||||
#include <openssl/mem.h>
|
||||
|
||||
|
||||
@ -115,8 +116,8 @@ int HMAC_Init_ex(HMAC_CTX *ctx, const void *key, size_t key_len,
|
||||
* case. Fix to API to avoid this. */
|
||||
if (md != ctx->md || key != NULL) {
|
||||
size_t i;
|
||||
uint8_t pad[HMAC_MAX_MD_CBLOCK];
|
||||
uint8_t key_block[HMAC_MAX_MD_CBLOCK];
|
||||
uint8_t pad[EVP_MAX_MD_BLOCK_SIZE];
|
||||
uint8_t key_block[EVP_MAX_MD_BLOCK_SIZE];
|
||||
unsigned key_block_len;
|
||||
|
||||
size_t block_size = EVP_MD_block_size(md);
|
||||
@ -134,11 +135,11 @@ int HMAC_Init_ex(HMAC_CTX *ctx, const void *key, size_t key_len,
|
||||
key_block_len = (unsigned)key_len;
|
||||
}
|
||||
/* Keys are then padded with zeros. */
|
||||
if (key_block_len != HMAC_MAX_MD_CBLOCK) {
|
||||
if (key_block_len != EVP_MAX_MD_BLOCK_SIZE) {
|
||||
memset(&key_block[key_block_len], 0, sizeof(key_block) - key_block_len);
|
||||
}
|
||||
|
||||
for (i = 0; i < HMAC_MAX_MD_CBLOCK; i++) {
|
||||
for (i = 0; i < EVP_MAX_MD_BLOCK_SIZE; i++) {
|
||||
pad[i] = 0x36 ^ key_block[i];
|
||||
}
|
||||
if (!EVP_DigestInit_ex(&ctx->i_ctx, md, impl) ||
|
||||
@ -146,7 +147,7 @@ int HMAC_Init_ex(HMAC_CTX *ctx, const void *key, size_t key_len,
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < HMAC_MAX_MD_CBLOCK; i++) {
|
||||
for (i = 0; i < EVP_MAX_MD_BLOCK_SIZE; i++) {
|
||||
pad[i] = 0x5c ^ key_block[i];
|
||||
}
|
||||
if (!EVP_DigestInit_ex(&ctx->o_ctx, md, impl) ||
|
||||
|
@ -60,7 +60,6 @@
|
||||
#include <string.h>
|
||||
|
||||
#include <openssl/asn1.h>
|
||||
#include <openssl/bn.h>
|
||||
#include <openssl/buf.h>
|
||||
#include <openssl/bytestring.h>
|
||||
#include <openssl/cipher.h>
|
||||
@ -107,112 +106,122 @@ static int ascii_to_ucs2(const char *ascii, size_t ascii_len,
|
||||
|
||||
static int pkcs12_key_gen_raw(const uint8_t *pass_raw, size_t pass_raw_len,
|
||||
const uint8_t *salt, size_t salt_len,
|
||||
int id, int iterations,
|
||||
uint8_t id, int iterations,
|
||||
size_t out_len, uint8_t *out,
|
||||
const EVP_MD *md_type) {
|
||||
uint8_t *B, *D, *I, *p, *Ai;
|
||||
int Slen, Plen, Ilen, Ijlen;
|
||||
int i, j, v;
|
||||
size_t u;
|
||||
int ret = 0;
|
||||
BIGNUM *Ij, *Bpl1; /* These hold Ij and B + 1 */
|
||||
EVP_MD_CTX ctx;
|
||||
const EVP_MD *md) {
|
||||
/* See https://tools.ietf.org/html/rfc7292#appendix-B. Quoted parts of the
|
||||
* specification have errata applied and other typos fixed. */
|
||||
|
||||
if (iterations < 1) {
|
||||
OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_BAD_ITERATION_COUNT);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* In the spec, |block_size| is called "v", but measured in bits. */
|
||||
size_t block_size = EVP_MD_block_size(md);
|
||||
|
||||
/* 1. Construct a string, D (the "diversifier"), by concatenating v/8 copies
|
||||
* of ID. */
|
||||
uint8_t D[EVP_MAX_MD_BLOCK_SIZE];
|
||||
memset(D, id, block_size);
|
||||
|
||||
/* 2. Concatenate copies of the salt together to create a string S of length
|
||||
* v(ceiling(s/v)) bits (the final copy of the salt may be truncated to
|
||||
* create S). Note that if the salt is the empty string, then so is S.
|
||||
*
|
||||
* 3. Concatenate copies of the password together to create a string P of
|
||||
* length v(ceiling(p/v)) bits (the final copy of the password may be
|
||||
* truncated to create P). Note that if the password is the empty string,
|
||||
* then so is P.
|
||||
*
|
||||
* 4. Set I=S||P to be the concatenation of S and P. */
|
||||
if (salt_len + block_size - 1 < salt_len ||
|
||||
pass_raw_len + block_size - 1 < pass_raw_len) {
|
||||
OPENSSL_PUT_ERROR(PKCS8, ERR_R_OVERFLOW);
|
||||
return 0;
|
||||
}
|
||||
size_t S_len = block_size * ((salt_len + block_size - 1) / block_size);
|
||||
size_t P_len = block_size * ((pass_raw_len + block_size - 1) / block_size);
|
||||
size_t I_len = S_len + P_len;
|
||||
if (I_len < S_len) {
|
||||
OPENSSL_PUT_ERROR(PKCS8, ERR_R_OVERFLOW);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t *I = OPENSSL_malloc(I_len);
|
||||
if (I_len != 0 && I == NULL) {
|
||||
OPENSSL_PUT_ERROR(PKCS8, ERR_R_MALLOC_FAILURE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t i;
|
||||
for (i = 0; i < S_len; i++) {
|
||||
I[i] = salt[i % salt_len];
|
||||
}
|
||||
for (i = 0; i < P_len; i++) {
|
||||
I[i + S_len] = pass_raw[i % pass_raw_len];
|
||||
}
|
||||
|
||||
int ret = 0;
|
||||
EVP_MD_CTX ctx;
|
||||
EVP_MD_CTX_init(&ctx);
|
||||
v = EVP_MD_block_size(md_type);
|
||||
u = EVP_MD_size(md_type);
|
||||
D = OPENSSL_malloc(v);
|
||||
Ai = OPENSSL_malloc(u);
|
||||
B = OPENSSL_malloc(v + 1);
|
||||
Slen = v * ((salt_len + v - 1) / v);
|
||||
if (pass_raw_len) {
|
||||
Plen = v * ((pass_raw_len + v - 1) / v);
|
||||
} else {
|
||||
Plen = 0;
|
||||
}
|
||||
Ilen = Slen + Plen;
|
||||
I = OPENSSL_malloc(Ilen);
|
||||
Ij = BN_new();
|
||||
Bpl1 = BN_new();
|
||||
if (!D || !Ai || !B || !I || !Ij || !Bpl1) {
|
||||
|
||||
while (out_len != 0) {
|
||||
/* A. Set A_i=H^r(D||I). (i.e., the r-th hash of D||I,
|
||||
* H(H(H(... H(D||I)))) */
|
||||
uint8_t A[EVP_MAX_MD_SIZE];
|
||||
unsigned A_len;
|
||||
if (!EVP_DigestInit_ex(&ctx, md, NULL) ||
|
||||
!EVP_DigestUpdate(&ctx, D, block_size) ||
|
||||
!EVP_DigestUpdate(&ctx, I, I_len) ||
|
||||
!EVP_DigestFinal_ex(&ctx, A, &A_len)) {
|
||||
goto err;
|
||||
}
|
||||
for (i = 0; i < v; i++) {
|
||||
D[i] = id;
|
||||
}
|
||||
p = I;
|
||||
for (i = 0; i < Slen; i++) {
|
||||
*p++ = salt[i % salt_len];
|
||||
}
|
||||
for (i = 0; i < Plen; i++) {
|
||||
*p++ = pass_raw[i % pass_raw_len];
|
||||
}
|
||||
for (;;) {
|
||||
if (!EVP_DigestInit_ex(&ctx, md_type, NULL) ||
|
||||
!EVP_DigestUpdate(&ctx, D, v) ||
|
||||
!EVP_DigestUpdate(&ctx, I, Ilen) ||
|
||||
!EVP_DigestFinal_ex(&ctx, Ai, NULL)) {
|
||||
goto err;
|
||||
}
|
||||
for (j = 1; j < iterations; j++) {
|
||||
if (!EVP_DigestInit_ex(&ctx, md_type, NULL) ||
|
||||
!EVP_DigestUpdate(&ctx, Ai, u) ||
|
||||
!EVP_DigestFinal_ex(&ctx, Ai, NULL)) {
|
||||
int iter;
|
||||
for (iter = 1; iter < iterations; iter++) {
|
||||
if (!EVP_DigestInit_ex(&ctx, md, NULL) ||
|
||||
!EVP_DigestUpdate(&ctx, A, A_len) ||
|
||||
!EVP_DigestFinal_ex(&ctx, A, &A_len)) {
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
memcpy(out, Ai, out_len < u ? out_len : u);
|
||||
if (u >= out_len) {
|
||||
|
||||
size_t todo = out_len < A_len ? out_len : A_len;
|
||||
memcpy(out, A, todo);
|
||||
out += todo;
|
||||
out_len -= todo;
|
||||
if (out_len == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* B. Concatenate copies of A_i to create a string B of length v bits (the
|
||||
* final copy of A_i may be truncated to create B). */
|
||||
uint8_t B[EVP_MAX_MD_BLOCK_SIZE];
|
||||
for (i = 0; i < block_size; i++) {
|
||||
B[i] = A[i % A_len];
|
||||
}
|
||||
|
||||
/* C. Treating I as a concatenation I_0, I_1, ..., I_(k-1) of v-bit blocks,
|
||||
* where k=ceiling(s/v)+ceiling(p/v), modify I by setting I_j=(I_j+B+1) mod
|
||||
* 2^v for each j. */
|
||||
assert(I_len % block_size == 0);
|
||||
for (i = 0; i < I_len; i += block_size) {
|
||||
unsigned carry = 1;
|
||||
size_t j;
|
||||
for (j = block_size - 1; j < block_size; j--) {
|
||||
carry += I[i + j] + B[j];
|
||||
I[i + j] = (uint8_t)carry;
|
||||
carry >>= 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ret = 1;
|
||||
goto end;
|
||||
}
|
||||
out_len -= u;
|
||||
out += u;
|
||||
for (j = 0; j < v; j++) {
|
||||
B[j] = Ai[j % u];
|
||||
}
|
||||
/* Work out B + 1 first then can use B as tmp space */
|
||||
if (!BN_bin2bn(B, v, Bpl1) ||
|
||||
!BN_add_word(Bpl1, 1)) {
|
||||
goto err;
|
||||
}
|
||||
for (j = 0; j < Ilen; j += v) {
|
||||
if (!BN_bin2bn(I + j, v, Ij) ||
|
||||
!BN_add(Ij, Ij, Bpl1) ||
|
||||
!BN_bn2bin(Ij, B)) {
|
||||
goto err;
|
||||
}
|
||||
Ijlen = BN_num_bytes(Ij);
|
||||
/* If more than 2^(v*8) - 1 cut off MSB */
|
||||
if (Ijlen > v) {
|
||||
if (!BN_bn2bin(Ij, B)) {
|
||||
goto err;
|
||||
}
|
||||
memcpy(I + j, B + 1, v);
|
||||
/* If less than v bytes pad with zeroes */
|
||||
} else if (Ijlen < v) {
|
||||
memset(I + j, 0, v - Ijlen);
|
||||
if (!BN_bn2bin(Ij, I + j + v - Ijlen)) {
|
||||
goto err;
|
||||
}
|
||||
} else if (!BN_bn2bin(Ij, I + j)) {
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
err:
|
||||
OPENSSL_PUT_ERROR(PKCS8, ERR_R_MALLOC_FAILURE);
|
||||
|
||||
end:
|
||||
OPENSSL_free(Ai);
|
||||
OPENSSL_free(B);
|
||||
OPENSSL_free(D);
|
||||
OPENSSL_cleanse(I, I_len);
|
||||
OPENSSL_free(I);
|
||||
BN_free(Ij);
|
||||
BN_free(Bpl1);
|
||||
EVP_MD_CTX_cleanup(&ctx);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -143,6 +143,9 @@ OPENSSL_EXPORT int EVP_DigestUpdate(EVP_MD_CTX *ctx, const void *data,
|
||||
* at least this much space. */
|
||||
#define EVP_MAX_MD_SIZE 64 /* SHA-512 is the longest so far. */
|
||||
|
||||
/* EVP_MAX_MD_BLOCK_SIZE is the largest digest block size supported, in bytes. */
|
||||
#define EVP_MAX_MD_BLOCK_SIZE 128 /* SHA-512 is the longest so far. */
|
||||
|
||||
/* EVP_DigestFinal_ex finishes the digest in |ctx| and writes the output to
|
||||
* |md_out|. At most |EVP_MAX_MD_SIZE| bytes are written. If |out_size| is not
|
||||
* NULL then |*out_size| is set to the number of bytes written. It returns one.
|
||||
|
@ -143,8 +143,6 @@ OPENSSL_EXPORT int HMAC_CTX_copy(HMAC_CTX *dest, const HMAC_CTX *src);
|
||||
|
||||
/* Private functions */
|
||||
|
||||
#define HMAC_MAX_MD_CBLOCK 128 /* largest known is SHA512 */
|
||||
|
||||
struct hmac_ctx_st {
|
||||
const EVP_MD *md;
|
||||
EVP_MD_CTX md_ctx;
|
||||
|
Loading…
Reference in New Issue
Block a user