1b077aed3SPierre Pronchery /*
2*e7be843bSPierre Pronchery * Copyright 2016-2025 The OpenSSL Project Authors. All Rights Reserved.
3b077aed3SPierre Pronchery *
4b077aed3SPierre Pronchery * Licensed under the Apache License 2.0 (the "License"). You may not use
5b077aed3SPierre Pronchery * this file except in compliance with the License. You can obtain a copy
6b077aed3SPierre Pronchery * in the file LICENSE in the source distribution or at
7b077aed3SPierre Pronchery * https://www.openssl.org/source/license.html
8b077aed3SPierre Pronchery */
9b077aed3SPierre Pronchery
10b077aed3SPierre Pronchery /*
11b077aed3SPierre Pronchery * HMAC low level APIs are deprecated for public use, but still ok for internal
12b077aed3SPierre Pronchery * use.
13b077aed3SPierre Pronchery */
14b077aed3SPierre Pronchery #include "internal/deprecated.h"
15b077aed3SPierre Pronchery
16b077aed3SPierre Pronchery #include <stdlib.h>
17b077aed3SPierre Pronchery #include <stdarg.h>
18b077aed3SPierre Pronchery #include <string.h>
19b077aed3SPierre Pronchery #include <openssl/hmac.h>
20b077aed3SPierre Pronchery #include <openssl/evp.h>
21b077aed3SPierre Pronchery #include <openssl/kdf.h>
22b077aed3SPierre Pronchery #include <openssl/core_names.h>
23b077aed3SPierre Pronchery #include <openssl/proverr.h>
24b077aed3SPierre Pronchery #include "internal/cryptlib.h"
25b077aed3SPierre Pronchery #include "internal/numbers.h"
26b077aed3SPierre Pronchery #include "internal/packet.h"
27b077aed3SPierre Pronchery #include "crypto/evp.h"
28b077aed3SPierre Pronchery #include "prov/provider_ctx.h"
29b077aed3SPierre Pronchery #include "prov/providercommon.h"
30b077aed3SPierre Pronchery #include "prov/implementations.h"
31b077aed3SPierre Pronchery #include "prov/provider_util.h"
32*e7be843bSPierre Pronchery #include "prov/securitycheck.h"
33*e7be843bSPierre Pronchery #include "internal/e_os.h"
34*e7be843bSPierre Pronchery #include "internal/params.h"
35b077aed3SPierre Pronchery
36b077aed3SPierre Pronchery #define HKDF_MAXBUF 2048
37b077aed3SPierre Pronchery #define HKDF_MAXINFO (32*1024)
38b077aed3SPierre Pronchery
39b077aed3SPierre Pronchery static OSSL_FUNC_kdf_newctx_fn kdf_hkdf_new;
40*e7be843bSPierre Pronchery static OSSL_FUNC_kdf_dupctx_fn kdf_hkdf_dup;
41b077aed3SPierre Pronchery static OSSL_FUNC_kdf_freectx_fn kdf_hkdf_free;
42b077aed3SPierre Pronchery static OSSL_FUNC_kdf_reset_fn kdf_hkdf_reset;
43b077aed3SPierre Pronchery static OSSL_FUNC_kdf_derive_fn kdf_hkdf_derive;
44b077aed3SPierre Pronchery static OSSL_FUNC_kdf_settable_ctx_params_fn kdf_hkdf_settable_ctx_params;
45b077aed3SPierre Pronchery static OSSL_FUNC_kdf_set_ctx_params_fn kdf_hkdf_set_ctx_params;
46b077aed3SPierre Pronchery static OSSL_FUNC_kdf_gettable_ctx_params_fn kdf_hkdf_gettable_ctx_params;
47b077aed3SPierre Pronchery static OSSL_FUNC_kdf_get_ctx_params_fn kdf_hkdf_get_ctx_params;
48b077aed3SPierre Pronchery static OSSL_FUNC_kdf_derive_fn kdf_tls1_3_derive;
49b077aed3SPierre Pronchery static OSSL_FUNC_kdf_settable_ctx_params_fn kdf_tls1_3_settable_ctx_params;
50b077aed3SPierre Pronchery static OSSL_FUNC_kdf_set_ctx_params_fn kdf_tls1_3_set_ctx_params;
51*e7be843bSPierre Pronchery static OSSL_FUNC_kdf_gettable_ctx_params_fn kdf_tls1_3_gettable_ctx_params;
52*e7be843bSPierre Pronchery static OSSL_FUNC_kdf_get_ctx_params_fn kdf_tls1_3_get_ctx_params;
53b077aed3SPierre Pronchery
54b077aed3SPierre Pronchery static int HKDF(OSSL_LIB_CTX *libctx, const EVP_MD *evp_md,
55b077aed3SPierre Pronchery const unsigned char *salt, size_t salt_len,
56b077aed3SPierre Pronchery const unsigned char *key, size_t key_len,
57b077aed3SPierre Pronchery const unsigned char *info, size_t info_len,
58b077aed3SPierre Pronchery unsigned char *okm, size_t okm_len);
59b077aed3SPierre Pronchery static int HKDF_Extract(OSSL_LIB_CTX *libctx, const EVP_MD *evp_md,
60b077aed3SPierre Pronchery const unsigned char *salt, size_t salt_len,
61b077aed3SPierre Pronchery const unsigned char *ikm, size_t ikm_len,
62b077aed3SPierre Pronchery unsigned char *prk, size_t prk_len);
63b077aed3SPierre Pronchery static int HKDF_Expand(const EVP_MD *evp_md,
64b077aed3SPierre Pronchery const unsigned char *prk, size_t prk_len,
65b077aed3SPierre Pronchery const unsigned char *info, size_t info_len,
66b077aed3SPierre Pronchery unsigned char *okm, size_t okm_len);
67b077aed3SPierre Pronchery
68b077aed3SPierre Pronchery /* Settable context parameters that are common across HKDF and the TLS KDF */
69b077aed3SPierre Pronchery #define HKDF_COMMON_SETTABLES \
70b077aed3SPierre Pronchery OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_MODE, NULL, 0), \
71b077aed3SPierre Pronchery OSSL_PARAM_int(OSSL_KDF_PARAM_MODE, NULL), \
72b077aed3SPierre Pronchery OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_PROPERTIES, NULL, 0), \
73b077aed3SPierre Pronchery OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_DIGEST, NULL, 0), \
74b077aed3SPierre Pronchery OSSL_PARAM_octet_string(OSSL_KDF_PARAM_KEY, NULL, 0), \
75b077aed3SPierre Pronchery OSSL_PARAM_octet_string(OSSL_KDF_PARAM_SALT, NULL, 0)
76b077aed3SPierre Pronchery
77*e7be843bSPierre Pronchery /* Gettable context parameters that are common across HKDF and the TLS KDF */
78*e7be843bSPierre Pronchery #define HKDF_COMMON_GETTABLES \
79*e7be843bSPierre Pronchery OSSL_PARAM_size_t(OSSL_KDF_PARAM_SIZE, NULL), \
80*e7be843bSPierre Pronchery OSSL_PARAM_octet_string(OSSL_KDF_PARAM_INFO, NULL, 0)
81*e7be843bSPierre Pronchery
82b077aed3SPierre Pronchery typedef struct {
83b077aed3SPierre Pronchery void *provctx;
84b077aed3SPierre Pronchery int mode;
85b077aed3SPierre Pronchery PROV_DIGEST digest;
86b077aed3SPierre Pronchery unsigned char *salt;
87b077aed3SPierre Pronchery size_t salt_len;
88b077aed3SPierre Pronchery unsigned char *key;
89b077aed3SPierre Pronchery size_t key_len;
90b077aed3SPierre Pronchery unsigned char *prefix;
91b077aed3SPierre Pronchery size_t prefix_len;
92b077aed3SPierre Pronchery unsigned char *label;
93b077aed3SPierre Pronchery size_t label_len;
94b077aed3SPierre Pronchery unsigned char *data;
95b077aed3SPierre Pronchery size_t data_len;
96b077aed3SPierre Pronchery unsigned char *info;
97b077aed3SPierre Pronchery size_t info_len;
98*e7be843bSPierre Pronchery OSSL_FIPS_IND_DECLARE
99b077aed3SPierre Pronchery } KDF_HKDF;
100b077aed3SPierre Pronchery
kdf_hkdf_new(void * provctx)101b077aed3SPierre Pronchery static void *kdf_hkdf_new(void *provctx)
102b077aed3SPierre Pronchery {
103b077aed3SPierre Pronchery KDF_HKDF *ctx;
104b077aed3SPierre Pronchery
105b077aed3SPierre Pronchery if (!ossl_prov_is_running())
106b077aed3SPierre Pronchery return NULL;
107b077aed3SPierre Pronchery
108*e7be843bSPierre Pronchery if ((ctx = OPENSSL_zalloc(sizeof(*ctx))) != NULL) {
109b077aed3SPierre Pronchery ctx->provctx = provctx;
110*e7be843bSPierre Pronchery OSSL_FIPS_IND_INIT(ctx)
111*e7be843bSPierre Pronchery }
112b077aed3SPierre Pronchery return ctx;
113b077aed3SPierre Pronchery }
114b077aed3SPierre Pronchery
kdf_hkdf_free(void * vctx)115b077aed3SPierre Pronchery static void kdf_hkdf_free(void *vctx)
116b077aed3SPierre Pronchery {
117b077aed3SPierre Pronchery KDF_HKDF *ctx = (KDF_HKDF *)vctx;
118b077aed3SPierre Pronchery
119b077aed3SPierre Pronchery if (ctx != NULL) {
120b077aed3SPierre Pronchery kdf_hkdf_reset(ctx);
121b077aed3SPierre Pronchery OPENSSL_free(ctx);
122b077aed3SPierre Pronchery }
123b077aed3SPierre Pronchery }
124b077aed3SPierre Pronchery
kdf_hkdf_reset(void * vctx)125b077aed3SPierre Pronchery static void kdf_hkdf_reset(void *vctx)
126b077aed3SPierre Pronchery {
127b077aed3SPierre Pronchery KDF_HKDF *ctx = (KDF_HKDF *)vctx;
128b077aed3SPierre Pronchery void *provctx = ctx->provctx;
129b077aed3SPierre Pronchery
130b077aed3SPierre Pronchery ossl_prov_digest_reset(&ctx->digest);
131*e7be843bSPierre Pronchery #ifdef OPENSSL_PEDANTIC_ZEROIZATION
132*e7be843bSPierre Pronchery OPENSSL_clear_free(ctx->salt, ctx->salt_len);
133*e7be843bSPierre Pronchery #else
134b077aed3SPierre Pronchery OPENSSL_free(ctx->salt);
135*e7be843bSPierre Pronchery #endif
136b077aed3SPierre Pronchery OPENSSL_free(ctx->prefix);
137b077aed3SPierre Pronchery OPENSSL_free(ctx->label);
138b077aed3SPierre Pronchery OPENSSL_clear_free(ctx->data, ctx->data_len);
139b077aed3SPierre Pronchery OPENSSL_clear_free(ctx->key, ctx->key_len);
140b077aed3SPierre Pronchery OPENSSL_clear_free(ctx->info, ctx->info_len);
141b077aed3SPierre Pronchery memset(ctx, 0, sizeof(*ctx));
142b077aed3SPierre Pronchery ctx->provctx = provctx;
143b077aed3SPierre Pronchery }
144b077aed3SPierre Pronchery
kdf_hkdf_dup(void * vctx)145*e7be843bSPierre Pronchery static void *kdf_hkdf_dup(void *vctx)
146*e7be843bSPierre Pronchery {
147*e7be843bSPierre Pronchery const KDF_HKDF *src = (const KDF_HKDF *)vctx;
148*e7be843bSPierre Pronchery KDF_HKDF *dest;
149*e7be843bSPierre Pronchery
150*e7be843bSPierre Pronchery dest = kdf_hkdf_new(src->provctx);
151*e7be843bSPierre Pronchery if (dest != NULL) {
152*e7be843bSPierre Pronchery if (!ossl_prov_memdup(src->salt, src->salt_len, &dest->salt,
153*e7be843bSPierre Pronchery &dest->salt_len)
154*e7be843bSPierre Pronchery || !ossl_prov_memdup(src->key, src->key_len,
155*e7be843bSPierre Pronchery &dest->key , &dest->key_len)
156*e7be843bSPierre Pronchery || !ossl_prov_memdup(src->prefix, src->prefix_len,
157*e7be843bSPierre Pronchery &dest->prefix, &dest->prefix_len)
158*e7be843bSPierre Pronchery || !ossl_prov_memdup(src->label, src->label_len,
159*e7be843bSPierre Pronchery &dest->label, &dest->label_len)
160*e7be843bSPierre Pronchery || !ossl_prov_memdup(src->data, src->data_len,
161*e7be843bSPierre Pronchery &dest->data, &dest->data_len)
162*e7be843bSPierre Pronchery || !ossl_prov_memdup(src->info, src->info_len,
163*e7be843bSPierre Pronchery &dest->info, &dest->info_len)
164*e7be843bSPierre Pronchery || !ossl_prov_digest_copy(&dest->digest, &src->digest))
165*e7be843bSPierre Pronchery goto err;
166*e7be843bSPierre Pronchery dest->mode = src->mode;
167*e7be843bSPierre Pronchery OSSL_FIPS_IND_COPY(dest, src)
168*e7be843bSPierre Pronchery }
169*e7be843bSPierre Pronchery return dest;
170*e7be843bSPierre Pronchery
171*e7be843bSPierre Pronchery err:
172*e7be843bSPierre Pronchery kdf_hkdf_free(dest);
173*e7be843bSPierre Pronchery return NULL;
174*e7be843bSPierre Pronchery }
175*e7be843bSPierre Pronchery
kdf_hkdf_size(KDF_HKDF * ctx)176b077aed3SPierre Pronchery static size_t kdf_hkdf_size(KDF_HKDF *ctx)
177b077aed3SPierre Pronchery {
178b077aed3SPierre Pronchery int sz;
179b077aed3SPierre Pronchery const EVP_MD *md = ossl_prov_digest_md(&ctx->digest);
180b077aed3SPierre Pronchery
181b077aed3SPierre Pronchery if (ctx->mode != EVP_KDF_HKDF_MODE_EXTRACT_ONLY)
182b077aed3SPierre Pronchery return SIZE_MAX;
183b077aed3SPierre Pronchery
184b077aed3SPierre Pronchery if (md == NULL) {
185b077aed3SPierre Pronchery ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_MESSAGE_DIGEST);
186b077aed3SPierre Pronchery return 0;
187b077aed3SPierre Pronchery }
188b077aed3SPierre Pronchery sz = EVP_MD_get_size(md);
189*e7be843bSPierre Pronchery if (sz <= 0)
190b077aed3SPierre Pronchery return 0;
191b077aed3SPierre Pronchery
192b077aed3SPierre Pronchery return sz;
193b077aed3SPierre Pronchery }
194b077aed3SPierre Pronchery
195*e7be843bSPierre Pronchery #ifdef FIPS_MODULE
fips_hkdf_key_check_passed(KDF_HKDF * ctx)196*e7be843bSPierre Pronchery static int fips_hkdf_key_check_passed(KDF_HKDF *ctx)
197*e7be843bSPierre Pronchery {
198*e7be843bSPierre Pronchery OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(ctx->provctx);
199*e7be843bSPierre Pronchery int key_approved = ossl_kdf_check_key_size(ctx->key_len);
200*e7be843bSPierre Pronchery
201*e7be843bSPierre Pronchery if (!key_approved) {
202*e7be843bSPierre Pronchery if (!OSSL_FIPS_IND_ON_UNAPPROVED(ctx, OSSL_FIPS_IND_SETTABLE0,
203*e7be843bSPierre Pronchery libctx, "HKDF", "Key size",
204*e7be843bSPierre Pronchery ossl_fips_config_hkdf_key_check)) {
205*e7be843bSPierre Pronchery ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH);
206*e7be843bSPierre Pronchery return 0;
207*e7be843bSPierre Pronchery }
208*e7be843bSPierre Pronchery }
209*e7be843bSPierre Pronchery return 1;
210*e7be843bSPierre Pronchery }
211*e7be843bSPierre Pronchery #endif
212*e7be843bSPierre Pronchery
kdf_hkdf_derive(void * vctx,unsigned char * key,size_t keylen,const OSSL_PARAM params[])213b077aed3SPierre Pronchery static int kdf_hkdf_derive(void *vctx, unsigned char *key, size_t keylen,
214b077aed3SPierre Pronchery const OSSL_PARAM params[])
215b077aed3SPierre Pronchery {
216b077aed3SPierre Pronchery KDF_HKDF *ctx = (KDF_HKDF *)vctx;
217b077aed3SPierre Pronchery OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(ctx->provctx);
218b077aed3SPierre Pronchery const EVP_MD *md;
219b077aed3SPierre Pronchery
220b077aed3SPierre Pronchery if (!ossl_prov_is_running() || !kdf_hkdf_set_ctx_params(ctx, params))
221b077aed3SPierre Pronchery return 0;
222b077aed3SPierre Pronchery
223b077aed3SPierre Pronchery md = ossl_prov_digest_md(&ctx->digest);
224b077aed3SPierre Pronchery if (md == NULL) {
225b077aed3SPierre Pronchery ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_MESSAGE_DIGEST);
226b077aed3SPierre Pronchery return 0;
227b077aed3SPierre Pronchery }
228b077aed3SPierre Pronchery if (ctx->key == NULL) {
229b077aed3SPierre Pronchery ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY);
230b077aed3SPierre Pronchery return 0;
231b077aed3SPierre Pronchery }
232b077aed3SPierre Pronchery if (keylen == 0) {
233b077aed3SPierre Pronchery ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH);
234b077aed3SPierre Pronchery return 0;
235b077aed3SPierre Pronchery }
236b077aed3SPierre Pronchery
237b077aed3SPierre Pronchery switch (ctx->mode) {
238b077aed3SPierre Pronchery case EVP_KDF_HKDF_MODE_EXTRACT_AND_EXPAND:
239b077aed3SPierre Pronchery default:
240b077aed3SPierre Pronchery return HKDF(libctx, md, ctx->salt, ctx->salt_len,
241b077aed3SPierre Pronchery ctx->key, ctx->key_len, ctx->info, ctx->info_len, key, keylen);
242b077aed3SPierre Pronchery
243b077aed3SPierre Pronchery case EVP_KDF_HKDF_MODE_EXTRACT_ONLY:
244b077aed3SPierre Pronchery return HKDF_Extract(libctx, md, ctx->salt, ctx->salt_len,
245b077aed3SPierre Pronchery ctx->key, ctx->key_len, key, keylen);
246b077aed3SPierre Pronchery
247b077aed3SPierre Pronchery case EVP_KDF_HKDF_MODE_EXPAND_ONLY:
248b077aed3SPierre Pronchery return HKDF_Expand(md, ctx->key, ctx->key_len, ctx->info,
249b077aed3SPierre Pronchery ctx->info_len, key, keylen);
250b077aed3SPierre Pronchery }
251b077aed3SPierre Pronchery }
252b077aed3SPierre Pronchery
hkdf_common_set_ctx_params(KDF_HKDF * ctx,const OSSL_PARAM params[])253b077aed3SPierre Pronchery static int hkdf_common_set_ctx_params(KDF_HKDF *ctx, const OSSL_PARAM params[])
254b077aed3SPierre Pronchery {
255b077aed3SPierre Pronchery OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(ctx->provctx);
256b077aed3SPierre Pronchery const OSSL_PARAM *p;
257b077aed3SPierre Pronchery int n;
258b077aed3SPierre Pronchery
259*e7be843bSPierre Pronchery if (ossl_param_is_empty(params))
260b077aed3SPierre Pronchery return 1;
261b077aed3SPierre Pronchery
262*e7be843bSPierre Pronchery if (OSSL_PARAM_locate_const(params, OSSL_ALG_PARAM_DIGEST) != NULL) {
263*e7be843bSPierre Pronchery const EVP_MD *md = NULL;
264*e7be843bSPierre Pronchery
265b077aed3SPierre Pronchery if (!ossl_prov_digest_load_from_params(&ctx->digest, params, libctx))
266b077aed3SPierre Pronchery return 0;
267b077aed3SPierre Pronchery
268*e7be843bSPierre Pronchery md = ossl_prov_digest_md(&ctx->digest);
269*e7be843bSPierre Pronchery if (EVP_MD_xof(md)) {
270*e7be843bSPierre Pronchery ERR_raise(ERR_LIB_PROV, PROV_R_XOF_DIGESTS_NOT_ALLOWED);
271*e7be843bSPierre Pronchery return 0;
272*e7be843bSPierre Pronchery }
273*e7be843bSPierre Pronchery }
274*e7be843bSPierre Pronchery
275b077aed3SPierre Pronchery if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_MODE)) != NULL) {
276b077aed3SPierre Pronchery if (p->data_type == OSSL_PARAM_UTF8_STRING) {
277b077aed3SPierre Pronchery if (OPENSSL_strcasecmp(p->data, "EXTRACT_AND_EXPAND") == 0) {
278b077aed3SPierre Pronchery ctx->mode = EVP_KDF_HKDF_MODE_EXTRACT_AND_EXPAND;
279b077aed3SPierre Pronchery } else if (OPENSSL_strcasecmp(p->data, "EXTRACT_ONLY") == 0) {
280b077aed3SPierre Pronchery ctx->mode = EVP_KDF_HKDF_MODE_EXTRACT_ONLY;
281b077aed3SPierre Pronchery } else if (OPENSSL_strcasecmp(p->data, "EXPAND_ONLY") == 0) {
282b077aed3SPierre Pronchery ctx->mode = EVP_KDF_HKDF_MODE_EXPAND_ONLY;
283b077aed3SPierre Pronchery } else {
284b077aed3SPierre Pronchery ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_MODE);
285b077aed3SPierre Pronchery return 0;
286b077aed3SPierre Pronchery }
287b077aed3SPierre Pronchery } else if (OSSL_PARAM_get_int(p, &n)) {
288b077aed3SPierre Pronchery if (n != EVP_KDF_HKDF_MODE_EXTRACT_AND_EXPAND
289b077aed3SPierre Pronchery && n != EVP_KDF_HKDF_MODE_EXTRACT_ONLY
290b077aed3SPierre Pronchery && n != EVP_KDF_HKDF_MODE_EXPAND_ONLY) {
291b077aed3SPierre Pronchery ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_MODE);
292b077aed3SPierre Pronchery return 0;
293b077aed3SPierre Pronchery }
294b077aed3SPierre Pronchery ctx->mode = n;
295b077aed3SPierre Pronchery } else {
296b077aed3SPierre Pronchery ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_MODE);
297b077aed3SPierre Pronchery return 0;
298b077aed3SPierre Pronchery }
299b077aed3SPierre Pronchery }
300b077aed3SPierre Pronchery
301b077aed3SPierre Pronchery if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_KEY)) != NULL) {
302b077aed3SPierre Pronchery OPENSSL_clear_free(ctx->key, ctx->key_len);
303b077aed3SPierre Pronchery ctx->key = NULL;
304b077aed3SPierre Pronchery if (!OSSL_PARAM_get_octet_string(p, (void **)&ctx->key, 0,
305b077aed3SPierre Pronchery &ctx->key_len))
306b077aed3SPierre Pronchery return 0;
307b077aed3SPierre Pronchery }
308b077aed3SPierre Pronchery
309b077aed3SPierre Pronchery if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_SALT)) != NULL) {
310b077aed3SPierre Pronchery OPENSSL_free(ctx->salt);
311b077aed3SPierre Pronchery ctx->salt = NULL;
312b077aed3SPierre Pronchery if (!OSSL_PARAM_get_octet_string(p, (void **)&ctx->salt, 0,
313b077aed3SPierre Pronchery &ctx->salt_len))
314b077aed3SPierre Pronchery return 0;
315b077aed3SPierre Pronchery }
316b077aed3SPierre Pronchery
317b077aed3SPierre Pronchery return 1;
318b077aed3SPierre Pronchery }
319b077aed3SPierre Pronchery
kdf_hkdf_set_ctx_params(void * vctx,const OSSL_PARAM params[])320b077aed3SPierre Pronchery static int kdf_hkdf_set_ctx_params(void *vctx, const OSSL_PARAM params[])
321b077aed3SPierre Pronchery {
322b077aed3SPierre Pronchery KDF_HKDF *ctx = vctx;
323b077aed3SPierre Pronchery
324*e7be843bSPierre Pronchery if (ossl_param_is_empty(params))
325b077aed3SPierre Pronchery return 1;
326b077aed3SPierre Pronchery
327*e7be843bSPierre Pronchery if (!OSSL_FIPS_IND_SET_CTX_PARAM(ctx, OSSL_FIPS_IND_SETTABLE0, params,
328*e7be843bSPierre Pronchery OSSL_KDF_PARAM_FIPS_KEY_CHECK))
329*e7be843bSPierre Pronchery return 0;
330*e7be843bSPierre Pronchery
331b077aed3SPierre Pronchery if (!hkdf_common_set_ctx_params(ctx, params))
332b077aed3SPierre Pronchery return 0;
333b077aed3SPierre Pronchery
334*e7be843bSPierre Pronchery if (ossl_param_get1_concat_octet_string(params, OSSL_KDF_PARAM_INFO,
335*e7be843bSPierre Pronchery &ctx->info, &ctx->info_len,
336*e7be843bSPierre Pronchery HKDF_MAXINFO) == 0)
337b077aed3SPierre Pronchery return 0;
338b077aed3SPierre Pronchery
339*e7be843bSPierre Pronchery #ifdef FIPS_MODULE
340*e7be843bSPierre Pronchery if (OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_KEY) != NULL)
341*e7be843bSPierre Pronchery if (!fips_hkdf_key_check_passed(ctx))
342b077aed3SPierre Pronchery return 0;
343*e7be843bSPierre Pronchery #endif
344*e7be843bSPierre Pronchery
345b077aed3SPierre Pronchery return 1;
346b077aed3SPierre Pronchery }
347b077aed3SPierre Pronchery
kdf_hkdf_settable_ctx_params(ossl_unused void * ctx,ossl_unused void * provctx)348b077aed3SPierre Pronchery static const OSSL_PARAM *kdf_hkdf_settable_ctx_params(ossl_unused void *ctx,
349b077aed3SPierre Pronchery ossl_unused void *provctx)
350b077aed3SPierre Pronchery {
351b077aed3SPierre Pronchery static const OSSL_PARAM known_settable_ctx_params[] = {
352b077aed3SPierre Pronchery HKDF_COMMON_SETTABLES,
353b077aed3SPierre Pronchery OSSL_PARAM_octet_string(OSSL_KDF_PARAM_INFO, NULL, 0),
354*e7be843bSPierre Pronchery OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_KDF_PARAM_FIPS_KEY_CHECK)
355b077aed3SPierre Pronchery OSSL_PARAM_END
356b077aed3SPierre Pronchery };
357b077aed3SPierre Pronchery return known_settable_ctx_params;
358b077aed3SPierre Pronchery }
359b077aed3SPierre Pronchery
hkdf_common_get_ctx_params(KDF_HKDF * ctx,OSSL_PARAM params[])360*e7be843bSPierre Pronchery static int hkdf_common_get_ctx_params(KDF_HKDF *ctx, OSSL_PARAM params[])
361b077aed3SPierre Pronchery {
362b077aed3SPierre Pronchery OSSL_PARAM *p;
363b077aed3SPierre Pronchery
364*e7be843bSPierre Pronchery if (ossl_param_is_empty(params))
365*e7be843bSPierre Pronchery return 1;
366*e7be843bSPierre Pronchery
367b077aed3SPierre Pronchery if ((p = OSSL_PARAM_locate(params, OSSL_KDF_PARAM_SIZE)) != NULL) {
368b077aed3SPierre Pronchery size_t sz = kdf_hkdf_size(ctx);
369b077aed3SPierre Pronchery
370b077aed3SPierre Pronchery if (sz == 0)
371b077aed3SPierre Pronchery return 0;
372*e7be843bSPierre Pronchery if (!OSSL_PARAM_set_size_t(p, sz))
373*e7be843bSPierre Pronchery return 0;
374b077aed3SPierre Pronchery }
375*e7be843bSPierre Pronchery
37644096ebdSEnji Cooper if ((p = OSSL_PARAM_locate(params, OSSL_KDF_PARAM_INFO)) != NULL) {
377*e7be843bSPierre Pronchery if (ctx->info == NULL || ctx->info_len == 0)
37844096ebdSEnji Cooper p->return_size = 0;
379*e7be843bSPierre Pronchery else if (!OSSL_PARAM_set_octet_string(p, ctx->info, ctx->info_len))
380*e7be843bSPierre Pronchery return 0;
381*e7be843bSPierre Pronchery }
382*e7be843bSPierre Pronchery
38344096ebdSEnji Cooper return 1;
38444096ebdSEnji Cooper }
385*e7be843bSPierre Pronchery
kdf_hkdf_get_ctx_params(void * vctx,OSSL_PARAM params[])386*e7be843bSPierre Pronchery static int kdf_hkdf_get_ctx_params(void *vctx, OSSL_PARAM params[])
387*e7be843bSPierre Pronchery {
388*e7be843bSPierre Pronchery KDF_HKDF *ctx = (KDF_HKDF *)vctx;
389*e7be843bSPierre Pronchery
390*e7be843bSPierre Pronchery if (ossl_param_is_empty(params))
391*e7be843bSPierre Pronchery return 1;
392*e7be843bSPierre Pronchery
393*e7be843bSPierre Pronchery if (!hkdf_common_get_ctx_params(ctx, params))
394*e7be843bSPierre Pronchery return 0;
395*e7be843bSPierre Pronchery
396*e7be843bSPierre Pronchery if (!OSSL_FIPS_IND_GET_CTX_PARAM(ctx, params))
397*e7be843bSPierre Pronchery return 0;
398*e7be843bSPierre Pronchery
399*e7be843bSPierre Pronchery return 1;
400b077aed3SPierre Pronchery }
401b077aed3SPierre Pronchery
kdf_hkdf_gettable_ctx_params(ossl_unused void * ctx,ossl_unused void * provctx)402b077aed3SPierre Pronchery static const OSSL_PARAM *kdf_hkdf_gettable_ctx_params(ossl_unused void *ctx,
403b077aed3SPierre Pronchery ossl_unused void *provctx)
404b077aed3SPierre Pronchery {
405b077aed3SPierre Pronchery static const OSSL_PARAM known_gettable_ctx_params[] = {
406*e7be843bSPierre Pronchery HKDF_COMMON_GETTABLES,
407*e7be843bSPierre Pronchery OSSL_FIPS_IND_GETTABLE_CTX_PARAM()
408b077aed3SPierre Pronchery OSSL_PARAM_END
409b077aed3SPierre Pronchery };
410b077aed3SPierre Pronchery return known_gettable_ctx_params;
411b077aed3SPierre Pronchery }
412b077aed3SPierre Pronchery
413b077aed3SPierre Pronchery const OSSL_DISPATCH ossl_kdf_hkdf_functions[] = {
414b077aed3SPierre Pronchery { OSSL_FUNC_KDF_NEWCTX, (void(*)(void))kdf_hkdf_new },
415*e7be843bSPierre Pronchery { OSSL_FUNC_KDF_DUPCTX, (void(*)(void))kdf_hkdf_dup },
416b077aed3SPierre Pronchery { OSSL_FUNC_KDF_FREECTX, (void(*)(void))kdf_hkdf_free },
417b077aed3SPierre Pronchery { OSSL_FUNC_KDF_RESET, (void(*)(void))kdf_hkdf_reset },
418b077aed3SPierre Pronchery { OSSL_FUNC_KDF_DERIVE, (void(*)(void))kdf_hkdf_derive },
419b077aed3SPierre Pronchery { OSSL_FUNC_KDF_SETTABLE_CTX_PARAMS,
420b077aed3SPierre Pronchery (void(*)(void))kdf_hkdf_settable_ctx_params },
421b077aed3SPierre Pronchery { OSSL_FUNC_KDF_SET_CTX_PARAMS, (void(*)(void))kdf_hkdf_set_ctx_params },
422b077aed3SPierre Pronchery { OSSL_FUNC_KDF_GETTABLE_CTX_PARAMS,
423b077aed3SPierre Pronchery (void(*)(void))kdf_hkdf_gettable_ctx_params },
424b077aed3SPierre Pronchery { OSSL_FUNC_KDF_GET_CTX_PARAMS, (void(*)(void))kdf_hkdf_get_ctx_params },
425*e7be843bSPierre Pronchery OSSL_DISPATCH_END
426b077aed3SPierre Pronchery };
427b077aed3SPierre Pronchery
428b077aed3SPierre Pronchery /*
429b077aed3SPierre Pronchery * Refer to "HMAC-based Extract-and-Expand Key Derivation Function (HKDF)"
430b077aed3SPierre Pronchery * Section 2 (https://tools.ietf.org/html/rfc5869#section-2) and
431b077aed3SPierre Pronchery * "Cryptographic Extraction and Key Derivation: The HKDF Scheme"
432b077aed3SPierre Pronchery * Section 4.2 (https://eprint.iacr.org/2010/264.pdf).
433b077aed3SPierre Pronchery *
434b077aed3SPierre Pronchery * From the paper:
435b077aed3SPierre Pronchery * The scheme HKDF is specified as:
436b077aed3SPierre Pronchery * HKDF(XTS, SKM, CTXinfo, L) = K(1) | K(2) | ... | K(t)
437b077aed3SPierre Pronchery *
438b077aed3SPierre Pronchery * where:
439b077aed3SPierre Pronchery * SKM is source key material
440b077aed3SPierre Pronchery * XTS is extractor salt (which may be null or constant)
441b077aed3SPierre Pronchery * CTXinfo is context information (may be null)
442b077aed3SPierre Pronchery * L is the number of key bits to be produced by KDF
443b077aed3SPierre Pronchery * k is the output length in bits of the hash function used with HMAC
444b077aed3SPierre Pronchery * t = ceil(L/k)
445b077aed3SPierre Pronchery * the value K(t) is truncated to its first d = L mod k bits.
446b077aed3SPierre Pronchery *
447b077aed3SPierre Pronchery * From RFC 5869:
448b077aed3SPierre Pronchery * 2.2. Step 1: Extract
449b077aed3SPierre Pronchery * HKDF-Extract(salt, IKM) -> PRK
450b077aed3SPierre Pronchery * 2.3. Step 2: Expand
451b077aed3SPierre Pronchery * HKDF-Expand(PRK, info, L) -> OKM
452b077aed3SPierre Pronchery */
HKDF(OSSL_LIB_CTX * libctx,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)453b077aed3SPierre Pronchery static int HKDF(OSSL_LIB_CTX *libctx, const EVP_MD *evp_md,
454b077aed3SPierre Pronchery const unsigned char *salt, size_t salt_len,
455b077aed3SPierre Pronchery const unsigned char *ikm, size_t ikm_len,
456b077aed3SPierre Pronchery const unsigned char *info, size_t info_len,
457b077aed3SPierre Pronchery unsigned char *okm, size_t okm_len)
458b077aed3SPierre Pronchery {
459b077aed3SPierre Pronchery unsigned char prk[EVP_MAX_MD_SIZE];
460b077aed3SPierre Pronchery int ret, sz;
461b077aed3SPierre Pronchery size_t prk_len;
462b077aed3SPierre Pronchery
463b077aed3SPierre Pronchery sz = EVP_MD_get_size(evp_md);
464*e7be843bSPierre Pronchery if (sz <= 0)
465b077aed3SPierre Pronchery return 0;
466b077aed3SPierre Pronchery prk_len = (size_t)sz;
467b077aed3SPierre Pronchery
468b077aed3SPierre Pronchery /* Step 1: HKDF-Extract(salt, IKM) -> PRK */
469b077aed3SPierre Pronchery if (!HKDF_Extract(libctx, evp_md,
470b077aed3SPierre Pronchery salt, salt_len, ikm, ikm_len, prk, prk_len))
471b077aed3SPierre Pronchery return 0;
472b077aed3SPierre Pronchery
473b077aed3SPierre Pronchery /* Step 2: HKDF-Expand(PRK, info, L) -> OKM */
474b077aed3SPierre Pronchery ret = HKDF_Expand(evp_md, prk, prk_len, info, info_len, okm, okm_len);
475b077aed3SPierre Pronchery OPENSSL_cleanse(prk, sizeof(prk));
476b077aed3SPierre Pronchery
477b077aed3SPierre Pronchery return ret;
478b077aed3SPierre Pronchery }
479b077aed3SPierre Pronchery
480b077aed3SPierre Pronchery /*
481b077aed3SPierre Pronchery * Refer to "HMAC-based Extract-and-Expand Key Derivation Function (HKDF)"
482b077aed3SPierre Pronchery * Section 2.2 (https://tools.ietf.org/html/rfc5869#section-2.2).
483b077aed3SPierre Pronchery *
484b077aed3SPierre Pronchery * 2.2. Step 1: Extract
485b077aed3SPierre Pronchery *
486b077aed3SPierre Pronchery * HKDF-Extract(salt, IKM) -> PRK
487b077aed3SPierre Pronchery *
488b077aed3SPierre Pronchery * Options:
489b077aed3SPierre Pronchery * Hash a hash function; HashLen denotes the length of the
490b077aed3SPierre Pronchery * hash function output in octets
491b077aed3SPierre Pronchery *
492b077aed3SPierre Pronchery * Inputs:
493b077aed3SPierre Pronchery * salt optional salt value (a non-secret random value);
494b077aed3SPierre Pronchery * if not provided, it is set to a string of HashLen zeros.
495b077aed3SPierre Pronchery * IKM input keying material
496b077aed3SPierre Pronchery *
497b077aed3SPierre Pronchery * Output:
498b077aed3SPierre Pronchery * PRK a pseudorandom key (of HashLen octets)
499b077aed3SPierre Pronchery *
500b077aed3SPierre Pronchery * The output PRK is calculated as follows:
501b077aed3SPierre Pronchery *
502b077aed3SPierre Pronchery * PRK = HMAC-Hash(salt, IKM)
503b077aed3SPierre Pronchery */
HKDF_Extract(OSSL_LIB_CTX * libctx,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)504b077aed3SPierre Pronchery static int HKDF_Extract(OSSL_LIB_CTX *libctx, const EVP_MD *evp_md,
505b077aed3SPierre Pronchery const unsigned char *salt, size_t salt_len,
506b077aed3SPierre Pronchery const unsigned char *ikm, size_t ikm_len,
507b077aed3SPierre Pronchery unsigned char *prk, size_t prk_len)
508b077aed3SPierre Pronchery {
509b077aed3SPierre Pronchery int sz = EVP_MD_get_size(evp_md);
510b077aed3SPierre Pronchery
511*e7be843bSPierre Pronchery if (sz <= 0)
512b077aed3SPierre Pronchery return 0;
513b077aed3SPierre Pronchery if (prk_len != (size_t)sz) {
514b077aed3SPierre Pronchery ERR_raise(ERR_LIB_PROV, PROV_R_WRONG_OUTPUT_BUFFER_SIZE);
515b077aed3SPierre Pronchery return 0;
516b077aed3SPierre Pronchery }
517b077aed3SPierre Pronchery /* calc: PRK = HMAC-Hash(salt, IKM) */
518b077aed3SPierre Pronchery return
519b077aed3SPierre Pronchery EVP_Q_mac(libctx, "HMAC", NULL, EVP_MD_get0_name(evp_md), NULL, salt,
520b077aed3SPierre Pronchery salt_len, ikm, ikm_len, prk, EVP_MD_get_size(evp_md), NULL)
521b077aed3SPierre Pronchery != NULL;
522b077aed3SPierre Pronchery }
523b077aed3SPierre Pronchery
524b077aed3SPierre Pronchery /*
525b077aed3SPierre Pronchery * Refer to "HMAC-based Extract-and-Expand Key Derivation Function (HKDF)"
526b077aed3SPierre Pronchery * Section 2.3 (https://tools.ietf.org/html/rfc5869#section-2.3).
527b077aed3SPierre Pronchery *
528b077aed3SPierre Pronchery * 2.3. Step 2: Expand
529b077aed3SPierre Pronchery *
530b077aed3SPierre Pronchery * HKDF-Expand(PRK, info, L) -> OKM
531b077aed3SPierre Pronchery *
532b077aed3SPierre Pronchery * Options:
533b077aed3SPierre Pronchery * Hash a hash function; HashLen denotes the length of the
534b077aed3SPierre Pronchery * hash function output in octets
535b077aed3SPierre Pronchery *
536b077aed3SPierre Pronchery * Inputs:
537b077aed3SPierre Pronchery * PRK a pseudorandom key of at least HashLen octets
538b077aed3SPierre Pronchery * (usually, the output from the extract step)
539b077aed3SPierre Pronchery * info optional context and application specific information
540b077aed3SPierre Pronchery * (can be a zero-length string)
541b077aed3SPierre Pronchery * L length of output keying material in octets
542b077aed3SPierre Pronchery * (<= 255*HashLen)
543b077aed3SPierre Pronchery *
544b077aed3SPierre Pronchery * Output:
545b077aed3SPierre Pronchery * OKM output keying material (of L octets)
546b077aed3SPierre Pronchery *
547b077aed3SPierre Pronchery * The output OKM is calculated as follows:
548b077aed3SPierre Pronchery *
549b077aed3SPierre Pronchery * N = ceil(L/HashLen)
550b077aed3SPierre Pronchery * T = T(1) | T(2) | T(3) | ... | T(N)
551b077aed3SPierre Pronchery * OKM = first L octets of T
552b077aed3SPierre Pronchery *
553b077aed3SPierre Pronchery * where:
554b077aed3SPierre Pronchery * T(0) = empty string (zero length)
555b077aed3SPierre Pronchery * T(1) = HMAC-Hash(PRK, T(0) | info | 0x01)
556b077aed3SPierre Pronchery * T(2) = HMAC-Hash(PRK, T(1) | info | 0x02)
557b077aed3SPierre Pronchery * T(3) = HMAC-Hash(PRK, T(2) | info | 0x03)
558b077aed3SPierre Pronchery * ...
559b077aed3SPierre Pronchery *
560b077aed3SPierre Pronchery * (where the constant concatenated to the end of each T(n) is a
561b077aed3SPierre Pronchery * single octet.)
562b077aed3SPierre Pronchery */
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)563b077aed3SPierre Pronchery static int HKDF_Expand(const EVP_MD *evp_md,
564b077aed3SPierre Pronchery const unsigned char *prk, size_t prk_len,
565b077aed3SPierre Pronchery const unsigned char *info, size_t info_len,
566b077aed3SPierre Pronchery unsigned char *okm, size_t okm_len)
567b077aed3SPierre Pronchery {
568b077aed3SPierre Pronchery HMAC_CTX *hmac;
569b077aed3SPierre Pronchery int ret = 0, sz;
570b077aed3SPierre Pronchery unsigned int i;
571b077aed3SPierre Pronchery unsigned char prev[EVP_MAX_MD_SIZE];
572b077aed3SPierre Pronchery size_t done_len = 0, dig_len, n;
573b077aed3SPierre Pronchery
574b077aed3SPierre Pronchery sz = EVP_MD_get_size(evp_md);
575b077aed3SPierre Pronchery if (sz <= 0)
576b077aed3SPierre Pronchery return 0;
577b077aed3SPierre Pronchery dig_len = (size_t)sz;
578b077aed3SPierre Pronchery
579b077aed3SPierre Pronchery /* calc: N = ceil(L/HashLen) */
580b077aed3SPierre Pronchery n = okm_len / dig_len;
581b077aed3SPierre Pronchery if (okm_len % dig_len)
582b077aed3SPierre Pronchery n++;
583b077aed3SPierre Pronchery
584b077aed3SPierre Pronchery if (n > 255 || okm == NULL)
585b077aed3SPierre Pronchery return 0;
586b077aed3SPierre Pronchery
587b077aed3SPierre Pronchery if ((hmac = HMAC_CTX_new()) == NULL)
588b077aed3SPierre Pronchery return 0;
589b077aed3SPierre Pronchery
590b077aed3SPierre Pronchery if (!HMAC_Init_ex(hmac, prk, prk_len, evp_md, NULL))
591b077aed3SPierre Pronchery goto err;
592b077aed3SPierre Pronchery
593b077aed3SPierre Pronchery for (i = 1; i <= n; i++) {
594b077aed3SPierre Pronchery size_t copy_len;
595b077aed3SPierre Pronchery const unsigned char ctr = i;
596b077aed3SPierre Pronchery
597b077aed3SPierre Pronchery /* calc: T(i) = HMAC-Hash(PRK, T(i - 1) | info | i) */
598b077aed3SPierre Pronchery if (i > 1) {
599b077aed3SPierre Pronchery if (!HMAC_Init_ex(hmac, NULL, 0, NULL, NULL))
600b077aed3SPierre Pronchery goto err;
601b077aed3SPierre Pronchery
602b077aed3SPierre Pronchery if (!HMAC_Update(hmac, prev, dig_len))
603b077aed3SPierre Pronchery goto err;
604b077aed3SPierre Pronchery }
605b077aed3SPierre Pronchery
606b077aed3SPierre Pronchery if (!HMAC_Update(hmac, info, info_len))
607b077aed3SPierre Pronchery goto err;
608b077aed3SPierre Pronchery
609b077aed3SPierre Pronchery if (!HMAC_Update(hmac, &ctr, 1))
610b077aed3SPierre Pronchery goto err;
611b077aed3SPierre Pronchery
612b077aed3SPierre Pronchery if (!HMAC_Final(hmac, prev, NULL))
613b077aed3SPierre Pronchery goto err;
614b077aed3SPierre Pronchery
615b077aed3SPierre Pronchery copy_len = (dig_len > okm_len - done_len) ?
616b077aed3SPierre Pronchery okm_len - done_len :
617b077aed3SPierre Pronchery dig_len;
618b077aed3SPierre Pronchery
619b077aed3SPierre Pronchery memcpy(okm + done_len, prev, copy_len);
620b077aed3SPierre Pronchery
621b077aed3SPierre Pronchery done_len += copy_len;
622b077aed3SPierre Pronchery }
623b077aed3SPierre Pronchery ret = 1;
624b077aed3SPierre Pronchery
625b077aed3SPierre Pronchery err:
626b077aed3SPierre Pronchery OPENSSL_cleanse(prev, sizeof(prev));
627b077aed3SPierre Pronchery HMAC_CTX_free(hmac);
628b077aed3SPierre Pronchery return ret;
629b077aed3SPierre Pronchery }
630b077aed3SPierre Pronchery
631b077aed3SPierre Pronchery /*
632b077aed3SPierre Pronchery * TLS uses slight variations of the above and for FIPS validation purposes,
633b077aed3SPierre Pronchery * they need to be present here.
634b077aed3SPierre Pronchery * Refer to RFC 8446 section 7 for specific details.
635b077aed3SPierre Pronchery */
636b077aed3SPierre Pronchery
637b077aed3SPierre Pronchery /*
638b077aed3SPierre Pronchery * Given a |secret|; a |label| of length |labellen|; and |data| of length
639b077aed3SPierre Pronchery * |datalen| (e.g. typically a hash of the handshake messages), derive a new
640b077aed3SPierre Pronchery * secret |outlen| bytes long and store it in the location pointed to be |out|.
641b077aed3SPierre Pronchery * The |data| value may be zero length. Returns 1 on success and 0 on failure.
642b077aed3SPierre Pronchery */
prov_tls13_hkdf_expand(const EVP_MD * md,const unsigned char * key,size_t keylen,const unsigned char * prefix,size_t prefixlen,const unsigned char * label,size_t labellen,const unsigned char * data,size_t datalen,unsigned char * out,size_t outlen)643b077aed3SPierre Pronchery static int prov_tls13_hkdf_expand(const EVP_MD *md,
644b077aed3SPierre Pronchery const unsigned char *key, size_t keylen,
645b077aed3SPierre Pronchery const unsigned char *prefix, size_t prefixlen,
646b077aed3SPierre Pronchery const unsigned char *label, size_t labellen,
647b077aed3SPierre Pronchery const unsigned char *data, size_t datalen,
648b077aed3SPierre Pronchery unsigned char *out, size_t outlen)
649b077aed3SPierre Pronchery {
650b077aed3SPierre Pronchery size_t hkdflabellen;
651b077aed3SPierre Pronchery unsigned char hkdflabel[HKDF_MAXBUF];
652b077aed3SPierre Pronchery WPACKET pkt;
653b077aed3SPierre Pronchery
654b077aed3SPierre Pronchery /*
655b077aed3SPierre Pronchery * 2 bytes for length of derived secret + 1 byte for length of combined
656b077aed3SPierre Pronchery * prefix and label + bytes for the label itself + 1 byte length of hash
657b077aed3SPierre Pronchery * + bytes for the hash itself. We've got the maximum the KDF can handle
658b077aed3SPierre Pronchery * which should always be sufficient.
659b077aed3SPierre Pronchery */
660b077aed3SPierre Pronchery if (!WPACKET_init_static_len(&pkt, hkdflabel, sizeof(hkdflabel), 0)
661b077aed3SPierre Pronchery || !WPACKET_put_bytes_u16(&pkt, outlen)
662b077aed3SPierre Pronchery || !WPACKET_start_sub_packet_u8(&pkt)
663b077aed3SPierre Pronchery || !WPACKET_memcpy(&pkt, prefix, prefixlen)
664b077aed3SPierre Pronchery || !WPACKET_memcpy(&pkt, label, labellen)
665b077aed3SPierre Pronchery || !WPACKET_close(&pkt)
666b077aed3SPierre Pronchery || !WPACKET_sub_memcpy_u8(&pkt, data, (data == NULL) ? 0 : datalen)
667b077aed3SPierre Pronchery || !WPACKET_get_total_written(&pkt, &hkdflabellen)
668b077aed3SPierre Pronchery || !WPACKET_finish(&pkt)) {
669b077aed3SPierre Pronchery WPACKET_cleanup(&pkt);
670b077aed3SPierre Pronchery return 0;
671b077aed3SPierre Pronchery }
672b077aed3SPierre Pronchery
673b077aed3SPierre Pronchery return HKDF_Expand(md, key, keylen, hkdflabel, hkdflabellen,
674b077aed3SPierre Pronchery out, outlen);
675b077aed3SPierre Pronchery }
676b077aed3SPierre Pronchery
prov_tls13_hkdf_generate_secret(OSSL_LIB_CTX * libctx,const EVP_MD * md,const unsigned char * prevsecret,size_t prevsecretlen,const unsigned char * insecret,size_t insecretlen,const unsigned char * prefix,size_t prefixlen,const unsigned char * label,size_t labellen,unsigned char * out,size_t outlen)677b077aed3SPierre Pronchery static int prov_tls13_hkdf_generate_secret(OSSL_LIB_CTX *libctx,
678b077aed3SPierre Pronchery const EVP_MD *md,
679b077aed3SPierre Pronchery const unsigned char *prevsecret,
680b077aed3SPierre Pronchery size_t prevsecretlen,
681b077aed3SPierre Pronchery const unsigned char *insecret,
682b077aed3SPierre Pronchery size_t insecretlen,
683b077aed3SPierre Pronchery const unsigned char *prefix,
684b077aed3SPierre Pronchery size_t prefixlen,
685b077aed3SPierre Pronchery const unsigned char *label,
686b077aed3SPierre Pronchery size_t labellen,
687b077aed3SPierre Pronchery unsigned char *out, size_t outlen)
688b077aed3SPierre Pronchery {
689b077aed3SPierre Pronchery size_t mdlen;
690b077aed3SPierre Pronchery int ret;
691b077aed3SPierre Pronchery unsigned char preextractsec[EVP_MAX_MD_SIZE];
692b077aed3SPierre Pronchery /* Always filled with zeros */
693b077aed3SPierre Pronchery static const unsigned char default_zeros[EVP_MAX_MD_SIZE];
694b077aed3SPierre Pronchery
695b077aed3SPierre Pronchery ret = EVP_MD_get_size(md);
696b077aed3SPierre Pronchery /* Ensure cast to size_t is safe */
697b077aed3SPierre Pronchery if (ret <= 0)
698b077aed3SPierre Pronchery return 0;
699b077aed3SPierre Pronchery mdlen = (size_t)ret;
700b077aed3SPierre Pronchery
701b077aed3SPierre Pronchery if (insecret == NULL) {
702b077aed3SPierre Pronchery insecret = default_zeros;
703b077aed3SPierre Pronchery insecretlen = mdlen;
704b077aed3SPierre Pronchery }
705b077aed3SPierre Pronchery if (prevsecret == NULL) {
706b077aed3SPierre Pronchery prevsecret = default_zeros;
707*e7be843bSPierre Pronchery prevsecretlen = mdlen;
708b077aed3SPierre Pronchery } else {
709b077aed3SPierre Pronchery EVP_MD_CTX *mctx = EVP_MD_CTX_new();
710b077aed3SPierre Pronchery unsigned char hash[EVP_MAX_MD_SIZE];
711b077aed3SPierre Pronchery
712b077aed3SPierre Pronchery /* The pre-extract derive step uses a hash of no messages */
713b077aed3SPierre Pronchery if (mctx == NULL
714b077aed3SPierre Pronchery || EVP_DigestInit_ex(mctx, md, NULL) <= 0
715b077aed3SPierre Pronchery || EVP_DigestFinal_ex(mctx, hash, NULL) <= 0) {
716b077aed3SPierre Pronchery EVP_MD_CTX_free(mctx);
717b077aed3SPierre Pronchery return 0;
718b077aed3SPierre Pronchery }
719b077aed3SPierre Pronchery EVP_MD_CTX_free(mctx);
720b077aed3SPierre Pronchery
721b077aed3SPierre Pronchery /* Generate the pre-extract secret */
7220d0c8621SEnji Cooper if (!prov_tls13_hkdf_expand(md, prevsecret, prevsecretlen,
723b077aed3SPierre Pronchery prefix, prefixlen, label, labellen,
724b077aed3SPierre Pronchery hash, mdlen, preextractsec, mdlen))
725b077aed3SPierre Pronchery return 0;
726b077aed3SPierre Pronchery prevsecret = preextractsec;
727b077aed3SPierre Pronchery prevsecretlen = mdlen;
728b077aed3SPierre Pronchery }
729b077aed3SPierre Pronchery
730b077aed3SPierre Pronchery ret = HKDF_Extract(libctx, md, prevsecret, prevsecretlen,
731b077aed3SPierre Pronchery insecret, insecretlen, out, outlen);
732b077aed3SPierre Pronchery
733b077aed3SPierre Pronchery if (prevsecret == preextractsec)
734b077aed3SPierre Pronchery OPENSSL_cleanse(preextractsec, mdlen);
735b077aed3SPierre Pronchery return ret;
736b077aed3SPierre Pronchery }
737b077aed3SPierre Pronchery
738*e7be843bSPierre Pronchery #ifdef FIPS_MODULE
fips_tls1_3_digest_check_passed(KDF_HKDF * ctx,const EVP_MD * md)739*e7be843bSPierre Pronchery static int fips_tls1_3_digest_check_passed(KDF_HKDF *ctx, const EVP_MD *md)
740*e7be843bSPierre Pronchery {
741*e7be843bSPierre Pronchery OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(ctx->provctx);
742*e7be843bSPierre Pronchery /*
743*e7be843bSPierre Pronchery * Perform digest check
744*e7be843bSPierre Pronchery *
745*e7be843bSPierre Pronchery * According to RFC 8446 appendix B.4, the valid hash functions are
746*e7be843bSPierre Pronchery * specified in FIPS 180-4. However, it only lists SHA2-256 and SHA2-384 in
747*e7be843bSPierre Pronchery * the table. ACVP also only lists the same set of hash functions.
748*e7be843bSPierre Pronchery */
749*e7be843bSPierre Pronchery int digest_unapproved = !EVP_MD_is_a(md, SN_sha256)
750*e7be843bSPierre Pronchery && !EVP_MD_is_a(md, SN_sha384);
751*e7be843bSPierre Pronchery
752*e7be843bSPierre Pronchery if (digest_unapproved) {
753*e7be843bSPierre Pronchery if (!OSSL_FIPS_IND_ON_UNAPPROVED(ctx, OSSL_FIPS_IND_SETTABLE0,
754*e7be843bSPierre Pronchery libctx, "TLS13 KDF", "Digest",
755*e7be843bSPierre Pronchery ossl_fips_config_tls13_kdf_digest_check)) {
756*e7be843bSPierre Pronchery ERR_raise(ERR_LIB_PROV, PROV_R_DIGEST_NOT_ALLOWED);
757*e7be843bSPierre Pronchery return 0;
758*e7be843bSPierre Pronchery }
759*e7be843bSPierre Pronchery }
760*e7be843bSPierre Pronchery return 1;
761*e7be843bSPierre Pronchery }
762*e7be843bSPierre Pronchery
763*e7be843bSPierre Pronchery /*
764*e7be843bSPierre Pronchery * Calculate the correct length of the secret key.
765*e7be843bSPierre Pronchery *
766*e7be843bSPierre Pronchery * RFC 8446:
767*e7be843bSPierre Pronchery * If a given secret is not available, then the 0-value consisting of a
768*e7be843bSPierre Pronchery * string of Hash.length bytes set to zeros is used.
769*e7be843bSPierre Pronchery */
fips_tls1_3_key_size(KDF_HKDF * ctx)770*e7be843bSPierre Pronchery static size_t fips_tls1_3_key_size(KDF_HKDF *ctx)
771*e7be843bSPierre Pronchery {
772*e7be843bSPierre Pronchery const EVP_MD *md = ossl_prov_digest_md(&ctx->digest);
773*e7be843bSPierre Pronchery size_t key_size = 0;
774*e7be843bSPierre Pronchery
775*e7be843bSPierre Pronchery if (ctx->key != NULL)
776*e7be843bSPierre Pronchery key_size = ctx->key_len;
777*e7be843bSPierre Pronchery else if (md != NULL)
778*e7be843bSPierre Pronchery key_size = EVP_MD_size(md);
779*e7be843bSPierre Pronchery
780*e7be843bSPierre Pronchery return key_size;
781*e7be843bSPierre Pronchery }
782*e7be843bSPierre Pronchery
fips_tls1_3_key_check_passed(KDF_HKDF * ctx)783*e7be843bSPierre Pronchery static int fips_tls1_3_key_check_passed(KDF_HKDF *ctx)
784*e7be843bSPierre Pronchery {
785*e7be843bSPierre Pronchery OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(ctx->provctx);
786*e7be843bSPierre Pronchery int key_approved = ossl_kdf_check_key_size(fips_tls1_3_key_size(ctx));
787*e7be843bSPierre Pronchery
788*e7be843bSPierre Pronchery if (!key_approved) {
789*e7be843bSPierre Pronchery if (!OSSL_FIPS_IND_ON_UNAPPROVED(ctx, OSSL_FIPS_IND_SETTABLE1,
790*e7be843bSPierre Pronchery libctx, "TLS13 KDF", "Key size",
791*e7be843bSPierre Pronchery ossl_fips_config_tls13_kdf_key_check)) {
792*e7be843bSPierre Pronchery ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH);
793*e7be843bSPierre Pronchery return 0;
794*e7be843bSPierre Pronchery }
795*e7be843bSPierre Pronchery }
796*e7be843bSPierre Pronchery return 1;
797*e7be843bSPierre Pronchery }
798*e7be843bSPierre Pronchery #endif
799*e7be843bSPierre Pronchery
kdf_tls1_3_derive(void * vctx,unsigned char * key,size_t keylen,const OSSL_PARAM params[])800b077aed3SPierre Pronchery static int kdf_tls1_3_derive(void *vctx, unsigned char *key, size_t keylen,
801b077aed3SPierre Pronchery const OSSL_PARAM params[])
802b077aed3SPierre Pronchery {
803b077aed3SPierre Pronchery KDF_HKDF *ctx = (KDF_HKDF *)vctx;
804b077aed3SPierre Pronchery const EVP_MD *md;
805b077aed3SPierre Pronchery
806b077aed3SPierre Pronchery if (!ossl_prov_is_running() || !kdf_tls1_3_set_ctx_params(ctx, params))
807b077aed3SPierre Pronchery return 0;
808b077aed3SPierre Pronchery
809b077aed3SPierre Pronchery md = ossl_prov_digest_md(&ctx->digest);
810b077aed3SPierre Pronchery if (md == NULL) {
811b077aed3SPierre Pronchery ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_MESSAGE_DIGEST);
812b077aed3SPierre Pronchery return 0;
813b077aed3SPierre Pronchery }
814b077aed3SPierre Pronchery
815b077aed3SPierre Pronchery switch (ctx->mode) {
816b077aed3SPierre Pronchery default:
817b077aed3SPierre Pronchery return 0;
818b077aed3SPierre Pronchery
819b077aed3SPierre Pronchery case EVP_KDF_HKDF_MODE_EXTRACT_ONLY:
820b077aed3SPierre Pronchery return prov_tls13_hkdf_generate_secret(PROV_LIBCTX_OF(ctx->provctx),
821b077aed3SPierre Pronchery md,
822b077aed3SPierre Pronchery ctx->salt, ctx->salt_len,
823b077aed3SPierre Pronchery ctx->key, ctx->key_len,
824b077aed3SPierre Pronchery ctx->prefix, ctx->prefix_len,
825b077aed3SPierre Pronchery ctx->label, ctx->label_len,
826b077aed3SPierre Pronchery key, keylen);
827b077aed3SPierre Pronchery
828b077aed3SPierre Pronchery case EVP_KDF_HKDF_MODE_EXPAND_ONLY:
829b077aed3SPierre Pronchery return prov_tls13_hkdf_expand(md, ctx->key, ctx->key_len,
830b077aed3SPierre Pronchery ctx->prefix, ctx->prefix_len,
831b077aed3SPierre Pronchery ctx->label, ctx->label_len,
832b077aed3SPierre Pronchery ctx->data, ctx->data_len,
833b077aed3SPierre Pronchery key, keylen);
834b077aed3SPierre Pronchery }
835b077aed3SPierre Pronchery }
836b077aed3SPierre Pronchery
kdf_tls1_3_set_ctx_params(void * vctx,const OSSL_PARAM params[])837b077aed3SPierre Pronchery static int kdf_tls1_3_set_ctx_params(void *vctx, const OSSL_PARAM params[])
838b077aed3SPierre Pronchery {
839b077aed3SPierre Pronchery const OSSL_PARAM *p;
840b077aed3SPierre Pronchery KDF_HKDF *ctx = vctx;
841b077aed3SPierre Pronchery
842*e7be843bSPierre Pronchery if (ossl_param_is_empty(params))
843b077aed3SPierre Pronchery return 1;
844b077aed3SPierre Pronchery
845*e7be843bSPierre Pronchery if (!OSSL_FIPS_IND_SET_CTX_PARAM(ctx, OSSL_FIPS_IND_SETTABLE0, params,
846*e7be843bSPierre Pronchery OSSL_KDF_PARAM_FIPS_DIGEST_CHECK))
847*e7be843bSPierre Pronchery return 0;
848*e7be843bSPierre Pronchery if (!OSSL_FIPS_IND_SET_CTX_PARAM(ctx, OSSL_FIPS_IND_SETTABLE1, params,
849*e7be843bSPierre Pronchery OSSL_KDF_PARAM_FIPS_KEY_CHECK))
850*e7be843bSPierre Pronchery return 0;
851*e7be843bSPierre Pronchery
852b077aed3SPierre Pronchery if (!hkdf_common_set_ctx_params(ctx, params))
853b077aed3SPierre Pronchery return 0;
854b077aed3SPierre Pronchery
855b077aed3SPierre Pronchery if (ctx->mode == EVP_KDF_HKDF_MODE_EXTRACT_AND_EXPAND) {
856b077aed3SPierre Pronchery ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_MODE);
857b077aed3SPierre Pronchery return 0;
858b077aed3SPierre Pronchery }
859b077aed3SPierre Pronchery
860b077aed3SPierre Pronchery if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_PREFIX)) != NULL) {
861b077aed3SPierre Pronchery OPENSSL_free(ctx->prefix);
862b077aed3SPierre Pronchery ctx->prefix = NULL;
863b077aed3SPierre Pronchery if (!OSSL_PARAM_get_octet_string(p, (void **)&ctx->prefix, 0,
864b077aed3SPierre Pronchery &ctx->prefix_len))
865b077aed3SPierre Pronchery return 0;
866b077aed3SPierre Pronchery }
867b077aed3SPierre Pronchery
868b077aed3SPierre Pronchery if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_LABEL)) != NULL) {
869b077aed3SPierre Pronchery OPENSSL_free(ctx->label);
870b077aed3SPierre Pronchery ctx->label = NULL;
871b077aed3SPierre Pronchery if (!OSSL_PARAM_get_octet_string(p, (void **)&ctx->label, 0,
872b077aed3SPierre Pronchery &ctx->label_len))
873b077aed3SPierre Pronchery return 0;
874b077aed3SPierre Pronchery }
875b077aed3SPierre Pronchery
876b077aed3SPierre Pronchery OPENSSL_clear_free(ctx->data, ctx->data_len);
877b077aed3SPierre Pronchery ctx->data = NULL;
878b077aed3SPierre Pronchery if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_DATA)) != NULL
879b077aed3SPierre Pronchery && !OSSL_PARAM_get_octet_string(p, (void **)&ctx->data, 0,
880b077aed3SPierre Pronchery &ctx->data_len))
881b077aed3SPierre Pronchery return 0;
882*e7be843bSPierre Pronchery
883*e7be843bSPierre Pronchery #ifdef FIPS_MODULE
884*e7be843bSPierre Pronchery if (OSSL_PARAM_locate_const(params, OSSL_ALG_PARAM_DIGEST) != NULL) {
885*e7be843bSPierre Pronchery const EVP_MD *md = ossl_prov_digest_md(&ctx->digest);
886*e7be843bSPierre Pronchery
887*e7be843bSPierre Pronchery if (!fips_tls1_3_digest_check_passed(ctx, md))
888*e7be843bSPierre Pronchery return 0;
889*e7be843bSPierre Pronchery }
890*e7be843bSPierre Pronchery
891*e7be843bSPierre Pronchery if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_KEY)) != NULL)
892*e7be843bSPierre Pronchery if (!fips_tls1_3_key_check_passed(ctx))
893*e7be843bSPierre Pronchery return 0;
894*e7be843bSPierre Pronchery #endif
895*e7be843bSPierre Pronchery
896b077aed3SPierre Pronchery return 1;
897b077aed3SPierre Pronchery }
898b077aed3SPierre Pronchery
kdf_tls1_3_settable_ctx_params(ossl_unused void * ctx,ossl_unused void * provctx)899b077aed3SPierre Pronchery static const OSSL_PARAM *kdf_tls1_3_settable_ctx_params(ossl_unused void *ctx,
900b077aed3SPierre Pronchery ossl_unused void *provctx)
901b077aed3SPierre Pronchery {
902b077aed3SPierre Pronchery static const OSSL_PARAM known_settable_ctx_params[] = {
903b077aed3SPierre Pronchery HKDF_COMMON_SETTABLES,
904b077aed3SPierre Pronchery OSSL_PARAM_octet_string(OSSL_KDF_PARAM_PREFIX, NULL, 0),
905b077aed3SPierre Pronchery OSSL_PARAM_octet_string(OSSL_KDF_PARAM_LABEL, NULL, 0),
906b077aed3SPierre Pronchery OSSL_PARAM_octet_string(OSSL_KDF_PARAM_DATA, NULL, 0),
907*e7be843bSPierre Pronchery OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_KDF_PARAM_FIPS_DIGEST_CHECK)
908*e7be843bSPierre Pronchery OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_KDF_PARAM_FIPS_KEY_CHECK)
909b077aed3SPierre Pronchery OSSL_PARAM_END
910b077aed3SPierre Pronchery };
911b077aed3SPierre Pronchery return known_settable_ctx_params;
912b077aed3SPierre Pronchery }
913b077aed3SPierre Pronchery
kdf_tls1_3_get_ctx_params(void * vctx,OSSL_PARAM params[])914*e7be843bSPierre Pronchery static int kdf_tls1_3_get_ctx_params(void *vctx, OSSL_PARAM params[])
915*e7be843bSPierre Pronchery {
916*e7be843bSPierre Pronchery KDF_HKDF *ctx = (KDF_HKDF *)vctx;
917*e7be843bSPierre Pronchery
918*e7be843bSPierre Pronchery if (ossl_param_is_empty(params))
919*e7be843bSPierre Pronchery return 1;
920*e7be843bSPierre Pronchery
921*e7be843bSPierre Pronchery if (!hkdf_common_get_ctx_params(ctx, params))
922*e7be843bSPierre Pronchery return 0;
923*e7be843bSPierre Pronchery
924*e7be843bSPierre Pronchery if (!OSSL_FIPS_IND_GET_CTX_PARAM(ctx, params))
925*e7be843bSPierre Pronchery return 0;
926*e7be843bSPierre Pronchery
927*e7be843bSPierre Pronchery return 1;
928*e7be843bSPierre Pronchery }
929*e7be843bSPierre Pronchery
kdf_tls1_3_gettable_ctx_params(ossl_unused void * ctx,ossl_unused void * provctx)930*e7be843bSPierre Pronchery static const OSSL_PARAM *kdf_tls1_3_gettable_ctx_params(ossl_unused void *ctx,
931*e7be843bSPierre Pronchery ossl_unused void *provctx)
932*e7be843bSPierre Pronchery {
933*e7be843bSPierre Pronchery static const OSSL_PARAM known_gettable_ctx_params[] = {
934*e7be843bSPierre Pronchery HKDF_COMMON_GETTABLES,
935*e7be843bSPierre Pronchery OSSL_FIPS_IND_GETTABLE_CTX_PARAM()
936*e7be843bSPierre Pronchery OSSL_PARAM_END
937*e7be843bSPierre Pronchery };
938*e7be843bSPierre Pronchery return known_gettable_ctx_params;
939*e7be843bSPierre Pronchery }
940*e7be843bSPierre Pronchery
941b077aed3SPierre Pronchery const OSSL_DISPATCH ossl_kdf_tls1_3_kdf_functions[] = {
942b077aed3SPierre Pronchery { OSSL_FUNC_KDF_NEWCTX, (void(*)(void))kdf_hkdf_new },
943*e7be843bSPierre Pronchery { OSSL_FUNC_KDF_DUPCTX, (void(*)(void))kdf_hkdf_dup },
944b077aed3SPierre Pronchery { OSSL_FUNC_KDF_FREECTX, (void(*)(void))kdf_hkdf_free },
945b077aed3SPierre Pronchery { OSSL_FUNC_KDF_RESET, (void(*)(void))kdf_hkdf_reset },
946b077aed3SPierre Pronchery { OSSL_FUNC_KDF_DERIVE, (void(*)(void))kdf_tls1_3_derive },
947b077aed3SPierre Pronchery { OSSL_FUNC_KDF_SETTABLE_CTX_PARAMS,
948b077aed3SPierre Pronchery (void(*)(void))kdf_tls1_3_settable_ctx_params },
949b077aed3SPierre Pronchery { OSSL_FUNC_KDF_SET_CTX_PARAMS, (void(*)(void))kdf_tls1_3_set_ctx_params },
950b077aed3SPierre Pronchery { OSSL_FUNC_KDF_GETTABLE_CTX_PARAMS,
951*e7be843bSPierre Pronchery (void(*)(void))kdf_tls1_3_gettable_ctx_params },
952*e7be843bSPierre Pronchery { OSSL_FUNC_KDF_GET_CTX_PARAMS, (void(*)(void))kdf_tls1_3_get_ctx_params },
953*e7be843bSPierre Pronchery OSSL_DISPATCH_END
954b077aed3SPierre Pronchery };
955