diff options
author | Enji Cooper <ngie@FreeBSD.org> | 2025-05-07 21:18:24 +0000 |
---|---|---|
committer | Enji Cooper <ngie@FreeBSD.org> | 2025-05-07 22:37:22 +0000 |
commit | 29536654cc41bf41b92dc836c47496dc6fe0b00c (patch) | |
tree | 368a3c5b14e610bb5f6b71657f61a41e373eaf97 /providers/implementations/kdfs/hkdf.c | |
parent | 1c34280346af8284acdc0eae39496811d37df25d (diff) |
Diffstat (limited to 'providers/implementations/kdfs/hkdf.c')
-rw-r--r-- | providers/implementations/kdfs/hkdf.c | 345 |
1 files changed, 255 insertions, 90 deletions
diff --git a/providers/implementations/kdfs/hkdf.c b/providers/implementations/kdfs/hkdf.c index 69ef565d04fc..264bc5d4c162 100644 --- a/providers/implementations/kdfs/hkdf.c +++ b/providers/implementations/kdfs/hkdf.c @@ -29,12 +29,15 @@ #include "prov/providercommon.h" #include "prov/implementations.h" #include "prov/provider_util.h" -#include "e_os.h" +#include "prov/securitycheck.h" +#include "internal/e_os.h" +#include "internal/params.h" #define HKDF_MAXBUF 2048 #define HKDF_MAXINFO (32*1024) static OSSL_FUNC_kdf_newctx_fn kdf_hkdf_new; +static OSSL_FUNC_kdf_dupctx_fn kdf_hkdf_dup; static OSSL_FUNC_kdf_freectx_fn kdf_hkdf_free; static OSSL_FUNC_kdf_reset_fn kdf_hkdf_reset; static OSSL_FUNC_kdf_derive_fn kdf_hkdf_derive; @@ -45,6 +48,8 @@ static OSSL_FUNC_kdf_get_ctx_params_fn kdf_hkdf_get_ctx_params; static OSSL_FUNC_kdf_derive_fn kdf_tls1_3_derive; static OSSL_FUNC_kdf_settable_ctx_params_fn kdf_tls1_3_settable_ctx_params; static OSSL_FUNC_kdf_set_ctx_params_fn kdf_tls1_3_set_ctx_params; +static OSSL_FUNC_kdf_gettable_ctx_params_fn kdf_tls1_3_gettable_ctx_params; +static OSSL_FUNC_kdf_get_ctx_params_fn kdf_tls1_3_get_ctx_params; static int HKDF(OSSL_LIB_CTX *libctx, const EVP_MD *evp_md, const unsigned char *salt, size_t salt_len, @@ -61,13 +66,18 @@ static int HKDF_Expand(const EVP_MD *evp_md, unsigned char *okm, size_t okm_len); /* Settable context parameters that are common across HKDF and the TLS KDF */ -#define HKDF_COMMON_SETTABLES \ - 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_KEY, NULL, 0), \ - OSSL_PARAM_octet_string(OSSL_KDF_PARAM_SALT, NULL, 0) +#define HKDF_COMMON_SETTABLES \ + 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_KEY, NULL, 0), \ + OSSL_PARAM_octet_string(OSSL_KDF_PARAM_SALT, NULL, 0) + +/* Gettable context parameters that are common across HKDF and the TLS KDF */ +#define HKDF_COMMON_GETTABLES \ + OSSL_PARAM_size_t(OSSL_KDF_PARAM_SIZE, NULL), \ + OSSL_PARAM_octet_string(OSSL_KDF_PARAM_INFO, NULL, 0) typedef struct { void *provctx; @@ -85,6 +95,7 @@ typedef struct { size_t data_len; unsigned char *info; size_t info_len; + OSSL_FIPS_IND_DECLARE } KDF_HKDF; static void *kdf_hkdf_new(void *provctx) @@ -94,10 +105,10 @@ static void *kdf_hkdf_new(void *provctx) if (!ossl_prov_is_running()) return NULL; - if ((ctx = OPENSSL_zalloc(sizeof(*ctx))) == NULL) - ERR_raise(ERR_LIB_PROV, ERR_R_MALLOC_FAILURE); - else + if ((ctx = OPENSSL_zalloc(sizeof(*ctx))) != NULL) { ctx->provctx = provctx; + OSSL_FIPS_IND_INIT(ctx) + } return ctx; } @@ -117,7 +128,11 @@ static void kdf_hkdf_reset(void *vctx) void *provctx = ctx->provctx; ossl_prov_digest_reset(&ctx->digest); +#ifdef OPENSSL_PEDANTIC_ZEROIZATION + OPENSSL_clear_free(ctx->salt, ctx->salt_len); +#else OPENSSL_free(ctx->salt); +#endif OPENSSL_free(ctx->prefix); OPENSSL_free(ctx->label); OPENSSL_clear_free(ctx->data, ctx->data_len); @@ -127,6 +142,37 @@ static void kdf_hkdf_reset(void *vctx) ctx->provctx = provctx; } +static void *kdf_hkdf_dup(void *vctx) +{ + const KDF_HKDF *src = (const KDF_HKDF *)vctx; + KDF_HKDF *dest; + + dest = kdf_hkdf_new(src->provctx); + if (dest != NULL) { + if (!ossl_prov_memdup(src->salt, src->salt_len, &dest->salt, + &dest->salt_len) + || !ossl_prov_memdup(src->key, src->key_len, + &dest->key , &dest->key_len) + || !ossl_prov_memdup(src->prefix, src->prefix_len, + &dest->prefix, &dest->prefix_len) + || !ossl_prov_memdup(src->label, src->label_len, + &dest->label, &dest->label_len) + || !ossl_prov_memdup(src->data, src->data_len, + &dest->data, &dest->data_len) + || !ossl_prov_memdup(src->info, src->info_len, + &dest->info, &dest->info_len) + || !ossl_prov_digest_copy(&dest->digest, &src->digest)) + goto err; + dest->mode = src->mode; + OSSL_FIPS_IND_COPY(dest, src) + } + return dest; + + err: + kdf_hkdf_free(dest); + return NULL; +} + static size_t kdf_hkdf_size(KDF_HKDF *ctx) { int sz; @@ -140,12 +186,30 @@ static size_t kdf_hkdf_size(KDF_HKDF *ctx) return 0; } sz = EVP_MD_get_size(md); - if (sz < 0) + if (sz <= 0) return 0; return sz; } +#ifdef FIPS_MODULE +static int fips_hkdf_key_check_passed(KDF_HKDF *ctx) +{ + OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(ctx->provctx); + int key_approved = ossl_kdf_check_key_size(ctx->key_len); + + if (!key_approved) { + if (!OSSL_FIPS_IND_ON_UNAPPROVED(ctx, OSSL_FIPS_IND_SETTABLE0, + libctx, "HKDF", "Key size", + ossl_fips_config_hkdf_key_check)) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH); + return 0; + } + } + return 1; +} +#endif + static int kdf_hkdf_derive(void *vctx, unsigned char *key, size_t keylen, const OSSL_PARAM params[]) { @@ -192,11 +256,21 @@ static int hkdf_common_set_ctx_params(KDF_HKDF *ctx, const OSSL_PARAM params[]) const OSSL_PARAM *p; int n; - if (params == NULL) + if (ossl_param_is_empty(params)) return 1; - if (!ossl_prov_digest_load_from_params(&ctx->digest, params, libctx)) - return 0; + if (OSSL_PARAM_locate_const(params, OSSL_ALG_PARAM_DIGEST) != NULL) { + const EVP_MD *md = NULL; + + if (!ossl_prov_digest_load_from_params(&ctx->digest, params, libctx)) + return 0; + + md = ossl_prov_digest_md(&ctx->digest); + if (EVP_MD_xof(md)) { + ERR_raise(ERR_LIB_PROV, PROV_R_XOF_DIGESTS_NOT_ALLOWED); + return 0; + } + } if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_MODE)) != NULL) { if (p->data_type == OSSL_PARAM_UTF8_STRING) { @@ -245,75 +319,31 @@ static int hkdf_common_set_ctx_params(KDF_HKDF *ctx, const OSSL_PARAM params[]) return 1; } -/* - * Use WPACKET to concat one or more OSSL_KDF_PARAM_INFO fields into a fixed - * out buffer of size *outlen. - * If out is NULL then outlen is used to return the required buffer size. - */ -static int setinfo_fromparams(const OSSL_PARAM *p, unsigned char *out, size_t *outlen) -{ - int ret = 0; - WPACKET pkt; - - if (out == NULL) { - if (!WPACKET_init_null(&pkt, 0)) - return 0; - } else { - if (!WPACKET_init_static_len(&pkt, out, *outlen, 0)) - return 0; - } - - for (; p != NULL; p = OSSL_PARAM_locate_const(p + 1, OSSL_KDF_PARAM_INFO)) { - if (p->data_type != OSSL_PARAM_OCTET_STRING) - goto err; - if (p->data != NULL - && p->data_size != 0 - && !WPACKET_memcpy(&pkt, p->data, p->data_size)) - goto err; - } - if (!WPACKET_get_total_written(&pkt, outlen) - || !WPACKET_finish(&pkt)) - goto err; - ret = 1; -err: - WPACKET_cleanup(&pkt); - return ret; -} - static int kdf_hkdf_set_ctx_params(void *vctx, const OSSL_PARAM params[]) { - const OSSL_PARAM *p; KDF_HKDF *ctx = vctx; - if (params == NULL) + if (ossl_param_is_empty(params)) return 1; + if (!OSSL_FIPS_IND_SET_CTX_PARAM(ctx, OSSL_FIPS_IND_SETTABLE0, params, + OSSL_KDF_PARAM_FIPS_KEY_CHECK)) + return 0; + if (!hkdf_common_set_ctx_params(ctx, params)) return 0; - /* The info fields concatenate, so process them all */ - if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_INFO)) != NULL) { - size_t sz = 0; + if (ossl_param_get1_concat_octet_string(params, OSSL_KDF_PARAM_INFO, + &ctx->info, &ctx->info_len, + HKDF_MAXINFO) == 0) + return 0; - /* calculate the total size */ - if (!setinfo_fromparams(p, NULL, &sz)) - return 0; - if (sz > HKDF_MAXINFO) +#ifdef FIPS_MODULE + if (OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_KEY) != NULL) + if (!fips_hkdf_key_check_passed(ctx)) return 0; +#endif - OPENSSL_clear_free(ctx->info, ctx->info_len); - ctx->info = NULL; - if (sz == 0) - return 1; - /* Alloc the buffer */ - ctx->info = OPENSSL_malloc(sz); - if (ctx->info == NULL) - return 0; - ctx->info_len = sz; - /* Concat one or more OSSL_KDF_PARAM_INFO fields */ - if (!setinfo_fromparams(p, ctx->info, &sz)) - return 0; - } return 1; } @@ -323,39 +353,60 @@ static const OSSL_PARAM *kdf_hkdf_settable_ctx_params(ossl_unused void *ctx, static const OSSL_PARAM known_settable_ctx_params[] = { HKDF_COMMON_SETTABLES, OSSL_PARAM_octet_string(OSSL_KDF_PARAM_INFO, NULL, 0), + OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_KDF_PARAM_FIPS_KEY_CHECK) OSSL_PARAM_END }; return known_settable_ctx_params; } -static int kdf_hkdf_get_ctx_params(void *vctx, OSSL_PARAM params[]) +static int hkdf_common_get_ctx_params(KDF_HKDF *ctx, OSSL_PARAM params[]) { - KDF_HKDF *ctx = (KDF_HKDF *)vctx; OSSL_PARAM *p; + if (ossl_param_is_empty(params)) + return 1; + if ((p = OSSL_PARAM_locate(params, OSSL_KDF_PARAM_SIZE)) != NULL) { size_t sz = kdf_hkdf_size(ctx); if (sz == 0) return 0; - return OSSL_PARAM_set_size_t(p, sz); + if (!OSSL_PARAM_set_size_t(p, sz)) + return 0; } + if ((p = OSSL_PARAM_locate(params, OSSL_KDF_PARAM_INFO)) != NULL) { - if (ctx->info == NULL || ctx->info_len == 0) { + if (ctx->info == NULL || ctx->info_len == 0) p->return_size = 0; - return 1; - } - return OSSL_PARAM_set_octet_string(p, ctx->info, ctx->info_len); + else if (!OSSL_PARAM_set_octet_string(p, ctx->info, ctx->info_len)) + return 0; } - return -2; + + return 1; +} + +static int kdf_hkdf_get_ctx_params(void *vctx, OSSL_PARAM params[]) +{ + KDF_HKDF *ctx = (KDF_HKDF *)vctx; + + if (ossl_param_is_empty(params)) + return 1; + + if (!hkdf_common_get_ctx_params(ctx, params)) + return 0; + + if (!OSSL_FIPS_IND_GET_CTX_PARAM(ctx, params)) + return 0; + + return 1; } static const OSSL_PARAM *kdf_hkdf_gettable_ctx_params(ossl_unused void *ctx, ossl_unused void *provctx) { static const OSSL_PARAM known_gettable_ctx_params[] = { - OSSL_PARAM_size_t(OSSL_KDF_PARAM_SIZE, NULL), - OSSL_PARAM_octet_string(OSSL_KDF_PARAM_INFO, NULL, 0), + HKDF_COMMON_GETTABLES, + OSSL_FIPS_IND_GETTABLE_CTX_PARAM() OSSL_PARAM_END }; return known_gettable_ctx_params; @@ -363,6 +414,7 @@ static const OSSL_PARAM *kdf_hkdf_gettable_ctx_params(ossl_unused void *ctx, const OSSL_DISPATCH ossl_kdf_hkdf_functions[] = { { OSSL_FUNC_KDF_NEWCTX, (void(*)(void))kdf_hkdf_new }, + { OSSL_FUNC_KDF_DUPCTX, (void(*)(void))kdf_hkdf_dup }, { 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 }, @@ -372,7 +424,7 @@ const OSSL_DISPATCH ossl_kdf_hkdf_functions[] = { { 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 } + OSSL_DISPATCH_END }; /* @@ -411,7 +463,7 @@ static int HKDF(OSSL_LIB_CTX *libctx, const EVP_MD *evp_md, size_t prk_len; sz = EVP_MD_get_size(evp_md); - if (sz < 0) + if (sz <= 0) return 0; prk_len = (size_t)sz; @@ -458,7 +510,7 @@ static int HKDF_Extract(OSSL_LIB_CTX *libctx, const EVP_MD *evp_md, { int sz = EVP_MD_get_size(evp_md); - if (sz < 0) + if (sz <= 0) return 0; if (prk_len != (size_t)sz) { ERR_raise(ERR_LIB_PROV, PROV_R_WRONG_OUTPUT_BUFFER_SIZE); @@ -654,7 +706,7 @@ static int prov_tls13_hkdf_generate_secret(OSSL_LIB_CTX *libctx, } if (prevsecret == NULL) { prevsecret = default_zeros; - prevsecretlen = 0; + prevsecretlen = mdlen; } else { EVP_MD_CTX *mctx = EVP_MD_CTX_new(); unsigned char hash[EVP_MAX_MD_SIZE]; @@ -685,6 +737,68 @@ static int prov_tls13_hkdf_generate_secret(OSSL_LIB_CTX *libctx, return ret; } +#ifdef FIPS_MODULE +static int fips_tls1_3_digest_check_passed(KDF_HKDF *ctx, const EVP_MD *md) +{ + OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(ctx->provctx); + /* + * Perform digest check + * + * According to RFC 8446 appendix B.4, the valid hash functions are + * specified in FIPS 180-4. However, it only lists SHA2-256 and SHA2-384 in + * the table. ACVP also only lists the same set of hash functions. + */ + int digest_unapproved = !EVP_MD_is_a(md, SN_sha256) + && !EVP_MD_is_a(md, SN_sha384); + + if (digest_unapproved) { + if (!OSSL_FIPS_IND_ON_UNAPPROVED(ctx, OSSL_FIPS_IND_SETTABLE0, + libctx, "TLS13 KDF", "Digest", + ossl_fips_config_tls13_kdf_digest_check)) { + ERR_raise(ERR_LIB_PROV, PROV_R_DIGEST_NOT_ALLOWED); + return 0; + } + } + return 1; +} + +/* + * Calculate the correct length of the secret key. + * + * RFC 8446: + * If a given secret is not available, then the 0-value consisting of a + * string of Hash.length bytes set to zeros is used. + */ +static size_t fips_tls1_3_key_size(KDF_HKDF *ctx) +{ + const EVP_MD *md = ossl_prov_digest_md(&ctx->digest); + size_t key_size = 0; + + if (ctx->key != NULL) + key_size = ctx->key_len; + else if (md != NULL) + key_size = EVP_MD_size(md); + + return key_size; +} + +static int fips_tls1_3_key_check_passed(KDF_HKDF *ctx) +{ + OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(ctx->provctx); + int key_approved = ossl_kdf_check_key_size(fips_tls1_3_key_size(ctx)); + + if (!key_approved) { + if (!OSSL_FIPS_IND_ON_UNAPPROVED(ctx, OSSL_FIPS_IND_SETTABLE1, + libctx, "TLS13 KDF", "Key size", + ossl_fips_config_tls13_kdf_key_check)) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH); + return 0; + } + } + return 1; +} +#endif + static int kdf_tls1_3_derive(void *vctx, unsigned char *key, size_t keylen, const OSSL_PARAM params[]) { @@ -727,9 +841,16 @@ static int kdf_tls1_3_set_ctx_params(void *vctx, const OSSL_PARAM params[]) const OSSL_PARAM *p; KDF_HKDF *ctx = vctx; - if (params == NULL) + if (ossl_param_is_empty(params)) return 1; + if (!OSSL_FIPS_IND_SET_CTX_PARAM(ctx, OSSL_FIPS_IND_SETTABLE0, params, + OSSL_KDF_PARAM_FIPS_DIGEST_CHECK)) + return 0; + if (!OSSL_FIPS_IND_SET_CTX_PARAM(ctx, OSSL_FIPS_IND_SETTABLE1, params, + OSSL_KDF_PARAM_FIPS_KEY_CHECK)) + return 0; + if (!hkdf_common_set_ctx_params(ctx, params)) return 0; @@ -760,6 +881,20 @@ static int kdf_tls1_3_set_ctx_params(void *vctx, const OSSL_PARAM params[]) && !OSSL_PARAM_get_octet_string(p, (void **)&ctx->data, 0, &ctx->data_len)) return 0; + +#ifdef FIPS_MODULE + if (OSSL_PARAM_locate_const(params, OSSL_ALG_PARAM_DIGEST) != NULL) { + const EVP_MD *md = ossl_prov_digest_md(&ctx->digest); + + if (!fips_tls1_3_digest_check_passed(ctx, md)) + return 0; + } + + if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_KEY)) != NULL) + if (!fips_tls1_3_key_check_passed(ctx)) + return 0; +#endif + return 1; } @@ -771,13 +906,43 @@ static const OSSL_PARAM *kdf_tls1_3_settable_ctx_params(ossl_unused void *ctx, OSSL_PARAM_octet_string(OSSL_KDF_PARAM_PREFIX, NULL, 0), OSSL_PARAM_octet_string(OSSL_KDF_PARAM_LABEL, NULL, 0), OSSL_PARAM_octet_string(OSSL_KDF_PARAM_DATA, NULL, 0), + OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_KDF_PARAM_FIPS_DIGEST_CHECK) + OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_KDF_PARAM_FIPS_KEY_CHECK) OSSL_PARAM_END }; return known_settable_ctx_params; } +static int kdf_tls1_3_get_ctx_params(void *vctx, OSSL_PARAM params[]) +{ + KDF_HKDF *ctx = (KDF_HKDF *)vctx; + + if (ossl_param_is_empty(params)) + return 1; + + if (!hkdf_common_get_ctx_params(ctx, params)) + return 0; + + if (!OSSL_FIPS_IND_GET_CTX_PARAM(ctx, params)) + return 0; + + return 1; +} + +static const OSSL_PARAM *kdf_tls1_3_gettable_ctx_params(ossl_unused void *ctx, + ossl_unused void *provctx) +{ + static const OSSL_PARAM known_gettable_ctx_params[] = { + HKDF_COMMON_GETTABLES, + OSSL_FIPS_IND_GETTABLE_CTX_PARAM() + OSSL_PARAM_END + }; + return known_gettable_ctx_params; +} + const OSSL_DISPATCH ossl_kdf_tls1_3_kdf_functions[] = { { OSSL_FUNC_KDF_NEWCTX, (void(*)(void))kdf_hkdf_new }, + { OSSL_FUNC_KDF_DUPCTX, (void(*)(void))kdf_hkdf_dup }, { 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_tls1_3_derive }, @@ -785,7 +950,7 @@ const OSSL_DISPATCH ossl_kdf_tls1_3_kdf_functions[] = { (void(*)(void))kdf_tls1_3_settable_ctx_params }, { OSSL_FUNC_KDF_SET_CTX_PARAMS, (void(*)(void))kdf_tls1_3_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 } + (void(*)(void))kdf_tls1_3_gettable_ctx_params }, + { OSSL_FUNC_KDF_GET_CTX_PARAMS, (void(*)(void))kdf_tls1_3_get_ctx_params }, + OSSL_DISPATCH_END }; |