From: Dmitry Belyavskiy Date: Fri, 10 Jan 2025 11:40:25 +0000 (+0100) Subject: Implement EVP_KDF_derive_SKEY X-Git-Tag: openssl-3.6.0-alpha1~7 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=b5d0d061d1c5468bb45bcf3495a1b9d8d3943264;p=thirdparty%2Fopenssl.git Implement EVP_KDF_derive_SKEY Signed-off-by: Dmitry Belyavskiy 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 1093aac29e9..2eb0eb1a6e9 100644 --- a/crypto/evp/kdf_lib.c +++ b/crypto/evp/kdf_lib.c @@ -144,6 +144,89 @@ 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[]) +{ + EVP_SKEYMGMT *skeymgmt = NULL; + EVP_SKEY *ret = NULL; + + if (ctx == NULL || key_type == NULL) { + ERR_raise(ERR_LIB_EVP, ERR_R_PASSED_NULL_PARAMETER); + return NULL; + } + + if (mgmt != NULL) { + skeymgmt = mgmt; + } else { + skeymgmt = evp_skeymgmt_fetch_from_prov(ctx->meth->prov, + key_type, propquery); + if (skeymgmt == NULL) { + /* + * The provider does not support skeymgmt, let's try to fallback + * to a provider that supports it + */ + skeymgmt = EVP_SKEYMGMT_fetch(ossl_provider_libctx(ctx->meth->prov), + key_type, propquery); + } + + if (skeymgmt == NULL) { + ERR_raise(ERR_LIB_EVP, ERR_R_FETCH_FAILED); + return NULL; + } + } + + /* Fallback to raw derive + import if necessary */ + if (skeymgmt->prov != ctx->meth->prov || + ctx->meth->derive_skey == NULL) { + unsigned char *key = NULL; + OSSL_PARAM import_params[2] = {OSSL_PARAM_END, OSSL_PARAM_END}; + + if (ctx->meth->derive == NULL) { + ERR_raise(ERR_R_EVP_LIB, ERR_R_UNSUPPORTED); + return NULL; + } + + key = OPENSSL_zalloc(keylen); + if (!key) + return NULL; + + if (!ctx->meth->derive(ctx->algctx, key, keylen, params)) { + OPENSSL_free(key); + return NULL; + } + import_params[0] = OSSL_PARAM_construct_octet_string(OSSL_SKEY_PARAM_RAW_BYTES, + key, keylen); + + ret = EVP_SKEY_import_SKEYMGMT(ossl_provider_libctx(ctx->meth->prov), skeymgmt, + OSSL_SKEYMGMT_SELECT_SECRET_KEY, import_params); + + if (mgmt != skeymgmt) + EVP_SKEYMGMT_free(skeymgmt); + + OPENSSL_clear_free(key, keylen); + return ret; + } + + ret = evp_skey_alloc(skeymgmt); + if (ret == NULL) { + if (mgmt != skeymgmt) + EVP_SKEYMGMT_free(skeymgmt); + return NULL; + } + + ret->keydata = ctx->meth->derive_skey(ctx->algctx, ossl_provider_ctx(skeymgmt->prov), + skeymgmt->import, keylen, params); + if (ret->keydata == NULL) { + EVP_SKEY_free(ret); + ret = NULL; + } + + if (mgmt != skeymgmt) + EVP_SKEYMGMT_free(skeymgmt); + return ret; +} + /* * The {get,set}_params functions return 1 if there is no corresponding * function in the implementation. This is the same as if there was one, diff --git a/crypto/evp/kdf_meth.c b/crypto/evp/kdf_meth.c index ad02b3381fa..233fdf25e0a 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_DERIVE_SKEY: + if (kdf->derive_skey != NULL) + break; + kdf->derive_skey = OSSL_FUNC_kdf_derive_skey(fns); + break; } } if (fnkdfcnt != 1 || fnctxcnt != 2) { diff --git a/doc/man3/EVP_KDF.pod b/doc/man3/EVP_KDF.pod index 1a5bbb1fcc3..3c87668760f 100644 --- a/doc/man3/EVP_KDF.pod +++ b/doc/man3/EVP_KDF.pod @@ -4,7 +4,7 @@ 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_CTX_reset, EVP_KDF_derive, 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, @@ -28,6 +28,10 @@ EVP_KDF_CTX_gettable_params, EVP_KDF_CTX_settable_params - EVP KDF routines 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[]); + 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_up_ref(EVP_KDF *kdf); void EVP_KDF_free(EVP_KDF *kdf); EVP_KDF *EVP_KDF_fetch(OSSL_LIB_CTX *libctx, const char *algorithm, @@ -58,10 +62,10 @@ The EVP KDF routines are a high-level interface to Key Derivation Function 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() call or using calls -to EVP_KDF_CTX_set_params() before calling EVP_KDF_derive() to derive -the key. +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. =head2 Types @@ -108,6 +112,14 @@ If the algorithm produces a fixed amount of output then an error will occur unless the I parameter is equal to that output size, as returned by EVP_KDF_CTX_get_kdf_size(). +EVP_KDF_derive_SKEY() behaves similarly except it returns an B +object. If the I argument is explicitly passed, it will be used for +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. + EVP_KDF_get_params() retrieves details about the implementation I. The set of parameters given with I determine exactly what @@ -283,6 +295,8 @@ EVP_KDF_get0_name() returns the name of the KDF, or NULL on error. EVP_KDF_names_do_all() returns 1 if the callback was called for all names. A return value of 0 means that the callback was not called for any names. +EVP_KDF_derive_SKEY() returns the B object on success or NULL on failure. + The remaining functions return 1 for success and 0 for failure. =head1 NOTES @@ -300,6 +314,9 @@ L. This functionality was added in OpenSSL 3.0. +EVP_KDF_derive_SKEY() and EVP_KDF_CTX_set_SKEY() functions were introduced in +OpenSSL 3.6. + =head1 COPYRIGHT Copyright 2019-2024 The OpenSSL Project Authors. All Rights Reserved. diff --git a/doc/man7/provider-kdf.pod b/doc/man7/provider-kdf.pod index 4221f9a0f40..4d842349af4 100644 --- a/doc/man7/provider-kdf.pod +++ b/doc/man7/provider-kdf.pod @@ -26,6 +26,9 @@ provider-kdf - The KDF library E-E provider functions int OSSL_FUNC_kdf_reset(void *kctx); int OSSL_FUNC_kdf_derive(void *kctx, unsigned char *key, size_t keylen, const OSSL_PARAM params[]); + void *OSSL_FUNC_kdf_derive_skey(void *ctx, void *provctx, + OSSL_FUNC_skeymgmt_import_fn *import, + size_t keylen, const OSSL_PARAM params[]); /* KDF parameter descriptors */ const OSSL_PARAM *OSSL_FUNC_kdf_gettable_params(void *provctx); @@ -71,6 +74,7 @@ macros in L, as follows: OSSL_FUNC_kdf_reset OSSL_FUNC_KDF_RESET OSSL_FUNC_kdf_derive OSSL_FUNC_KDF_DERIVE + OSSL_FUNC_kdf_derive_skey OSSL_FUNC_KDF_DERIVE_SKEY OSSL_FUNC_kdf_get_params OSSL_FUNC_KDF_GET_PARAMS OSSL_FUNC_kdf_get_ctx_params OSSL_FUNC_KDF_GET_CTX_PARAMS @@ -83,7 +87,8 @@ macros in L, as follows: 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 must be implemented: OSSL_FUNC_kdf_newctx(), OSSL_FUNC_kdf_freectx(), -OSSL_FUNC_kdf_set_ctx_params(), OSSL_FUNC_kdf_derive(). +OSSL_FUNC_kdf_set_ctx_params(), and at least one of +OSSL_FUNC_kdf_derive() or OSSL_FUNC_kdf_derive_skey(). All other functions are optional. =head2 Context Management Functions @@ -116,6 +121,9 @@ The resulting key of the desired I should be written to I. If the algorithm does not support the requested I the function must return error. +OSSL_FUNC_kdf_derive_skey() is similar to OSSL_FUNC_kdf_derive() but uses an +opaque object for storing the derived key. + =head2 KDF Parameters See L for further details on the parameters structure used by @@ -325,7 +333,7 @@ It is defined as per RFC 7292 section B.3. OSSL_FUNC_kdf_newctx() and OSSL_FUNC_kdf_dupctx() should return the newly created provider side KDF context, or NULL on failure. -OSSL_FUNC_kdf_derive(), OSSL_FUNC_kdf_get_params(), +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. @@ -347,6 +355,8 @@ L, L, L. The provider KDF interface was introduced in OpenSSL 3.0. +OSSL_FUNC_kdf_derive_skey() and OSSL_FUNC_kdf_set_skey() were added in OpenSSL 3.6. + =head1 COPYRIGHT Copyright 2020-2022 The OpenSSL Project Authors. All Rights Reserved. diff --git a/include/crypto/evp.h b/include/crypto/evp.h index 0a833f171f1..24158eee985 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_derive_skey_fn *derive_skey; }; #define EVP_ORIG_DYNAMIC 0 diff --git a/include/openssl/core_dispatch.h b/include/openssl/core_dispatch.h index 13de04e2622..cc152ceeb72 100644 --- a/include/openssl/core_dispatch.h +++ b/include/openssl/core_dispatch.h @@ -503,6 +503,52 @@ OSSL_CORE_MAKE_FUNC(int, mac_set_ctx_params, (void *mctx, const OSSL_PARAM params[])) OSSL_CORE_MAKE_FUNC(int, mac_init_skey, (void *mctx, void *key, const OSSL_PARAM params[])) +/*- + * Symmetric key management + * + * The Key Management takes care of provider side of symmetric key objects, and + * includes essentially everything that manipulates the keys themselves and + * their parameters. + * + * The key objects are commonly referred to as |keydata|, and it MUST be able + * to contain parameters if the key has any, and the secret key. + * + * Key objects are created with OSSL_FUNC_skeymgmt_import() (there is no + * dedicated memory allocation function), exported with + * OSSL_FUNC_skeymgmt_export() and destroyed with OSSL_FUNC_keymgmt_free(). + * + */ + +/* Key data subset selection - individual bits */ +# define OSSL_SKEYMGMT_SELECT_PARAMETERS 0x01 +# define OSSL_SKEYMGMT_SELECT_SECRET_KEY 0x02 + +/* Key data subset selection - combinations */ +# define OSSL_SKEYMGMT_SELECT_ALL \ + (OSSL_SKEYMGMT_SELECT_PARAMETERS | OSSL_SKEYMGMT_SELECT_SECRET_KEY) + +# define OSSL_FUNC_SKEYMGMT_FREE 1 +# define OSSL_FUNC_SKEYMGMT_IMPORT 2 +# define OSSL_FUNC_SKEYMGMT_EXPORT 3 +# define OSSL_FUNC_SKEYMGMT_GENERATE 4 +# define OSSL_FUNC_SKEYMGMT_GET_KEY_ID 5 +# define OSSL_FUNC_SKEYMGMT_IMP_SETTABLE_PARAMS 6 +# define OSSL_FUNC_SKEYMGMT_GEN_SETTABLE_PARAMS 7 + +OSSL_CORE_MAKE_FUNC(void, skeymgmt_free, (void *keydata)) +OSSL_CORE_MAKE_FUNC(const OSSL_PARAM *, + skeymgmt_imp_settable_params, (void *provctx)) +OSSL_CORE_MAKE_FUNC(void *, skeymgmt_import, (void *provctx, int selection, + const OSSL_PARAM params[])) +OSSL_CORE_MAKE_FUNC(int, skeymgmt_export, + (void *keydata, int selection, + OSSL_CALLBACK *param_cb, void *cbarg)) +OSSL_CORE_MAKE_FUNC(const OSSL_PARAM *, + skeymgmt_gen_settable_params, (void *provctx)) +OSSL_CORE_MAKE_FUNC(void *, skeymgmt_generate, (void *provctx, + const OSSL_PARAM params[])) +OSSL_CORE_MAKE_FUNC(const char *, skeymgmt_get_key_id, (void *keydata)) + /* KDFs and PRFs */ # define OSSL_FUNC_KDF_NEWCTX 1 @@ -516,6 +562,7 @@ OSSL_CORE_MAKE_FUNC(int, mac_init_skey, (void *mctx, void *key, const OSSL_PARAM # 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 OSSL_CORE_MAKE_FUNC(void *, kdf_newctx, (void *provctx)) OSSL_CORE_MAKE_FUNC(void *, kdf_dupctx, (void *src)) @@ -533,6 +580,11 @@ OSSL_CORE_MAKE_FUNC(int, kdf_get_ctx_params, (void *kctx, OSSL_PARAM params[])) OSSL_CORE_MAKE_FUNC(int, kdf_set_ctx_params, (void *kctx, const OSSL_PARAM params[])) +OSSL_CORE_MAKE_FUNC(int, kdf_set_skey, + (void *kctx, void *skeydata, const char *paramname)) +OSSL_CORE_MAKE_FUNC(void *, kdf_derive_skey, (void *ctx, void *provctx, + OSSL_FUNC_skeymgmt_import_fn *import, + size_t keylen, const OSSL_PARAM params[])) /* RAND */ diff --git a/include/openssl/kdf.h b/include/openssl/kdf.h index 0983230a488..4a320007951 100644 --- a/include/openssl/kdf.h +++ b/include/openssl/kdf.h @@ -43,6 +43,9 @@ 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_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/test/evp_kdf_test.c b/test/evp_kdf_test.c index 227b2974d47..9e67641a66a 100644 --- a/test/evp_kdf_test.c +++ b/test/evp_kdf_test.c @@ -80,6 +80,67 @@ static int test_kdf_tls1_prf(void) return ret; } +static int test_kdf_tls1_prf_set_skey(void) +{ + int ret; + EVP_KDF_CTX *kctx = NULL; + unsigned char out[16]; + OSSL_PARAM params[3] = {OSSL_PARAM_END, OSSL_PARAM_END, OSSL_PARAM_END}; + OSSL_PARAM *p = params; + static const unsigned char expected[sizeof(out)] = { + 0x8e, 0x4d, 0x93, 0x25, 0x30, 0xd7, 0x65, 0xa0, + 0xaa, 0xe9, 0x74, 0xc3, 0x04, 0x73, 0x5e, 0xcc + }; + EVP_SKEY *skey = NULL; + + *p++ = OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_DIGEST, + "sha256", 0); + *p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_SECRET, + "secret", 6); + skey = EVP_SKEY_import_raw_key(NULL, OSSL_SKEY_TYPE_GENERIC, + (unsigned char *)"seed", 4, NULL); + if (skey == NULL) + return 0; + + ret = TEST_ptr(kctx = get_kdfbyname(OSSL_KDF_NAME_TLS1_PRF)) + && TEST_int_gt(EVP_KDF_CTX_set_SKEY(kctx, skey, OSSL_KDF_PARAM_SEED), 0) + && TEST_int_gt(EVP_KDF_derive(kctx, out, sizeof(out), params), 0) + && TEST_mem_eq(out, sizeof(out), expected, sizeof(expected)); + + EVP_KDF_CTX_free(kctx); + EVP_SKEY_free(skey); + return ret; +} + +static int test_kdf_tls1_prf_derive_skey(void) +{ + int ret; + EVP_KDF_CTX *kctx = NULL; + unsigned char out[16]; + OSSL_PARAM *params; + static const unsigned char expected[sizeof(out)] = { + 0x8e, 0x4d, 0x93, 0x25, 0x30, 0xd7, 0x65, 0xa0, + 0xaa, 0xe9, 0x74, 0xc3, 0x04, 0x73, 0x5e, 0xcc + }; + const unsigned char *export = NULL; + size_t export_size = 0; + EVP_SKEY *skey = NULL; + + params = construct_tls1_prf_params("sha256", "secret", "seed"); + + ret = TEST_ptr(params) + && TEST_ptr(kctx = get_kdfbyname(OSSL_KDF_NAME_TLS1_PRF)) + && TEST_ptr(skey = EVP_KDF_derive_SKEY(kctx, NULL, OSSL_SKEY_TYPE_GENERIC, + NULL, sizeof(expected), params)) + && TEST_int_eq(EVP_SKEY_get0_raw_key(skey, &export, &export_size), 1) + && TEST_mem_eq(export, export_size, expected, sizeof(expected)); + + EVP_SKEY_free(skey); + EVP_KDF_CTX_free(kctx); + OPENSSL_free(params); + return ret; +} + static int test_kdf_tls1_prf_invalid_digest(void) { int ret; @@ -2241,6 +2302,8 @@ int setup_tests(void) ADD_TEST(test_kdf_kbkdf_kmac); ADD_TEST(test_kdf_get_kdf); ADD_TEST(test_kdf_tls1_prf); + ADD_TEST(test_kdf_tls1_prf_set_skey); + ADD_TEST(test_kdf_tls1_prf_derive_skey); ADD_TEST(test_kdf_tls1_prf_invalid_digest); ADD_TEST(test_kdf_tls1_prf_zero_output_size); ADD_TEST(test_kdf_tls1_prf_empty_secret);