diff options
author | Richard Levitte <levitte@openssl.org> | 2019-10-04 10:24:09 +0200 |
---|---|---|
committer | Richard Levitte <levitte@openssl.org> | 2019-10-10 14:12:15 +0200 |
commit | 5687e357c60b31dc274c6d14f1cd623d0cff469b (patch) | |
tree | 6cac9157790e44435359726e71aa033fb18d8701 /providers/implementations/kdfs | |
parent | dec95d75897125133380c7ce3c6ce58c93c06f10 (diff) |
Providers: move common exchange,kdfs,keymgmt,macs,signature
From providers/common/ to providers/implementations/
Reviewed-by: Matt Caswell <matt@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/10088)
Diffstat (limited to 'providers/implementations/kdfs')
-rw-r--r-- | providers/implementations/kdfs/build.info | 22 | ||||
-rw-r--r-- | providers/implementations/kdfs/hkdf.c | 463 | ||||
-rw-r--r-- | providers/implementations/kdfs/kbkdf.c | 303 | ||||
-rw-r--r-- | providers/implementations/kdfs/pbkdf2.c | 336 | ||||
-rw-r--r-- | providers/implementations/kdfs/pbkdf2.h | 14 | ||||
-rw-r--r-- | providers/implementations/kdfs/pbkdf2_fips.c | 20 | ||||
-rw-r--r-- | providers/implementations/kdfs/sskdf.c | 538 | ||||
-rw-r--r-- | providers/implementations/kdfs/tls1_prf.c | 396 |
8 files changed, 2092 insertions, 0 deletions
diff --git a/providers/implementations/kdfs/build.info b/providers/implementations/kdfs/build.info new file mode 100644 index 0000000000..8800b12f7e --- /dev/null +++ b/providers/implementations/kdfs/build.info @@ -0,0 +1,22 @@ +# We make separate GOAL variables for each algorithm, to make it easy to +# switch each to the Legacy provider when needed. + +$TLS1_PRF_GOAL=../../libimplementations.a +$HKDF_GOAL=../../libimplementations.a +$KBKDF_GOAL=../../libimplementations.a +$PBKDF2_GOAL=../../libimplementations.a +$SSKDF_GOAL=../../libimplementations.a + +SOURCE[$TLS1_PRF_GOAL]=tls1_prf.c + +SOURCE[$HKDF_GOAL]=hkdf.c + +SOURCE[$KBKDF_GOAL]=kbkdf.c + +SOURCE[$PBKDF2_GOAL]=pbkdf2.c +# Extra code to satisfy the FIPS and non-FIPS separation. +# When the PBKDF2 moves to legacy, this can be removed. +SOURCE[../../libfips.a]=pbkdf2_fips.c +SOURCE[../../libnonfips.a]=pbkdf2_fips.c + +SOURCE[$SSKDF_GOAL]=sskdf.c diff --git a/providers/implementations/kdfs/hkdf.c b/providers/implementations/kdfs/hkdf.c new file mode 100644 index 0000000000..041811f0e1 --- /dev/null +++ b/providers/implementations/kdfs/hkdf.c @@ -0,0 +1,463 @@ +/* + * Copyright 2016-2019 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 <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <openssl/hmac.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 "internal/provider_ctx.h" +#include "internal/providercommonerr.h" +#include "internal/provider_algs.h" +#include "internal/provider_util.h" +#include "e_os.h" + +#define HKDF_MAXBUF 1024 + +static OSSL_OP_kdf_newctx_fn kdf_hkdf_new; +static OSSL_OP_kdf_freectx_fn kdf_hkdf_free; +static OSSL_OP_kdf_reset_fn kdf_hkdf_reset; +static OSSL_OP_kdf_derive_fn kdf_hkdf_derive; +static OSSL_OP_kdf_settable_ctx_params_fn kdf_hkdf_settable_ctx_params; +static OSSL_OP_kdf_set_ctx_params_fn kdf_hkdf_set_ctx_params; +static OSSL_OP_kdf_gettable_ctx_params_fn kdf_hkdf_gettable_ctx_params; +static OSSL_OP_kdf_get_ctx_params_fn kdf_hkdf_get_ctx_params; + +static int HKDF(const EVP_MD *evp_md, + const unsigned char *salt, size_t salt_len, + const unsigned char *key, size_t key_len, + const unsigned char *info, size_t info_len, + unsigned char *okm, size_t okm_len); +static int HKDF_Extract(const EVP_MD *evp_md, + const unsigned char *salt, size_t salt_len, + const unsigned char *ikm, size_t ikm_len, + unsigned char *prk, size_t prk_len); +static int HKDF_Expand(const EVP_MD *evp_md, + const unsigned char *prk, size_t prk_len, + const unsigned char *info, size_t info_len, + unsigned char *okm, size_t okm_len); + +typedef struct { + void *provctx; + int mode; + PROV_DIGEST digest; + unsigned char *salt; + size_t salt_len; + unsigned char *key; + size_t key_len; + unsigned char info[HKDF_MAXBUF]; + size_t info_len; +} KDF_HKDF; + +static void *kdf_hkdf_new(void *provctx) +{ + KDF_HKDF *ctx; + + if ((ctx = OPENSSL_zalloc(sizeof(*ctx))) == NULL) + ERR_raise(ERR_LIB_PROV, ERR_R_MALLOC_FAILURE); + else + ctx->provctx = provctx; + return ctx; +} + +static void kdf_hkdf_free(void *vctx) +{ + KDF_HKDF *ctx = (KDF_HKDF *)vctx; + + kdf_hkdf_reset(ctx); + OPENSSL_free(ctx); +} + +static void kdf_hkdf_reset(void *vctx) +{ + KDF_HKDF *ctx = (KDF_HKDF *)vctx; + + ossl_prov_digest_reset(&ctx->digest); + OPENSSL_free(ctx->salt); + OPENSSL_clear_free(ctx->key, ctx->key_len); + OPENSSL_cleanse(ctx->info, ctx->info_len); + memset(ctx, 0, sizeof(*ctx)); +} + +static size_t kdf_hkdf_size(KDF_HKDF *ctx) +{ + int sz; + const EVP_MD *md = ossl_prov_digest_md(&ctx->digest); + + if (ctx->mode != EVP_KDF_HKDF_MODE_EXTRACT_ONLY) + return SIZE_MAX; + + if (md == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_MESSAGE_DIGEST); + return 0; + } + sz = EVP_MD_size(md); + if (sz < 0) + return 0; + + return sz; +} + +static int kdf_hkdf_derive(void *vctx, unsigned char *key, size_t keylen) +{ + KDF_HKDF *ctx = (KDF_HKDF *)vctx; + const EVP_MD *md = ossl_prov_digest_md(&ctx->digest); + + if (md == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_MESSAGE_DIGEST); + return 0; + } + if (ctx->key == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY); + return 0; + } + + switch (ctx->mode) { + case EVP_KDF_HKDF_MODE_EXTRACT_AND_EXPAND: + return HKDF(md, ctx->salt, ctx->salt_len, ctx->key, + ctx->key_len, ctx->info, ctx->info_len, key, + keylen); + + case EVP_KDF_HKDF_MODE_EXTRACT_ONLY: + return HKDF_Extract(md, ctx->salt, ctx->salt_len, ctx->key, + ctx->key_len, key, keylen); + + case EVP_KDF_HKDF_MODE_EXPAND_ONLY: + return HKDF_Expand(md, ctx->key, ctx->key_len, ctx->info, + ctx->info_len, key, keylen); + + default: + return 0; + } +} + +static int kdf_hkdf_set_ctx_params(void *vctx, const OSSL_PARAM params[]) +{ + const OSSL_PARAM *p; + KDF_HKDF *ctx = vctx; + OPENSSL_CTX *provctx = PROV_LIBRARY_CONTEXT_OF(ctx->provctx); + int n; + + if (!ossl_prov_digest_load_from_params(&ctx->digest, params, provctx)) + return 0; + + if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_MODE)) != NULL) { + if (p->data_type == OSSL_PARAM_UTF8_STRING) { + if (strcasecmp(p->data, "EXTRACT_AND_EXPAND") == 0) { + ctx->mode = EVP_KDF_HKDF_MODE_EXTRACT_AND_EXPAND; + } else if (strcasecmp(p->data, "EXTRACT_ONLY") == 0) { + ctx->mode = EVP_KDF_HKDF_MODE_EXTRACT_ONLY; + } else if (strcasecmp(p->data, "EXPAND_ONLY") == 0) { + ctx->mode = EVP_KDF_HKDF_MODE_EXPAND_ONLY; + } else { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_MODE); + return 0; + } + } else if (OSSL_PARAM_get_int(p, &n)) { + if (n != EVP_KDF_HKDF_MODE_EXTRACT_AND_EXPAND + && n != EVP_KDF_HKDF_MODE_EXTRACT_ONLY + && n != EVP_KDF_HKDF_MODE_EXPAND_ONLY) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_MODE); + return 0; + } + ctx->mode = n; + } else { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_MODE); + return 0; + } + } + + if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_KEY)) != NULL) { + OPENSSL_clear_free(ctx->key, ctx->key_len); + ctx->key = NULL; + if (!OSSL_PARAM_get_octet_string(p, (void **)&ctx->key, 0, + &ctx->key_len)) + return 0; + } + + if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_SALT)) != NULL) { + if (p->data_size != 0 && p->data != NULL) { + OPENSSL_free(ctx->salt); + ctx->salt = NULL; + if (!OSSL_PARAM_get_octet_string(p, (void **)&ctx->salt, 0, + &ctx->salt_len)) + return 0; + } + } + /* The info fields concatenate, so process them all */ + if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_INFO)) != NULL) { + ctx->info_len = 0; + for (; p != NULL; p = OSSL_PARAM_locate_const(p + 1, + OSSL_KDF_PARAM_INFO)) { + const void *q = ctx->info + ctx->info_len; + size_t sz = 0; + + if (p->data_size != 0 + && p->data != NULL + && !OSSL_PARAM_get_octet_string(p, (void **)&q, + HKDF_MAXBUF - ctx->info_len, + &sz)) + return 0; + ctx->info_len += sz; + } + } + return 1; +} + +static const OSSL_PARAM *kdf_hkdf_settable_ctx_params(void) +{ + static const OSSL_PARAM known_settable_ctx_params[] = { + OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_MODE, NULL, 0), + OSSL_PARAM_int(OSSL_KDF_PARAM_MODE, NULL), + 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_SALT, NULL, 0), + OSSL_PARAM_octet_string(OSSL_KDF_PARAM_KEY, NULL, 0), + OSSL_PARAM_octet_string(OSSL_KDF_PARAM_INFO, NULL, 0), + OSSL_PARAM_END + }; + return known_settable_ctx_params; +} + +static int kdf_hkdf_get_ctx_params(void *vctx, OSSL_PARAM params[]) +{ + KDF_HKDF *ctx = (KDF_HKDF *)vctx; + OSSL_PARAM *p; + + if ((p = OSSL_PARAM_locate(params, OSSL_KDF_PARAM_SIZE)) != NULL) + return OSSL_PARAM_set_size_t(p, kdf_hkdf_size(ctx)); + return -2; +} + +static const OSSL_PARAM *kdf_hkdf_gettable_ctx_params(void) +{ + 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_hkdf_functions[] = { + { OSSL_FUNC_KDF_NEWCTX, (void(*)(void))kdf_hkdf_new }, + { OSSL_FUNC_KDF_FREECTX, (void(*)(void))kdf_hkdf_free }, + { OSSL_FUNC_KDF_RESET, (void(*)(void))kdf_hkdf_reset }, + { OSSL_FUNC_KDF_DERIVE, (void(*)(void))kdf_hkdf_derive }, + { OSSL_FUNC_KDF_SETTABLE_CTX_PARAMS, + (void(*)(void))kdf_hkdf_settable_ctx_params }, + { OSSL_FUNC_KDF_SET_CTX_PARAMS, (void(*)(void))kdf_hkdf_set_ctx_params }, + { OSSL_FUNC_KDF_GETTABLE_CTX_PARAMS, + (void(*)(void))kdf_hkdf_gettable_ctx_params }, + { OSSL_FUNC_KDF_GET_CTX_PARAMS, (void(*)(void))kdf_hkdf_get_ctx_params }, + { 0, NULL } +}; + +/* + * Refer to "HMAC-based Extract-and-Expand Key Derivation Function (HKDF)" + * Section 2 (https://tools.ietf.org/html/rfc5869#section-2) and + * "Cryptographic Extraction and Key Derivation: The HKDF Scheme" + * Section 4.2 (https://eprint.iacr.org/2010/264.pdf). + * + * From the paper: + * The scheme HKDF is specified as: + * HKDF(XTS, SKM, CTXinfo, L) = K(1) | K(2) | ... | K(t) + * + * where: + * SKM is source key material + * XTS is extractor salt (which may be null or constant) + * CTXinfo is context information (may be null) + * L is the number of key bits to be produced by KDF + * k is the output length in bits of the hash function used with HMAC + * t = ceil(L/k) + * the value K(t) is truncated to its first d = L mod k bits. + * + * From RFC 5869: + * 2.2. Step 1: Extract + * HKDF-Extract(salt, IKM) -> PRK + * 2.3. Step 2: Expand + * HKDF-Expand(PRK, info, L) -> OKM + */ +static int HKDF(const EVP_MD *evp_md, + const unsigned char *salt, size_t salt_len, + const unsigned char *ikm, size_t ikm_len, + const unsigned char *info, size_t info_len, + unsigned char *okm, size_t okm_len) +{ + unsigned char prk[EVP_MAX_MD_SIZE]; + int ret, sz; + size_t prk_len; + + sz = EVP_MD_size(evp_md); + if (sz < 0) + return 0; + prk_len = (size_t)sz; + + /* Step 1: HKDF-Extract(salt, IKM) -> PRK */ + if (!HKDF_Extract(evp_md, salt, salt_len, ikm, ikm_len, prk, prk_len)) + return 0; + + /* Step 2: HKDF-Expand(PRK, info, L) -> OKM */ + ret = HKDF_Expand(evp_md, prk, prk_len, info, info_len, okm, okm_len); + OPENSSL_cleanse(prk, sizeof(prk)); + + return ret; +} + +/* + * Refer to "HMAC-based Extract-and-Expand Key Derivation Function (HKDF)" + * Section 2.2 (https://tools.ietf.org/html/rfc5869#section-2.2). + * + * 2.2. Step 1: Extract + * + * HKDF-Extract(salt, IKM) -> PRK + * + * Options: + * Hash a hash function; HashLen denotes the length of the + * hash function output in octets + * + * Inputs: + * salt optional salt value (a non-secret random value); + * if not provided, it is set to a string of HashLen zeros. + * IKM input keying material + * + * Output: + * PRK a pseudorandom key (of HashLen octets) + * + * The output PRK is calculated as follows: + * + * PRK = HMAC-Hash(salt, IKM) + */ +static int HKDF_Extract(const EVP_MD *evp_md, + const unsigned char *salt, size_t salt_len, + const unsigned char *ikm, size_t ikm_len, + unsigned char *prk, size_t prk_len) +{ + int sz = EVP_MD_size(evp_md); + + if (sz < 0) + return 0; + if (prk_len != (size_t)sz) { + ERR_raise(ERR_LIB_PROV, PROV_R_WRONG_OUTPUT_BUFFER_SIZE); + return 0; + } + /* calc: PRK = HMAC-Hash(salt, IKM) */ + return HMAC(evp_md, salt, salt_len, ikm, ikm_len, prk, NULL) != NULL; +} + +/* + * Refer to "HMAC-based Extract-and-Expand Key Derivation Function (HKDF)" + * Section 2.3 (https://tools.ietf.org/html/rfc5869#section-2.3). + * + * 2.3. Step 2: Expand + * + * HKDF-Expand(PRK, info, L) -> OKM + * + * Options: + * Hash a hash function; HashLen denotes the length of the + * hash function output in octets + * + * Inputs: + * PRK a pseudorandom key of at least HashLen octets + * (usually, the output from the extract step) + * info optional context and application specific information + * (can be a zero-length string) + * L length of output keying material in octets + * (<= 255*HashLen) + * + * Output: + * OKM output keying material (of L octets) + * + * The output OKM is calculated as follows: + * + * N = ceil(L/HashLen) + * T = T(1) | T(2) | T(3) | ... | T(N) + * OKM = first L octets of T + * + * where: + * T(0) = empty string (zero length) + * T(1) = HMAC-Hash(PRK, T(0) | info | 0x01) + * T(2) = HMAC-Hash(PRK, T(1) | info | 0x02) + * T(3) = HMAC-Hash(PRK, T(2) | info | 0x03) + * ... + * + * (where the constant concatenated to the end of each T(n) is a + * single octet.) + */ +static int HKDF_Expand(const EVP_MD *evp_md, + const unsigned char *prk, size_t prk_len, + const unsigned char *info, size_t info_len, + unsigned char *okm, size_t okm_len) +{ + HMAC_CTX *hmac; + int ret = 0, sz; + unsigned int i; + unsigned char prev[EVP_MAX_MD_SIZE]; + size_t done_len = 0, dig_len, n; + + sz = EVP_MD_size(evp_md); + if (sz <= 0) + return 0; + dig_len = (size_t)sz; + + /* calc: N = ceil(L/HashLen) */ + n = okm_len / dig_len; + if (okm_len % dig_len) + n++; + + if (n > 255 || okm == NULL) + return 0; + + if ((hmac = HMAC_CTX_new()) == NULL) + return 0; + + if (!HMAC_Init_ex(hmac, prk, prk_len, evp_md, NULL)) + goto err; + + for (i = 1; i <= n; i++) { + size_t copy_len; + const unsigned char ctr = i; + + /* calc: T(i) = HMAC-Hash(PRK, T(i - 1) | info | i) */ + if (i > 1) { + if (!HMAC_Init_ex(hmac, NULL, 0, NULL, NULL)) + goto err; + + if (!HMAC_Update(hmac, prev, dig_len)) + goto err; + } + + if (!HMAC_Update(hmac, info, info_len)) + goto err; + + if (!HMAC_Update(hmac, &ctr, 1)) + goto err; + + if (!HMAC_Final(hmac, prev, NULL)) + goto err; + + copy_len = (done_len + dig_len > okm_len) ? + okm_len - done_len : + dig_len; + + memcpy(okm + done_len, prev, copy_len); + + done_len += copy_len; + } + ret = 1; + + err: + OPENSSL_cleanse(prev, sizeof(prev)); + HMAC_CTX_free(hmac); + return ret; +} diff --git a/providers/implementations/kdfs/kbkdf.c b/providers/implementations/kdfs/kbkdf.c new file mode 100644 index 0000000000..ffffef0b17 --- /dev/null +++ b/providers/implementations/kdfs/kbkdf.c @@ -0,0 +1,303 @@ +/* + * Copyright 2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 2019 Red Hat, Inc. + * + * 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 + */ + +/* + * This implements https://csrc.nist.gov/publications/detail/sp/800-108/final + * section 5.1 ("counter mode") in HMAC only. That document does not name the + * KDFs it defines; the name is derived from + * https://csrc.nist.gov/Projects/Cryptographic-Algorithm-Validation-Program/Key-Derivation + * + * Note that sections 5.2 ("feedback mode") and 5.3 ("double-pipeline mode") + * are not implemented, though it would be possible to do so in the future. + * CMAC mode is also not implemented; some plumbing would be required. + * + * These versions all assume the counter is used. It would be relatively + * straightforward to expose a configuration handle should the need arise. + * + * Variable names attempt to match those of SP800-108. + */ + +#include <stdarg.h> +#include <stdlib.h> +#include <string.h> + +#include <openssl/core_names.h> +#include <openssl/evp.h> +#include <openssl/hmac.h> +#include <openssl/kdf.h> +#include <openssl/params.h> + +#include "internal/cryptlib.h" +#include "crypto/evp.h" +#include "internal/numbers.h" +#include "internal/provider_algs.h" +#include "internal/provider_ctx.h" +#include "internal/provider_util.h" +#include "internal/providercommonerr.h" + +#include "e_os.h" + +#define MIN(a, b) ((a) < (b)) ? (a) : (b) + +/* Our context structure. */ +typedef struct { + void *provctx; + EVP_MAC_CTX *ctx_init; + + /* Names are lowercased versions of those found in SP800-108. */ + unsigned char *ki; + size_t ki_len; + unsigned char *label; + size_t label_len; + unsigned char *context; + size_t context_len; +} KBKDF; + +/* Definitions needed for typechecking. */ +static OSSL_OP_kdf_newctx_fn kbkdf_new; +static OSSL_OP_kdf_freectx_fn kbkdf_free; +static OSSL_OP_kdf_reset_fn kbkdf_reset; +static OSSL_OP_kdf_derive_fn kbkdf_derive; +static OSSL_OP_kdf_settable_ctx_params_fn kbkdf_settable_ctx_params; +static OSSL_OP_kdf_set_ctx_params_fn kbkdf_set_ctx_params; + +/* Not all platforms have htobe32(). */ +static uint32_t be32(uint32_t host) +{ + uint32_t big = 0; + const union { + long one; + char little; + } is_endian = { 1 }; + + if (!is_endian.little) + return host; + + big |= (host & 0xff000000) >> 24; + big |= (host & 0x00ff0000) >> 8; + big |= (host & 0x0000ff00) << 8; + big |= (host & 0x000000ff) << 24; + return big; +} + +static void *kbkdf_new(void *provctx) +{ + KBKDF *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 kbkdf_free(void *vctx) +{ + KBKDF *ctx = (KBKDF *)vctx; + + kbkdf_reset(ctx); + OPENSSL_free(ctx); +} + +static void kbkdf_reset(void *vctx) +{ + KBKDF *ctx = (KBKDF *)vctx; + + EVP_MAC_CTX_free(ctx->ctx_init); + OPENSSL_clear_free(ctx->context, ctx->context_len); + OPENSSL_clear_free(ctx->label, ctx->label_len); + OPENSSL_clear_free(ctx->ki, ctx->ki_len); + memset(ctx, 0, sizeof(*ctx)); +} + +/* SP800-108 section 5.1. */ +static int kbkdf_derive_counter(EVP_MAC_CTX *ctx_init, + unsigned char *label, size_t label_len, + unsigned char *context, size_t context_len, + unsigned char *k_i, size_t h, uint32_t l, + unsigned char *ko, size_t ko_len) +{ + int ret = 0; + EVP_MAC_CTX *ctx = NULL; + size_t written = 0, to_write; + const unsigned char zero = 0; + uint32_t counter, i; + + for (counter = 1; written < ko_len; counter++) { + i = be32(counter); + + ctx = EVP_MAC_CTX_dup(ctx_init); + if (ctx == NULL) + goto done; + + if (!EVP_MAC_update(ctx, (unsigned char *)&i, 4) + || !EVP_MAC_update(ctx, label, label_len) + || !EVP_MAC_update(ctx, &zero, 1) + || !EVP_MAC_update(ctx, context, context_len) + || !EVP_MAC_update(ctx, (unsigned char *)&l, 4) + || !EVP_MAC_final(ctx, k_i, NULL, h)) + goto done; + + to_write = ko_len - written; + memcpy(ko + written, k_i, MIN(to_write, h)); + written += h; + + EVP_MAC_CTX_free(ctx); + ctx = NULL; + } + + ret = 1; +done: + EVP_MAC_CTX_free(ctx); + return ret; +} + +static int kbkdf_derive(void *vctx, unsigned char *key, size_t keylen) +{ + KBKDF *ctx = (KBKDF *)vctx; + int ret = 0; + unsigned char *k_i = NULL; + uint32_t l = be32(keylen * 8); + size_t h = 0; + + /* Label and Context are permitted to be empty. Check everything else. */ + if (ctx->ctx_init == NULL) { + if (ctx->ki_len == 0 || ctx->ki == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_NO_KEY_SET); + return 0; + } + /* Could either be missing MAC or missing message digest - + * arbitrarily, I pick this one. */ + ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_MAC); + return 0; + } + + h = EVP_MAC_size(ctx->ctx_init); + if (h == 0) + goto done; + + k_i = OPENSSL_zalloc(h); + if (k_i == NULL) + goto done; + + ret = kbkdf_derive_counter( + ctx->ctx_init, ctx->label, ctx->label_len, ctx->context, + ctx->context_len, k_i, h, l, key, keylen); +done: + if (ret != 1) + OPENSSL_cleanse(key, keylen); + OPENSSL_clear_free(k_i, h); + return ret; +} + +static int kbkdf_set_buffer(unsigned char **out, size_t *out_len, + const OSSL_PARAM *p) +{ + if (p->data == NULL || p->data_size == 0) + return 1; + + OPENSSL_clear_free(*out, *out_len); + *out = NULL; + return OSSL_PARAM_get_octet_string(p, (void **)out, 0, out_len); +} + +static int kbkdf_set_ctx_params(void *vctx, const OSSL_PARAM params[]) +{ + KBKDF *ctx = (KBKDF *)vctx; + OPENSSL_CTX *libctx = PROV_LIBRARY_CONTEXT_OF(ctx->provctx); + const OSSL_PARAM *p; + OSSL_PARAM mparams[2]; + + if (!ossl_prov_macctx_load_from_params(&ctx->ctx_init, params, NULL, + NULL, NULL, libctx)) + return 0; + else if (ctx->ctx_init != NULL + && !EVP_MAC_is_a(EVP_MAC_CTX_mac(ctx->ctx_init), + OSSL_MAC_NAME_HMAC)) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_MAC); + return 0; + } + + p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_KEY); + if (p != NULL && !kbkdf_set_buffer(&ctx->ki, &ctx->ki_len, p)) + return 0; + + p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_SALT); + if (p != NULL && !kbkdf_set_buffer(&ctx->label, &ctx->label_len, p)) + return 0; + + p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_INFO); + if (p != NULL && !kbkdf_set_buffer(&ctx->context, &ctx->context_len, p)) + return 0; + + /* Set up digest context, if we can. */ + if (ctx->ctx_init != NULL && ctx->ki_len != 0) { + mparams[0] = OSSL_PARAM_construct_octet_string(OSSL_MAC_PARAM_KEY, + ctx->ki, ctx->ki_len); + mparams[1] = OSSL_PARAM_construct_end(); + + if (!EVP_MAC_CTX_set_params(ctx->ctx_init, mparams) + || !EVP_MAC_init(ctx->ctx_init)) + return 0; + } + + return 1; +} + +static const OSSL_PARAM *kbkdf_settable_ctx_params(void) +{ + static const OSSL_PARAM known_settable_ctx_params[] = { + OSSL_PARAM_octet_string(OSSL_KDF_PARAM_INFO, NULL, 0), + OSSL_PARAM_octet_string(OSSL_KDF_PARAM_SALT, NULL, 0), + OSSL_PARAM_octet_string(OSSL_KDF_PARAM_KEY, NULL, 0), + OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_DIGEST, NULL, 0), + OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_MAC, NULL, 0), + + OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_PROPERTIES, NULL, 0), + OSSL_PARAM_END, + }; + return known_settable_ctx_params; +} + +static int kbkdf_get_ctx_params(void *vctx, OSSL_PARAM params[]) +{ + OSSL_PARAM *p; + + p = OSSL_PARAM_locate(params, OSSL_KDF_PARAM_SIZE); + if (p == NULL) + return -2; + + /* KBKDF can produce results as large as you like. */ + return OSSL_PARAM_set_size_t(p, SIZE_MAX); +} + +static const OSSL_PARAM *kbkdf_gettable_ctx_params(void) +{ + 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_kbkdf_functions[] = { + { OSSL_FUNC_KDF_NEWCTX, (void(*)(void))kbkdf_new }, + { OSSL_FUNC_KDF_FREECTX, (void(*)(void))kbkdf_free }, + { OSSL_FUNC_KDF_RESET, (void(*)(void))kbkdf_reset }, + { OSSL_FUNC_KDF_DERIVE, (void(*)(void))kbkdf_derive }, + { OSSL_FUNC_KDF_SETTABLE_CTX_PARAMS, + (void(*)(void))kbkdf_settable_ctx_params }, + { OSSL_FUNC_KDF_SET_CTX_PARAMS, (void(*)(void))kbkdf_set_ctx_params }, + { OSSL_FUNC_KDF_GETTABLE_CTX_PARAMS, + (void(*)(void))kbkdf_gettable_ctx_params }, + { OSSL_FUNC_KDF_GET_CTX_PARAMS, (void(*)(void))kbkdf_get_ctx_params }, + { 0, NULL }, +}; diff --git a/providers/implementations/kdfs/pbkdf2.c b/providers/implementations/kdfs/pbkdf2.c new file mode 100644 index 0000000000..68aa0aa7c4 --- /dev/null +++ b/providers/implementations/kdfs/pbkdf2.c @@ -0,0 +1,336 @@ +/* + * Copyright 2018-2019 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 <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <openssl/hmac.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 "internal/provider_ctx.h" +#include "internal/providercommonerr.h" +#include "internal/provider_algs.h" +#include "internal/provider_util.h" +#include "pbkdf2.h" + +/* Constants specified in SP800-132 */ +#define KDF_PBKDF2_MIN_KEY_LEN_BITS 112 +#define KDF_PBKDF2_MAX_KEY_LEN_DIGEST_RATIO 0xFFFFFFFF +#define KDF_PBKDF2_MIN_ITERATIONS 1000 +#define KDF_PBKDF2_MIN_SALT_LEN (128 / 8) + +static OSSL_OP_kdf_newctx_fn kdf_pbkdf2_new; +static OSSL_OP_kdf_freectx_fn kdf_pbkdf2_free; +static OSSL_OP_kdf_reset_fn kdf_pbkdf2_reset; +static OSSL_OP_kdf_derive_fn kdf_pbkdf2_derive; +static OSSL_OP_kdf_settable_ctx_params_fn kdf_pbkdf2_settable_ctx_params; +static OSSL_OP_kdf_set_ctx_params_fn kdf_pbkdf2_set_ctx_params; + +static int pbkdf2_derive(const char *pass, size_t passlen, + const unsigned char *salt, int saltlen, uint64_t iter, + const EVP_MD *digest, unsigned char *key, + size_t keylen, int extra_checks); + +typedef struct { + void *provctx; + unsigned char *pass; + size_t pass_len; + unsigned char *salt; + size_t salt_len; + uint64_t iter; + PROV_DIGEST digest; + int lower_bound_checks; +} KDF_PBKDF2; + +static void kdf_pbkdf2_init(KDF_PBKDF2 *ctx); + +static void *kdf_pbkdf2_new(void *provctx) +{ + KDF_PBKDF2 *ctx; + + ctx = OPENSSL_zalloc(sizeof(*ctx)); + if (ctx == NULL) { + ERR_raise(ERR_LIB_PROV, ERR_R_MALLOC_FAILURE); + return NULL; + } + ctx->provctx = provctx; + kdf_pbkdf2_init(ctx); + return ctx; +} + +static void kdf_pbkdf2_cleanup(KDF_PBKDF2 *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_pbkdf2_free(void *vctx) +{ + KDF_PBKDF2 *ctx = (KDF_PBKDF2 *)vctx; + + kdf_pbkdf2_cleanup(ctx); + OPENSSL_free(ctx); +} + +static void kdf_pbkdf2_reset(void *vctx) +{ + KDF_PBKDF2 *ctx = (KDF_PBKDF2 *)vctx; + + kdf_pbkdf2_cleanup(ctx); + kdf_pbkdf2_init(ctx); +} + +static void kdf_pbkdf2_init(KDF_PBKDF2 *ctx) +{ + OSSL_PARAM params[2] = { OSSL_PARAM_END, OSSL_PARAM_END }; + OPENSSL_CTX *provctx = PROV_LIBRARY_CONTEXT_OF(ctx->provctx); + + params[0] = OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_DIGEST, + SN_sha1, 0); + if (!ossl_prov_digest_load_from_params(&ctx->digest, params, provctx)) + /* This is an error, but there is no way to indicate such directly */ + ossl_prov_digest_reset(&ctx->digest); + ctx->iter = PKCS5_DEFAULT_ITER; + ctx->lower_bound_checks = kdf_pbkdf2_default_checks; +} + +static int pbkdf2_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_pbkdf2_derive(void *vctx, unsigned char *key, + size_t keylen) +{ + KDF_PBKDF2 *ctx = (KDF_PBKDF2 *)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 pbkdf2_derive((char *)ctx->pass, ctx->pass_len, + ctx->salt, ctx->salt_len, ctx->iter, + md, key, keylen, ctx->lower_bound_checks); +} + +static int kdf_pbkdf2_set_ctx_params(void *vctx, const OSSL_PARAM params[]) +{ + const OSSL_PARAM *p; + KDF_PBKDF2 *ctx = vctx; + OPENSSL_CTX *provctx = PROV_LIBRARY_CONTEXT_OF(ctx->provctx); + int pkcs5; + uint64_t iter, min_iter; + + if (!ossl_prov_digest |