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 <assert.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <openssl/digest.h>
|
||||||
#include <openssl/mem.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. */
|
* case. Fix to API to avoid this. */
|
||||||
if (md != ctx->md || key != NULL) {
|
if (md != ctx->md || key != NULL) {
|
||||||
size_t i;
|
size_t i;
|
||||||
uint8_t pad[HMAC_MAX_MD_CBLOCK];
|
uint8_t pad[EVP_MAX_MD_BLOCK_SIZE];
|
||||||
uint8_t key_block[HMAC_MAX_MD_CBLOCK];
|
uint8_t key_block[EVP_MAX_MD_BLOCK_SIZE];
|
||||||
unsigned key_block_len;
|
unsigned key_block_len;
|
||||||
|
|
||||||
size_t block_size = EVP_MD_block_size(md);
|
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;
|
key_block_len = (unsigned)key_len;
|
||||||
}
|
}
|
||||||
/* Keys are then padded with zeros. */
|
/* 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);
|
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];
|
pad[i] = 0x36 ^ key_block[i];
|
||||||
}
|
}
|
||||||
if (!EVP_DigestInit_ex(&ctx->i_ctx, md, impl) ||
|
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;
|
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];
|
pad[i] = 0x5c ^ key_block[i];
|
||||||
}
|
}
|
||||||
if (!EVP_DigestInit_ex(&ctx->o_ctx, md, impl) ||
|
if (!EVP_DigestInit_ex(&ctx->o_ctx, md, impl) ||
|
||||||
|
@ -60,7 +60,6 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include <openssl/asn1.h>
|
#include <openssl/asn1.h>
|
||||||
#include <openssl/bn.h>
|
|
||||||
#include <openssl/buf.h>
|
#include <openssl/buf.h>
|
||||||
#include <openssl/bytestring.h>
|
#include <openssl/bytestring.h>
|
||||||
#include <openssl/cipher.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,
|
static int pkcs12_key_gen_raw(const uint8_t *pass_raw, size_t pass_raw_len,
|
||||||
const uint8_t *salt, size_t salt_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,
|
size_t out_len, uint8_t *out,
|
||||||
const EVP_MD *md_type) {
|
const EVP_MD *md) {
|
||||||
uint8_t *B, *D, *I, *p, *Ai;
|
/* See https://tools.ietf.org/html/rfc7292#appendix-B. Quoted parts of the
|
||||||
int Slen, Plen, Ilen, Ijlen;
|
* specification have errata applied and other typos fixed. */
|
||||||
int i, j, v;
|
|
||||||
size_t u;
|
|
||||||
int ret = 0;
|
|
||||||
BIGNUM *Ij, *Bpl1; /* These hold Ij and B + 1 */
|
|
||||||
EVP_MD_CTX ctx;
|
|
||||||
|
|
||||||
|
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);
|
EVP_MD_CTX_init(&ctx);
|
||||||
v = EVP_MD_block_size(md_type);
|
|
||||||
u = EVP_MD_size(md_type);
|
while (out_len != 0) {
|
||||||
D = OPENSSL_malloc(v);
|
/* A. Set A_i=H^r(D||I). (i.e., the r-th hash of D||I,
|
||||||
Ai = OPENSSL_malloc(u);
|
* H(H(H(... H(D||I)))) */
|
||||||
B = OPENSSL_malloc(v + 1);
|
uint8_t A[EVP_MAX_MD_SIZE];
|
||||||
Slen = v * ((salt_len + v - 1) / v);
|
unsigned A_len;
|
||||||
if (pass_raw_len) {
|
if (!EVP_DigestInit_ex(&ctx, md, NULL) ||
|
||||||
Plen = v * ((pass_raw_len + v - 1) / v);
|
!EVP_DigestUpdate(&ctx, D, block_size) ||
|
||||||
} else {
|
!EVP_DigestUpdate(&ctx, I, I_len) ||
|
||||||
Plen = 0;
|
!EVP_DigestFinal_ex(&ctx, A, &A_len)) {
|
||||||
}
|
|
||||||
Ilen = Slen + Plen;
|
|
||||||
I = OPENSSL_malloc(Ilen);
|
|
||||||
Ij = BN_new();
|
|
||||||
Bpl1 = BN_new();
|
|
||||||
if (!D || !Ai || !B || !I || !Ij || !Bpl1) {
|
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
for (i = 0; i < v; i++) {
|
int iter;
|
||||||
D[i] = id;
|
for (iter = 1; iter < iterations; iter++) {
|
||||||
}
|
if (!EVP_DigestInit_ex(&ctx, md, NULL) ||
|
||||||
p = I;
|
!EVP_DigestUpdate(&ctx, A, A_len) ||
|
||||||
for (i = 0; i < Slen; i++) {
|
!EVP_DigestFinal_ex(&ctx, A, &A_len)) {
|
||||||
*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)) {
|
|
||||||
goto err;
|
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;
|
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:
|
err:
|
||||||
OPENSSL_PUT_ERROR(PKCS8, ERR_R_MALLOC_FAILURE);
|
OPENSSL_cleanse(I, I_len);
|
||||||
|
|
||||||
end:
|
|
||||||
OPENSSL_free(Ai);
|
|
||||||
OPENSSL_free(B);
|
|
||||||
OPENSSL_free(D);
|
|
||||||
OPENSSL_free(I);
|
OPENSSL_free(I);
|
||||||
BN_free(Ij);
|
|
||||||
BN_free(Bpl1);
|
|
||||||
EVP_MD_CTX_cleanup(&ctx);
|
EVP_MD_CTX_cleanup(&ctx);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -143,6 +143,9 @@ OPENSSL_EXPORT int EVP_DigestUpdate(EVP_MD_CTX *ctx, const void *data,
|
|||||||
* at least this much space. */
|
* at least this much space. */
|
||||||
#define EVP_MAX_MD_SIZE 64 /* SHA-512 is the longest so far. */
|
#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
|
/* 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
|
* |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.
|
* 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 */
|
/* Private functions */
|
||||||
|
|
||||||
#define HMAC_MAX_MD_CBLOCK 128 /* largest known is SHA512 */
|
|
||||||
|
|
||||||
struct hmac_ctx_st {
|
struct hmac_ctx_st {
|
||||||
const EVP_MD *md;
|
const EVP_MD *md;
|
||||||
EVP_MD_CTX md_ctx;
|
EVP_MD_CTX md_ctx;
|
||||||
|
Loading…
Reference in New Issue
Block a user