]> git.ipfire.org Git - thirdparty/openssl.git/commitdiff
Implement EVP_KDF_derive_SKEY
authorDmitry Belyavskiy <beldmit@gmail.com>
Fri, 10 Jan 2025 11:40:25 +0000 (12:40 +0100)
committerNeil Horman <nhorman@openssl.org>
Sun, 31 Aug 2025 01:11:06 +0000 (21:11 -0400)
Signed-off-by: Dmitry Belyavskiy <beldmit@gmail.com>
Signed-off-by: Simo Sorce <simo@redhat.com>
Reviewed-by: Tim Hudson <tjh@openssl.org>
Reviewed-by: Matt Caswell <matt@openssl.org>
Reviewed-by: Neil Horman <nhorman@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/28369)

crypto/evp/kdf_lib.c
crypto/evp/kdf_meth.c
doc/man3/EVP_KDF.pod
doc/man7/provider-kdf.pod
include/crypto/evp.h
include/openssl/core_dispatch.h
include/openssl/kdf.h
test/evp_kdf_test.c

index 1093aac29e91d85d1f188497d396d6052db0216f..2eb0eb1a6e91b9ea9dfa8a62dad82e3be6b7f01f 100644 (file)
@@ -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,
index ad02b3381fa4a49c9159ca8d6188c834ca17fc37..233fdf25e0ad4b63965e30cbb4e5b89839c98ee2 100644 (file)
@@ -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) {
index 1a5bbb1fcc37c39733398031924f5bf46b072d27..3c87668760f205b0ff8b1b05eb63463791331bbf 100644 (file)
@@ -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<EVP_KDF_CTX> 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<keylen> 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<EVP_SKEY>
+object.  If the I<skeymgmt> argument is explicitly passed, it will be used for
+the generated key. If the I<skeymgmt> argument is NULL, the I<key_type> and
+I<propquery> will be used for fetching the B<EVP_SKEYMGMT> object linked to the
+key.  If the KDF doesn't support dealing with opaque keys or the passed
+B<EVP_SKEYMGMT> is from a different provider than the KDF method, the operation
+will fail.
+
 EVP_KDF_get_params() retrieves details about the implementation
 I<kdf>.
 The set of parameters given with I<params> 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<EVP_SKEY> 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<life_cycle-kdf(7)>.
 
 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.
index 4221f9a0f401c98282a9db1ce18484569d3e2269..4d842349af4b4bd434e14e2f8b0ad16bd0762b68 100644 (file)
@@ -26,6 +26,9 @@ provider-kdf - The KDF library E<lt>-E<gt> 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<openssl-core_dispatch.h(7)>, 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<openssl-core_dispatch.h(7)>, 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<keylen> should be written to I<key>.
 If the algorithm does not support the requested I<keylen> 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<OSSL_PARAM(3)> 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<provider(7)>, L<life_cycle-kdf(7)>, L<EVP_KDF(3)>.
 
 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.
index 0a833f171f1b377bf9b7bf603f7a830122f6b89f..24158eee985d2f1842011cf5c7369c8654c669fc 100644 (file)
@@ -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
index 13de04e2622c49aa4257ee184f314c528461a921..cc152ceeb727b5a8b2917d8a595e68686f8c1380 100644 (file)
@@ -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 */
 
index 0983230a488afe30d65e317374b9520ec8cfb9ca..4a320007951125b522863eb1f2edc21b80b7c4fb 100644 (file)
@@ -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[]);
index 227b2974d47e03d6df40109e88787d3feeaf6255..9e67641a66af70fa8111c01558b4e87455ddda18 100644 (file)
@@ -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);