From: Simo Sorce Date: Fri, 10 Jan 2025 22:20:59 +0000 (-0500) Subject: Implement EVP_KDF_CTX_set_SKEY X-Git-Tag: openssl-3.6.0-alpha1~5 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=55b2bf1abc38d851357b486a9db951f3c0793c4d;p=thirdparty%2Fopenssl.git Implement EVP_KDF_CTX_set_SKEY This allows to use a SKEY as input to a KDF derive operation. Signed-off-by: Simo Sorce Reviewed-by: Tim Hudson Reviewed-by: Matt Caswell Reviewed-by: Neil Horman (Merged from https://github.com/openssl/openssl/pull/28369) --- diff --git a/crypto/evp/kdf_lib.c b/crypto/evp/kdf_lib.c index 2eb0eb1a6e9..bd9564d3851 100644 --- a/crypto/evp/kdf_lib.c +++ b/crypto/evp/kdf_lib.c @@ -19,6 +19,7 @@ #include "internal/numbers.h" #include "internal/provider.h" #include "evp_local.h" +#include "internal/param_build_set.h" EVP_KDF_CTX *EVP_KDF_CTX_new(EVP_KDF *kdf) { @@ -144,9 +145,64 @@ int EVP_KDF_derive(EVP_KDF_CTX *ctx, unsigned char *key, size_t keylen, return ctx->meth->derive(ctx->algctx, key, keylen, params); } -EVP_SKEY *EVP_KDF_derive_SKEY(EVP_KDF_CTX *ctx, const char *key_type, - size_t keylen, const char *propquery, - const OSSL_PARAM params[]) +struct convert_key { + const char *name; + OSSL_PARAM *param; +}; + +static int convert_key_cb(const OSSL_PARAM params[], void *arg) +{ + struct convert_key *ckey = arg; + const OSSL_PARAM *raw_bytes; + unsigned char *data; + size_t len; + + raw_bytes = OSSL_PARAM_locate_const(params, OSSL_SKEY_PARAM_RAW_BYTES); + if (raw_bytes == NULL) + return 0; + + if (!OSSL_PARAM_get_octet_string_ptr(raw_bytes, (const void **)&data, &len)) + return 0; + + *ckey->param = OSSL_PARAM_construct_octet_string(ckey->name, data, len); + return 1; +} + +int EVP_KDF_CTX_set_SKEY(EVP_KDF_CTX *ctx, EVP_SKEY *key, const char *paramname) +{ + struct convert_key ckey; + OSSL_PARAM params[2] = { + OSSL_PARAM_END, + OSSL_PARAM_END, + }; + + if (ctx == NULL) + return 0; + + ckey.name = (paramname != NULL) ? paramname : OSSL_KDF_PARAM_KEY; + + if (ctx->meth->set_skey != NULL && ctx->meth->prov == key->skeymgmt->prov) + return ctx->meth->set_skey(ctx->algctx, key->keydata, ckey.name); + + /* + * We can't use the opaque key directly. + * Let's try to export it and set the ctx params in a traditional manner. + */ + ckey.param = ¶ms[0]; + + if (!ctx->meth->set_ctx_params) + return 0; + + if (EVP_SKEY_export(key, OSSL_SKEYMGMT_SELECT_SECRET_KEY, + convert_key_cb, &ckey)) + return ctx->meth->set_ctx_params(ctx->algctx, params); + + return 0; +} + +EVP_SKEY *EVP_KDF_derive_SKEY(EVP_KDF_CTX *ctx, EVP_SKEYMGMT *mgmt, + const char *key_type, const char *propquery, + size_t keylen, const OSSL_PARAM params[]) { EVP_SKEYMGMT *skeymgmt = NULL; EVP_SKEY *ret = NULL; diff --git a/crypto/evp/kdf_meth.c b/crypto/evp/kdf_meth.c index 233fdf25e0a..65c875f2cc9 100644 --- a/crypto/evp/kdf_meth.c +++ b/crypto/evp/kdf_meth.c @@ -136,6 +136,11 @@ static void *evp_kdf_from_algorithm(int name_id, break; kdf->set_ctx_params = OSSL_FUNC_kdf_set_ctx_params(fns); break; + case OSSL_FUNC_KDF_SET_SKEY: + if (kdf->set_skey != NULL) + break; + kdf->set_skey = OSSL_FUNC_kdf_set_skey(fns); + break; case OSSL_FUNC_KDF_DERIVE_SKEY: if (kdf->derive_skey != NULL) break; diff --git a/doc/man3/EVP_KDF.pod b/doc/man3/EVP_KDF.pod index 3c87668760f..0c597b38d14 100644 --- a/doc/man3/EVP_KDF.pod +++ b/doc/man3/EVP_KDF.pod @@ -4,7 +4,8 @@ EVP_KDF, EVP_KDF_fetch, EVP_KDF_free, EVP_KDF_up_ref, EVP_KDF_CTX, EVP_KDF_CTX_new, EVP_KDF_CTX_free, EVP_KDF_CTX_dup, -EVP_KDF_CTX_reset, EVP_KDF_derive, EVP_KDF_derive_SKEY, +EVP_KDF_CTX_reset, EVP_KDF_derive, +EVP_KDF_CTX_set_SKEY, EVP_KDF_derive_SKEY, EVP_KDF_CTX_get_kdf_size, EVP_KDF_get0_provider, EVP_KDF_CTX_kdf, EVP_KDF_is_a, EVP_KDF_get0_name, EVP_KDF_names_do_all, EVP_KDF_get0_description, @@ -64,8 +65,8 @@ algorithms and should be used instead of algorithm-specific functions. After creating a B for the required algorithm using EVP_KDF_CTX_new(), inputs to the algorithm are supplied either by passing them as part of the EVP_KDF_derive() or EVP_KDF_derive_SKEY() call or using calls to -EVP_KDF_CTX_set_params() before calling EVP_KDF_derive() or -EVP_KDF_derive_SKEY() to derive the key. +EVP_KDF_CTX_set_params() or EVP_KDF_CTX_set_SKEY() before calling +EVP_KDF_derive() or EVP_KDF_derive_SKEY() to derive the key. =head2 Types @@ -118,7 +119,13 @@ the generated key. If the I argument is NULL, the I and I will be used for fetching the B object linked to the key. If the KDF doesn't support dealing with opaque keys or the passed B is from a different provider than the KDF method, the operation -will fail. +will try to perform export/import through the raw format. + +EVP_KDF_CTX_set_SKEY() allows setting an skey object as the input parameter for +the derivation functions. As different KDFs may accept several parameters as +key names, we can specify the particular parameter name we set. If null is +passed, the parameter B is implied. It is used when the +input key is represented by B rather than raw bytes. EVP_KDF_get_params() retrieves details about the implementation I. diff --git a/doc/man7/provider-kdf.pod b/doc/man7/provider-kdf.pod index 4d842349af4..51364d33f87 100644 --- a/doc/man7/provider-kdf.pod +++ b/doc/man7/provider-kdf.pod @@ -39,6 +39,7 @@ provider-kdf - The KDF library E-E provider functions int OSSL_FUNC_kdf_get_params(OSSL_PARAM params[]); int OSSL_FUNC_kdf_get_ctx_params(void *kctx, OSSL_PARAM params[]); int OSSL_FUNC_kdf_set_ctx_params(void *kctx, const OSSL_PARAM params[]); + int OSSL_FUNC_kdf_set_skey(void *kctx, void *skeydata, const char *paramname); =head1 DESCRIPTION @@ -83,6 +84,7 @@ macros in L, as follows: OSSL_FUNC_kdf_gettable_params OSSL_FUNC_KDF_GETTABLE_PARAMS OSSL_FUNC_kdf_gettable_ctx_params OSSL_FUNC_KDF_GETTABLE_CTX_PARAMS OSSL_FUNC_kdf_settable_ctx_params OSSL_FUNC_KDF_SETTABLE_CTX_PARAMS + OSSL_FUNC_kdf_set_skey OSSL_FUNC_KDF_SET_SKEY A KDF algorithm implementation may not implement all of these functions. In order to be a consistent set of functions, at least the following functions @@ -151,6 +153,8 @@ with the provider side context I in its current state if it is not NULL. Otherwise, they return the parameters associated with the provider side algorithm I. +OSSL_FUNF_kdf_set_skey() sets the input key for KDF functions that take a +key as input. Parameters currently recognised by built-in KDFs are as follows. Not all parameters are relevant to, or are understood by all KDFs: @@ -334,8 +338,8 @@ OSSL_FUNC_kdf_newctx() and OSSL_FUNC_kdf_dupctx() should return the newly create provider side KDF context, or NULL on failure. OSSL_FUNC_kdf_derive(), OSSL_FUNC_kdf_derive_skey(), OSSL_FUNC_kdf_get_params(), -OSSL_FUNC_kdf_get_ctx_params() and OSSL_FUNC_kdf_set_ctx_params() should return 1 for -success or 0 on error. +OSSL_FUNC_kdf_get_ctx_params(), OSSL_FUNC_kdf_set_ctx_params() and +OSSL_FUNC_kdf_set_skey() should return 1 for success or 0 on error. OSSL_FUNC_kdf_gettable_params(), OSSL_FUNC_kdf_gettable_ctx_params() and OSSL_FUNC_kdf_settable_ctx_params() should return a constant L diff --git a/include/crypto/evp.h b/include/crypto/evp.h index 24158eee985..ed867f77fbc 100644 --- a/include/crypto/evp.h +++ b/include/crypto/evp.h @@ -247,6 +247,7 @@ struct evp_kdf_st { OSSL_FUNC_kdf_get_params_fn *get_params; OSSL_FUNC_kdf_get_ctx_params_fn *get_ctx_params; OSSL_FUNC_kdf_set_ctx_params_fn *set_ctx_params; + OSSL_FUNC_kdf_set_skey_fn *set_skey; OSSL_FUNC_kdf_derive_skey_fn *derive_skey; }; diff --git a/include/openssl/core_dispatch.h b/include/openssl/core_dispatch.h index 41c42124826..d535d6b5375 100644 --- a/include/openssl/core_dispatch.h +++ b/include/openssl/core_dispatch.h @@ -562,7 +562,8 @@ OSSL_CORE_MAKE_FUNC(const char *, skeymgmt_get_key_id, (void *keydata)) # define OSSL_FUNC_KDF_GET_PARAMS 9 # define OSSL_FUNC_KDF_GET_CTX_PARAMS 10 # define OSSL_FUNC_KDF_SET_CTX_PARAMS 11 -# define OSSL_FUNC_KDF_DERIVE_SKEY 12 +# define OSSL_FUNC_KDF_SET_SKEY 12 +# define OSSL_FUNC_KDF_DERIVE_SKEY 13 OSSL_CORE_MAKE_FUNC(void *, kdf_newctx, (void *provctx)) OSSL_CORE_MAKE_FUNC(void *, kdf_dupctx, (void *src)) diff --git a/include/openssl/kdf.h b/include/openssl/kdf.h index 4a320007951..8fb2c63f750 100644 --- a/include/openssl/kdf.h +++ b/include/openssl/kdf.h @@ -43,9 +43,10 @@ void EVP_KDF_CTX_reset(EVP_KDF_CTX *ctx); size_t EVP_KDF_CTX_get_kdf_size(EVP_KDF_CTX *ctx); int EVP_KDF_derive(EVP_KDF_CTX *ctx, unsigned char *key, size_t keylen, const OSSL_PARAM params[]); -EVP_SKEY *EVP_KDF_derive_SKEY(EVP_KDF_CTX *ctx, const char *key_type, - size_t keylen, const char *propquery, - const OSSL_PARAM params[]); +int EVP_KDF_CTX_set_SKEY(EVP_KDF_CTX *ctx, EVP_SKEY *key, const char *paramname); +EVP_SKEY *EVP_KDF_derive_SKEY(EVP_KDF_CTX *ctx, EVP_SKEYMGMT *mgmt, + const char *key_type, const char *propquery, + size_t keylen, const OSSL_PARAM params[]); int EVP_KDF_get_params(EVP_KDF *kdf, OSSL_PARAM params[]); int EVP_KDF_CTX_get_params(EVP_KDF_CTX *ctx, OSSL_PARAM params[]); int EVP_KDF_CTX_set_params(EVP_KDF_CTX *ctx, const OSSL_PARAM params[]); diff --git a/providers/implementations/kdfs/pbkdf1.c.in b/providers/implementations/kdfs/pbkdf1.c.in index 9602c7d6bc2..be9a17514cd 100644 --- a/providers/implementations/kdfs/pbkdf1.c.in +++ b/providers/implementations/kdfs/pbkdf1.c.in @@ -21,7 +21,9 @@ use OpenSSL::paramnames qw(produce_param_decoder); #include "internal/common.h" #include "internal/cryptlib.h" #include "internal/numbers.h" +#include "internal/skey.h" #include "crypto/evp.h" +#include "crypto/types.h" #include "prov/provider_ctx.h" #include "prov/providercommon.h" #include "prov/implementations.h" @@ -36,6 +38,8 @@ static OSSL_FUNC_kdf_settable_ctx_params_fn kdf_pbkdf1_settable_ctx_params; static OSSL_FUNC_kdf_set_ctx_params_fn kdf_pbkdf1_set_ctx_params; static OSSL_FUNC_kdf_gettable_ctx_params_fn kdf_pbkdf1_gettable_ctx_params; static OSSL_FUNC_kdf_get_ctx_params_fn kdf_pbkdf1_get_ctx_params; +static OSSL_FUNC_kdf_set_skey_fn kdf_pbkdf1_set_skey; +static OSSL_FUNC_kdf_derive_skey_fn kdf_pbkdf1_derive_skey; typedef struct { void *provctx; @@ -178,6 +182,27 @@ static int kdf_pbkdf1_set_membuf(unsigned char **buffer, size_t *buflen, return 1; } +static int kdf_pbkdf1_set_membuf_skey(unsigned char **buffer, size_t *buflen, + const PROV_SKEY *pskey) +{ + OPENSSL_clear_free(*buffer, *buflen); + *buffer = NULL; + *buflen = 0; + + if (pskey->length == 0) { + if ((*buffer = OPENSSL_malloc(1)) == NULL) + return 0; + } else if (pskey->data != NULL) { + *buffer = OPENSSL_malloc(pskey->length); + if (*buffer == NULL) + return 0; + + memcpy(*buffer, pskey->data, pskey->length); + *buflen = pskey->length; + } + return 1; +} + static int kdf_pbkdf1_derive(void *vctx, unsigned char *key, size_t keylen, const OSSL_PARAM params[]) { @@ -266,6 +291,51 @@ static const OSSL_PARAM *kdf_pbkdf1_gettable_ctx_params(ossl_unused void *ctx, return pbkdf1_get_ctx_params_list; } +static int kdf_pbkdf1_set_skey(void *vctx, void *skeydata, const char *paramname) +{ + KDF_PBKDF1 *ctx = (KDF_PBKDF1 *)vctx; + PROV_SKEY *pskey = (PROV_SKEY *)skeydata; + + if (paramname == NULL || skeydata == NULL) + return 0; + + if (strcmp(paramname, OSSL_KDF_PARAM_PASSWORD) == 0) + return kdf_pbkdf1_set_membuf_skey(&ctx->pass, &ctx->pass_len, pskey); + + if (strcmp(paramname, OSSL_KDF_PARAM_SALT) == 0) + return kdf_pbkdf1_set_membuf_skey(&ctx->salt, &ctx->salt_len, pskey); + + return 0; +} + +static +void *kdf_pbkdf1_derive_skey(void *vctx, void *provctx, OSSL_FUNC_skeymgmt_import_fn *import, + size_t keylen, const OSSL_PARAM params[]) +{ + unsigned char *key = NULL; + void *ret = NULL; + OSSL_PARAM import_params[2] = {OSSL_PARAM_END, OSSL_PARAM_END}; + + if (import == NULL || keylen == 0) + return NULL; + + if ((key = OPENSSL_zalloc(keylen)) == NULL) + return NULL; + + if (kdf_pbkdf1_derive(vctx, key, keylen, params) == 0) { + OPENSSL_free(key); + return NULL; + } + + import_params[0] = OSSL_PARAM_construct_octet_string(OSSL_SKEY_PARAM_RAW_BYTES, + (void *)key, keylen); + + ret = import(provctx, OSSL_SKEYMGMT_SELECT_SECRET_KEY, import_params); + OPENSSL_free(key); + + return ret; +} + const OSSL_DISPATCH ossl_kdf_pbkdf1_functions[] = { { OSSL_FUNC_KDF_NEWCTX, (void(*)(void))kdf_pbkdf1_new }, { OSSL_FUNC_KDF_DUPCTX, (void(*)(void))kdf_pbkdf1_dup }, @@ -278,5 +348,7 @@ const OSSL_DISPATCH ossl_kdf_pbkdf1_functions[] = { { OSSL_FUNC_KDF_GETTABLE_CTX_PARAMS, (void(*)(void))kdf_pbkdf1_gettable_ctx_params }, { OSSL_FUNC_KDF_GET_CTX_PARAMS, (void(*)(void))kdf_pbkdf1_get_ctx_params }, + { OSSL_FUNC_KDF_SET_SKEY, (void(*)(void))kdf_pbkdf1_set_skey }, + { OSSL_FUNC_KDF_DERIVE_SKEY, (void(*)(void))kdf_pbkdf1_derive_skey }, OSSL_DISPATCH_END }; diff --git a/test/evp_kdf_test.c b/test/evp_kdf_test.c index 9e67641a66a..7a07168bc66 100644 --- a/test/evp_kdf_test.c +++ b/test/evp_kdf_test.c @@ -790,6 +790,67 @@ err: return ret; } +static int test_kdf_pbkdf1_skey(void) +{ + int ret = 0; + EVP_KDF_CTX *kctx = NULL; + unsigned char out[25]; + unsigned int iterations = 4096; + OSSL_LIB_CTX *libctx = NULL; + OSSL_PARAM *params = NULL; + OSSL_PROVIDER *legacyprov = NULL; + OSSL_PROVIDER *defprov = NULL; + EVP_SKEY *skey_in = NULL, *skey_out = NULL; + unsigned char salt[] = "saltSALTsaltSALTsaltSALTsaltSALTsalt"; + const unsigned char expected[sizeof(out)] = { + 0xfb, 0x83, 0x4d, 0x36, 0x6d, 0xbc, 0x53, 0x87, 0x35, 0x1b, 0x34, 0x75, + 0x95, 0x88, 0x32, 0x4f, 0x3e, 0x82, 0x81, 0x01, 0x21, 0x93, 0x64, 0x00, + 0xcc + }; + const unsigned char *export = NULL; + size_t export_size = 0; + + if (!TEST_ptr(libctx = OSSL_LIB_CTX_new())) + goto err; + + /* PBKDF1 only available in the legacy provider */ + legacyprov = OSSL_PROVIDER_load(libctx, "legacy"); + if (legacyprov == NULL) { + OSSL_LIB_CTX_free(libctx); + return TEST_skip("PBKDF1 only available in legacy provider"); + } + + if (!TEST_ptr(defprov = OSSL_PROVIDER_load(libctx, "default"))) + goto err; + + params = construct_pbkdf1_params("passwordPASSWORDpassword", "sha256", + "saltSALT", + &iterations); + + if (!TEST_ptr(params) + || !TEST_ptr(skey_in = EVP_SKEY_import_raw_key(libctx, OSSL_SKEY_TYPE_GENERIC, + salt, strlen((char *)salt), NULL)) + || !TEST_ptr(kctx = get_kdfbyname_libctx(libctx, OSSL_KDF_NAME_PBKDF1)) + || !TEST_true(EVP_KDF_CTX_set_params(kctx, params)) + || !TEST_true(EVP_KDF_CTX_set_SKEY(kctx, skey_in, OSSL_KDF_PARAM_SALT)) + || !TEST_ptr(skey_out = EVP_KDF_derive_SKEY(kctx, NULL, OSSL_SKEY_TYPE_GENERIC, + NULL, sizeof(out), NULL)) + || !TEST_int_eq(EVP_SKEY_get0_raw_key(skey_out, &export, &export_size), 1) + || !TEST_mem_eq(export, export_size, expected, sizeof(expected))) + goto err; + + ret = 1; +err: + EVP_SKEY_free(skey_in); + EVP_SKEY_free(skey_out); + EVP_KDF_CTX_free(kctx); + OPENSSL_free(params); + OSSL_PROVIDER_unload(defprov); + OSSL_PROVIDER_unload(legacyprov); + OSSL_LIB_CTX_free(libctx); + return ret; +} + static int test_kdf_pbkdf1_key_too_long(void) { int ret = 0; @@ -2282,6 +2343,7 @@ err: int setup_tests(void) { ADD_TEST(test_kdf_pbkdf1); + ADD_TEST(test_kdf_pbkdf1_skey); ADD_TEST(test_kdf_pbkdf1_key_too_long); #if !defined(OPENSSL_NO_CMAC) && !defined(OPENSSL_NO_CAMELLIA) ADD_TEST(test_kdf_kbkdf_6803_128);