Move PKCS#12 KDF to provider.

This KDF is defined in RFC7292 in appendix B.  It is widely used in PKCS#12
and should be provided.

Reviewed-by: Tomas Mraz <tmraz@fedoraproject.org>
(Merged from https://github.com/openssl/openssl/pull/12624)
This commit is contained in:
Pauli 2020-08-11 10:29:02 +10:00
parent c19e6da9a3
commit b7466c1303
11 changed files with 424 additions and 83 deletions

View File

@ -2874,6 +2874,7 @@ PROV_R_INVALID_CUSTOM_LENGTH:111:invalid custom length
PROV_R_INVALID_DATA:115:invalid data
PROV_R_INVALID_DIGEST:122:invalid digest
PROV_R_INVALID_DIGEST_LENGTH:166:invalid digest length
PROV_R_INVALID_DIGEST_SIZE:218:invalid digest size
PROV_R_INVALID_ITERATION_COUNT:123:invalid iteration count
PROV_R_INVALID_IVLEN:116:invalid ivlen
PROV_R_INVALID_IV_LENGTH:109:invalid iv length

View File

@ -12,11 +12,9 @@
#include <openssl/pkcs12.h>
#include <openssl/bn.h>
#include <openssl/trace.h>
/* PKCS12 compatible key/IV generation */
#ifndef min
# define min(a,b) ((a) < (b) ? (a) : (b))
#endif
#include <openssl/kdf.h>
#include <openssl/core_names.h>
#include "internal/provider.h"
int PKCS12_key_gen_asc(const char *pass, int passlen, unsigned char *salt,
int saltlen, int id, int iter, int n,
@ -68,16 +66,37 @@ int PKCS12_key_gen_uni(unsigned char *pass, int passlen, unsigned char *salt,
int saltlen, int id, int iter, int n,
unsigned char *out, const EVP_MD *md_type)
{
unsigned char *B = NULL, *D = NULL, *I = NULL, *p = NULL, *Ai = NULL;
int Slen, Plen, Ilen;
int i, j, u, v;
int ret = 0;
EVP_MD_CTX *ctx = NULL;
unsigned char *tmpout = out;
int tmpn = n;
int res = 0;
EVP_KDF *kdf;
EVP_KDF_CTX *ctx;
OSSL_PARAM params[6], *p = params;
ctx = EVP_MD_CTX_new();
if (n <= 0)
return 0;
/*
* The parameter query isn't available but the library context can be
* extracted from the passed digest.
*/
kdf = EVP_KDF_fetch(ossl_provider_library_context(EVP_MD_provider(md_type)),
"PKCS12KDF", NULL);
if (kdf == NULL)
return 0;
ctx = EVP_KDF_CTX_new(kdf);
EVP_KDF_free(kdf);
if (ctx == NULL)
return 0;
*p++ = OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_DIGEST,
(char *)EVP_MD_name(md_type), 0);
*p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_PASSWORD,
pass, passlen);
*p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_SALT,
salt, saltlen);
*p++ = OSSL_PARAM_construct_int(OSSL_KDF_PARAM_PKCS12_ID, &id);
*p++ = OSSL_PARAM_construct_int(OSSL_KDF_PARAM_ITER, &iter);
*p = OSSL_PARAM_construct_end();
if (!EVP_KDF_CTX_set_params(ctx, params))
goto err;
OSSL_TRACE_BEGIN(PKCS12_KEYGEN) {
@ -89,77 +108,16 @@ int PKCS12_key_gen_uni(unsigned char *pass, int passlen, unsigned char *salt,
BIO_hex_string(trc_out, 0, saltlen, salt, saltlen);
BIO_printf(trc_out, "\n");
} OSSL_TRACE_END(PKCS12_KEYGEN);
v = EVP_MD_block_size(md_type);
u = EVP_MD_size(md_type);
if (u < 0 || v <= 0)
goto err;
D = OPENSSL_malloc(v);
Ai = OPENSSL_malloc(u);
B = OPENSSL_malloc(v + 1);
Slen = v * ((saltlen + v - 1) / v);
if (passlen)
Plen = v * ((passlen + v - 1) / v);
else
Plen = 0;
Ilen = Slen + Plen;
I = OPENSSL_malloc(Ilen);
if (D == NULL || Ai == NULL || B == NULL || I == NULL)
goto err;
for (i = 0; i < v; i++)
D[i] = id;
p = I;
for (i = 0; i < Slen; i++)
*p++ = salt[i % saltlen];
for (i = 0; i < Plen; i++)
*p++ = pass[i % passlen];
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 < iter; j++) {
if (!EVP_DigestInit_ex(ctx, md_type, NULL)
|| !EVP_DigestUpdate(ctx, Ai, u)
|| !EVP_DigestFinal_ex(ctx, Ai, NULL))
goto err;
}
memcpy(out, Ai, min(n, u));
if (u >= n) {
OSSL_TRACE_BEGIN(PKCS12_KEYGEN) {
BIO_printf(trc_out, "Output KEY (length %d)\n", tmpn);
BIO_hex_string(trc_out, 0, tmpn, tmpout, tmpn);
BIO_printf(trc_out, "\n");
} OSSL_TRACE_END(PKCS12_KEYGEN);
ret = 1;
goto end;
}
n -= u;
out += u;
for (j = 0; j < v; j++)
B[j] = Ai[j % u];
for (j = 0; j < Ilen; j += v) {
int k;
unsigned char *Ij = I + j;
uint16_t c = 1;
/* Work out Ij = Ij + B + 1 */
for (k = v - 1; k >= 0; k--) {
c += Ij[k] + B[k];
Ij[k] = (unsigned char)c;
c >>= 8;
}
}
if (EVP_KDF_derive(ctx, out, (size_t)n)) {
res = 1;
OSSL_TRACE_BEGIN(PKCS12_KEYGEN) {
BIO_printf(trc_out, "Output KEY (length %d)\n", n);
BIO_hex_string(trc_out, 0, n, out, n);
BIO_printf(trc_out, "\n");
} OSSL_TRACE_END(PKCS12_KEYGEN);
}
err:
PKCS12err(PKCS12_F_PKCS12_KEY_GEN_UNI, ERR_R_MALLOC_FAILURE);
end:
OPENSSL_free(Ai);
OPENSSL_free(B);
OPENSSL_free(D);
OPENSSL_free(I);
EVP_MD_CTX_free(ctx);
return ret;
EVP_KDF_CTX_free(ctx);
return res;
}

View File

@ -0,0 +1,86 @@
=pod
=head1 NAME
EVP_KDF-PKCS12KDF - The PKCS#12 EVP_KDF implementation
=head1 DESCRIPTION
Support for computing the B<PKCS#12> password-based KDF through the B<EVP_KDF>
API.
The EVP_KDF-PKCS12KDF algorithm implements the PKCS#12 password-based key
derivation function, as described in appendix B of RFC 7292 (PKCS #12:
Personal Information Exchange Syntax); it derives a key from a password
using a salt, iteration count and the intended usage.
=head2 Identity
"PKCS12KDF" is the name for this implementation; it
can be used with the EVP_KDF_fetch() function.
=head2 Supported parameters
The supported parameters are:
=over 4
=item "pass" (B<OSSL_KDF_PARAM_PASSWORD>) <octet string>
=item "salt" (B<OSSL_KDF_PARAM_SALT>) <octet string>
=item "iter" (B<OSSL_KDF_PARAM_ITER>) <unsigned integer>
=item "properties" (B<OSSL_KDF_PARAM_PROPERTIES>) <UTF8 string>
=item "digest" (B<OSSL_KDF_PARAM_DIGEST>) <UTF8 string>
These parameters work as described in L<EVP_KDF(3)/PARAMETERS>.
=item "id" (B<OSSL_KDF_PARAM_PKCS12_ID>) <integer>
This parameter is used to specify the intended usage of the output bits, as per
RFC 7292 section B.3.
=back
=head1 NOTES
A typical application of this algorithm is to derive keying material for an
encryption algorithm from a password in the "pass", a salt in "salt",
and an iteration count.
Increasing the "iter" parameter slows down the algorithm which makes it
harder for an attacker to perform a brute force attack using a large number
of candidate passwords.
No assumption is made regarding the given password; it is simply treated as a
byte sequence.
=head1 CONFORMING TO
RFC7292
=head1 SEE ALSO
L<EVP_KDF(3)>,
L<EVP_KDF_CTX_new(3)>,
L<EVP_KDF_CTX_free(3)>,
L<EVP_KDF_CTX_set_params(3)>,
L<EVP_KDF_derive(3)>,
L<EVP_KDF(3)/PARAMETERS>
=head1 HISTORY
This functionality was added to OpenSSL 3.0.
=head1 COPYRIGHT
Copyright 2020 The OpenSSL Project Authors. All Rights Reserved.
Licensed under the Apache License 2.0 (the "License"). You may not use
this file except in compliance with the License. You can obtain a copy
in the file LICENSE in the source distribution or at
L<https://www.openssl.org/source/license.html>.
=cut

View File

@ -124,6 +124,8 @@ The OpenSSL default provider supports these operations and algorithms:
=item PBKDF2, see L<EVP_KDF-PBKDF2(7)>
=item PKCS12KDF, see L<EVP_KDF-PKCS12KDF(7)>
=item SSHKDF, see L<EVP_KDF-SSHKDF(7)>
=item TLS1-PRF, see L<EVP_KDF-TLS1_PRF(7)>

View File

@ -178,6 +178,7 @@ extern "C" {
#define OSSL_KDF_PARAM_SIZE "size" /* size_t */
#define OSSL_KDF_PARAM_CIPHER OSSL_ALG_PARAM_CIPHER /* utf8 string */
#define OSSL_KDF_PARAM_CONSTANT "constant" /* octet string */
#define OSSL_KDF_PARAM_PKCS12_ID "id" /* int */
/* Known KDF names */
#define OSSL_KDF_NAME_HKDF "HKDF"

View File

@ -88,6 +88,7 @@ int ERR_load_PROV_strings(void);
# define PROV_R_INVALID_DATA 115
# define PROV_R_INVALID_DIGEST 122
# define PROV_R_INVALID_DIGEST_LENGTH 166
# define PROV_R_INVALID_DIGEST_SIZE 218
# define PROV_R_INVALID_ITERATION_COUNT 123
# define PROV_R_INVALID_IVLEN 116
# define PROV_R_INVALID_IV_LENGTH 109

View File

@ -79,6 +79,8 @@ static const ERR_STRING_DATA PROV_str_reasons[] = {
{ERR_PACK(ERR_LIB_PROV, 0, PROV_R_INVALID_DIGEST), "invalid digest"},
{ERR_PACK(ERR_LIB_PROV, 0, PROV_R_INVALID_DIGEST_LENGTH),
"invalid digest length"},
{ERR_PACK(ERR_LIB_PROV, 0, PROV_R_INVALID_DIGEST_SIZE),
"invalid digest size"},
{ERR_PACK(ERR_LIB_PROV, 0, PROV_R_INVALID_ITERATION_COUNT),
"invalid iteration count"},
{ERR_PACK(ERR_LIB_PROV, 0, PROV_R_INVALID_IVLEN), "invalid ivlen"},

View File

@ -316,6 +316,7 @@ static const OSSL_ALGORITHM deflt_kdfs[] = {
{ "HKDF", "provider=default", kdf_hkdf_functions },
{ "SSKDF", "provider=default", kdf_sskdf_functions },
{ "PBKDF2", "provider=default", kdf_pbkdf2_functions },
{ "PKCS12KDF", "provider=default", kdf_pkcs12_functions },
{ "SSHKDF", "provider=default", kdf_sshkdf_functions },
{ "X963KDF", "provider=default", kdf_x963_kdf_functions },
{ "TLS1-PRF", "provider=default", kdf_tls1_prf_functions },

View File

@ -244,6 +244,7 @@ extern const OSSL_DISPATCH poly1305_functions[];
/* KDFs / PRFs */
extern const OSSL_DISPATCH kdf_pbkdf2_functions[];
extern const OSSL_DISPATCH kdf_pkcs12_functions[];
#ifndef OPENSSL_NO_SCRYPT
extern const OSSL_DISPATCH kdf_scrypt_functions[];
#endif

View File

@ -6,6 +6,7 @@ $HKDF_GOAL=../../libimplementations.a
$KBKDF_GOAL=../../libimplementations.a
$KRB5KDF_GOAL=../../libimplementations.a
$PBKDF2_GOAL=../../libimplementations.a
$PKCS12KDF_GOAL=../../libimplementations.a
$SSKDF_GOAL=../../libimplementations.a
$SCRYPT_GOAL=../../libimplementations.a
$SSHKDF_GOAL=../../libimplementations.a
@ -25,6 +26,8 @@ SOURCE[$PBKDF2_GOAL]=pbkdf2.c
SOURCE[../../libfips.a]=pbkdf2_fips.c
SOURCE[../../libnonfips.a]=pbkdf2_fips.c
SOURCE[$PKCS12KDF_GOAL]=pkcs12kdf.c
SOURCE[$SSKDF_GOAL]=sskdf.c
SOURCE[$SCRYPT_GOAL]=scrypt.c

View File

@ -0,0 +1,285 @@
/*
* Copyright 1999-2020 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the Apache License 2.0 (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
*/
#include <openssl/trace.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <openssl/evp.h>
#include <openssl/kdf.h>
#include <openssl/core_names.h>
#include "internal/cryptlib.h"
#include "internal/numbers.h"
#include "crypto/evp.h"
#include "prov/provider_ctx.h"
#include "prov/providercommonerr.h"
#include "prov/implementations.h"
#include "prov/provider_util.h"
static OSSL_FUNC_kdf_newctx_fn kdf_pkcs12_new;
static OSSL_FUNC_kdf_freectx_fn kdf_pkcs12_free;
static OSSL_FUNC_kdf_reset_fn kdf_pkcs12_reset;
static OSSL_FUNC_kdf_derive_fn kdf_pkcs12_derive;
static OSSL_FUNC_kdf_settable_ctx_params_fn kdf_pkcs12_settable_ctx_params;
static OSSL_FUNC_kdf_set_ctx_params_fn kdf_pkcs12_set_ctx_params;
static OSSL_FUNC_kdf_gettable_ctx_params_fn kdf_pkcs12_gettable_ctx_params;
static OSSL_FUNC_kdf_get_ctx_params_fn kdf_pkcs12_get_ctx_params;
typedef struct {
void *provctx;
PROV_DIGEST digest;
unsigned char *pass;
size_t pass_len;
unsigned char *salt;
size_t salt_len;
uint64_t iter;
int id;
} KDF_PKCS12;
/* PKCS12 compatible key/IV generation */
static int pkcs12kdf_derive(const unsigned char *pass, size_t passlen,
const unsigned char *salt, size_t saltlen,
int id, uint64_t iter, const EVP_MD *md_type,
unsigned char *out, size_t n)
{
unsigned char *B = NULL, *D = NULL, *I = NULL, *p = NULL, *Ai = NULL;
size_t Slen, Plen, Ilen;
size_t i, j, k, u, v;
uint64_t iter_cnt;
int ret = 0, ui, vi;
EVP_MD_CTX *ctx = NULL;
ctx = EVP_MD_CTX_new();
if (ctx == NULL) {
ERR_raise(ERR_LIB_PROV, ERR_R_MALLOC_FAILURE);
goto end;
}
vi = EVP_MD_block_size(md_type);
ui = EVP_MD_size(md_type);
if (ui < 0 || vi <= 0) {
ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_DIGEST_SIZE);
goto end;
}
u = (size_t)ui;
v = (size_t)vi;
D = OPENSSL_malloc(v);
Ai = OPENSSL_malloc(u);
B = OPENSSL_malloc(v + 1);
Slen = v * ((saltlen + v - 1) / v);
if (passlen != 0)
Plen = v * ((passlen + v - 1) / v);
else
Plen = 0;
Ilen = Slen + Plen;
I = OPENSSL_malloc(Ilen);
if (D == NULL || Ai == NULL || B == NULL || I == NULL) {
ERR_raise(ERR_LIB_PROV, ERR_R_MALLOC_FAILURE);
goto end;
}
for (i = 0; i < v; i++)
D[i] = id;
p = I;
for (i = 0; i < Slen; i++)
*p++ = salt[i % saltlen];
for (i = 0; i < Plen; i++)
*p++ = pass[i % passlen];
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 end;
for (iter_cnt = 1; iter_cnt < iter; iter_cnt++) {
if (!EVP_DigestInit_ex(ctx, md_type, NULL)
|| !EVP_DigestUpdate(ctx, Ai, u)
|| !EVP_DigestFinal_ex(ctx, Ai, NULL))
goto end;
}
memcpy(out, Ai, n < u ? n : u);
if (u >= n) {
ret = 1;
break;
}
n -= u;
out += u;
for (j = 0; j < v; j++)
B[j] = Ai[j % u];
for (j = 0; j < Ilen; j += v) {
unsigned char *Ij = I + j;
uint16_t c = 1;
/* Work out Ij = Ij + B + 1 */
for (k = v; k > 0;) {
k--;
c += Ij[k] + B[k];
Ij[k] = (unsigned char)c;
c >>= 8;
}
}
}
end:
OPENSSL_free(Ai);
OPENSSL_free(B);
OPENSSL_free(D);
OPENSSL_free(I);
EVP_MD_CTX_free(ctx);
return ret;
}
static void *kdf_pkcs12_new(void *provctx)
{
KDF_PKCS12 *ctx;
ctx = OPENSSL_zalloc(sizeof(*ctx));
if (ctx == NULL) {
ERR_raise(ERR_LIB_PROV, ERR_R_MALLOC_FAILURE);
return NULL;
}
ctx->provctx = provctx;
return ctx;
}
static void kdf_pkcs12_cleanup(KDF_PKCS12 *ctx)
{
ossl_prov_digest_reset(&ctx->digest);
OPENSSL_free(ctx->salt);
OPENSSL_clear_free(ctx->pass, ctx->pass_len);
memset(ctx, 0, sizeof(*ctx));
}
static void kdf_pkcs12_free(void *vctx)
{
KDF_PKCS12 *ctx = (KDF_PKCS12 *)vctx;
if (ctx != NULL) {
kdf_pkcs12_cleanup(ctx);
OPENSSL_free(ctx);
}
}
static void kdf_pkcs12_reset(void *vctx)
{
KDF_PKCS12 *ctx = (KDF_PKCS12 *)vctx;
void *provctx = ctx->provctx;
kdf_pkcs12_cleanup(ctx);
ctx->provctx = provctx;
}
static int pkcs12kdf_set_membuf(unsigned char **buffer, size_t *buflen,
const OSSL_PARAM *p)
{
OPENSSL_clear_free(*buffer, *buflen);
if (p->data_size == 0) {
if ((*buffer = OPENSSL_malloc(1)) == NULL) {
ERR_raise(ERR_LIB_PROV, ERR_R_MALLOC_FAILURE);
return 0;
}
} else if (p->data != NULL) {
*buffer = NULL;
if (!OSSL_PARAM_get_octet_string(p, (void **)buffer, 0, buflen))
return 0;
}
return 1;
}
static int kdf_pkcs12_derive(void *vctx, unsigned char *key,
size_t keylen)
{
KDF_PKCS12 *ctx = (KDF_PKCS12 *)vctx;
const EVP_MD *md = ossl_prov_digest_md(&ctx->digest);
if (ctx->pass == NULL) {
ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_PASS);
return 0;
}
if (ctx->salt == NULL) {
ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_SALT);
return 0;
}
return pkcs12kdf_derive(ctx->pass, ctx->pass_len, ctx->salt, ctx->salt_len,
ctx->id, ctx->iter, md, key, keylen);
}
static int kdf_pkcs12_set_ctx_params(void *vctx, const OSSL_PARAM params[])
{
const OSSL_PARAM *p;
KDF_PKCS12 *ctx = vctx;
OPENSSL_CTX *provctx = PROV_LIBRARY_CONTEXT_OF(ctx->provctx);
if (!ossl_prov_digest_load_from_params(&ctx->digest, params, provctx))
return 0;
if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_PASSWORD)) != NULL)
if (!pkcs12kdf_set_membuf(&ctx->pass, &ctx->pass_len, p))
return 0;
if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_SALT)) != NULL)
if (!pkcs12kdf_set_membuf(&ctx->salt, &ctx->salt_len,p))
return 0;
if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_PKCS12_ID)) != NULL)
if (!OSSL_PARAM_get_int(p, &ctx->id))
return 0;
if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_ITER)) != NULL)
if (!OSSL_PARAM_get_uint64(p, &ctx->iter))
return 0;
return 1;
}
static const OSSL_PARAM *kdf_pkcs12_settable_ctx_params(void *provctx)
{
static const OSSL_PARAM known_settable_ctx_params[] = {
OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_PROPERTIES, NULL, 0),
OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_DIGEST, NULL, 0),
OSSL_PARAM_octet_string(OSSL_KDF_PARAM_PASSWORD, NULL, 0),
OSSL_PARAM_octet_string(OSSL_KDF_PARAM_SALT, NULL, 0),
OSSL_PARAM_uint64(OSSL_KDF_PARAM_ITER, NULL),
OSSL_PARAM_int(OSSL_KDF_PARAM_PKCS12_ID, NULL),
OSSL_PARAM_END
};
return known_settable_ctx_params;
}
static int kdf_pkcs12_get_ctx_params(void *vctx, OSSL_PARAM params[])
{
OSSL_PARAM *p;
if ((p = OSSL_PARAM_locate(params, OSSL_KDF_PARAM_SIZE)) != NULL)
return OSSL_PARAM_set_size_t(p, SIZE_MAX);
return -2;
}
static const OSSL_PARAM *kdf_pkcs12_gettable_ctx_params(void *provctx)
{
static const OSSL_PARAM known_gettable_ctx_params[] = {
OSSL_PARAM_size_t(OSSL_KDF_PARAM_SIZE, NULL),
OSSL_PARAM_END
};
return known_gettable_ctx_params;
}
const OSSL_DISPATCH kdf_pkcs12_functions[] = {
{ OSSL_FUNC_KDF_NEWCTX, (void(*)(void))kdf_pkcs12_new },
{ OSSL_FUNC_KDF_FREECTX, (void(*)(void))kdf_pkcs12_free },
{ OSSL_FUNC_KDF_RESET, (void(*)(void))kdf_pkcs12_reset },
{ OSSL_FUNC_KDF_DERIVE, (void(*)(void))kdf_pkcs12_derive },
{ OSSL_FUNC_KDF_SETTABLE_CTX_PARAMS,
(void(*)(void))kdf_pkcs12_settable_ctx_params },
{ OSSL_FUNC_KDF_SET_CTX_PARAMS, (void(*)(void))kdf_pkcs12_set_ctx_params },
{ OSSL_FUNC_KDF_GETTABLE_CTX_PARAMS,
(void(*)(void))kdf_pkcs12_gettable_ctx_params },
{ OSSL_FUNC_KDF_GET_CTX_PARAMS, (void(*)(void))kdf_pkcs12_get_ctx_params },
{ 0, NULL }
};