1b077aed3SPierre Pronchery /*
2*e7be843bSPierre Pronchery * Copyright 2020-2024 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 * ECDH low level APIs are deprecated for public use, but still ok for
12b077aed3SPierre Pronchery * internal use.
13b077aed3SPierre Pronchery */
14b077aed3SPierre Pronchery #include "internal/deprecated.h"
15b077aed3SPierre Pronchery
16b077aed3SPierre Pronchery #include <string.h>
17b077aed3SPierre Pronchery #include <openssl/crypto.h>
18b077aed3SPierre Pronchery #include <openssl/evp.h>
19b077aed3SPierre Pronchery #include <openssl/core_dispatch.h>
20b077aed3SPierre Pronchery #include <openssl/core_names.h>
21b077aed3SPierre Pronchery #include <openssl/ec.h>
22b077aed3SPierre Pronchery #include <openssl/params.h>
23b077aed3SPierre Pronchery #include <openssl/err.h>
24b077aed3SPierre Pronchery #include <openssl/proverr.h>
25b077aed3SPierre Pronchery #include "prov/provider_ctx.h"
26b077aed3SPierre Pronchery #include "prov/providercommon.h"
27b077aed3SPierre Pronchery #include "prov/implementations.h"
28b077aed3SPierre Pronchery #include "prov/securitycheck.h"
29b077aed3SPierre Pronchery #include "crypto/ec.h" /* ossl_ecdh_kdf_X9_63() */
30b077aed3SPierre Pronchery
31b077aed3SPierre Pronchery static OSSL_FUNC_keyexch_newctx_fn ecdh_newctx;
32b077aed3SPierre Pronchery static OSSL_FUNC_keyexch_init_fn ecdh_init;
33b077aed3SPierre Pronchery static OSSL_FUNC_keyexch_set_peer_fn ecdh_set_peer;
34b077aed3SPierre Pronchery static OSSL_FUNC_keyexch_derive_fn ecdh_derive;
35b077aed3SPierre Pronchery static OSSL_FUNC_keyexch_freectx_fn ecdh_freectx;
36b077aed3SPierre Pronchery static OSSL_FUNC_keyexch_dupctx_fn ecdh_dupctx;
37b077aed3SPierre Pronchery static OSSL_FUNC_keyexch_set_ctx_params_fn ecdh_set_ctx_params;
38b077aed3SPierre Pronchery static OSSL_FUNC_keyexch_settable_ctx_params_fn ecdh_settable_ctx_params;
39b077aed3SPierre Pronchery static OSSL_FUNC_keyexch_get_ctx_params_fn ecdh_get_ctx_params;
40b077aed3SPierre Pronchery static OSSL_FUNC_keyexch_gettable_ctx_params_fn ecdh_gettable_ctx_params;
41b077aed3SPierre Pronchery
42b077aed3SPierre Pronchery enum kdf_type {
43b077aed3SPierre Pronchery PROV_ECDH_KDF_NONE = 0,
44b077aed3SPierre Pronchery PROV_ECDH_KDF_X9_63
45b077aed3SPierre Pronchery };
46b077aed3SPierre Pronchery
47b077aed3SPierre Pronchery /*
48b077aed3SPierre Pronchery * What's passed as an actual key is defined by the KEYMGMT interface.
49b077aed3SPierre Pronchery * We happen to know that our KEYMGMT simply passes EC_KEY structures, so
50b077aed3SPierre Pronchery * we use that here too.
51b077aed3SPierre Pronchery */
52b077aed3SPierre Pronchery
53b077aed3SPierre Pronchery typedef struct {
54b077aed3SPierre Pronchery OSSL_LIB_CTX *libctx;
55b077aed3SPierre Pronchery
56b077aed3SPierre Pronchery EC_KEY *k;
57b077aed3SPierre Pronchery EC_KEY *peerk;
58b077aed3SPierre Pronchery
59b077aed3SPierre Pronchery /*
60b077aed3SPierre Pronchery * ECDH cofactor mode:
61b077aed3SPierre Pronchery *
62b077aed3SPierre Pronchery * . 0 disabled
63b077aed3SPierre Pronchery * . 1 enabled
64b077aed3SPierre Pronchery * . -1 use cofactor mode set for k
65b077aed3SPierre Pronchery */
66b077aed3SPierre Pronchery int cofactor_mode;
67b077aed3SPierre Pronchery
68b077aed3SPierre Pronchery /************
69b077aed3SPierre Pronchery * ECDH KDF *
70b077aed3SPierre Pronchery ************/
71b077aed3SPierre Pronchery /* KDF (if any) to use for ECDH */
72b077aed3SPierre Pronchery enum kdf_type kdf_type;
73b077aed3SPierre Pronchery /* Message digest to use for key derivation */
74b077aed3SPierre Pronchery EVP_MD *kdf_md;
75b077aed3SPierre Pronchery /* User key material */
76b077aed3SPierre Pronchery unsigned char *kdf_ukm;
77b077aed3SPierre Pronchery size_t kdf_ukmlen;
78b077aed3SPierre Pronchery /* KDF output length */
79b077aed3SPierre Pronchery size_t kdf_outlen;
80*e7be843bSPierre Pronchery OSSL_FIPS_IND_DECLARE
81b077aed3SPierre Pronchery } PROV_ECDH_CTX;
82b077aed3SPierre Pronchery
83b077aed3SPierre Pronchery static
ecdh_newctx(void * provctx)84b077aed3SPierre Pronchery void *ecdh_newctx(void *provctx)
85b077aed3SPierre Pronchery {
86b077aed3SPierre Pronchery PROV_ECDH_CTX *pectx;
87b077aed3SPierre Pronchery
88b077aed3SPierre Pronchery if (!ossl_prov_is_running())
89b077aed3SPierre Pronchery return NULL;
90b077aed3SPierre Pronchery
91b077aed3SPierre Pronchery pectx = OPENSSL_zalloc(sizeof(*pectx));
92b077aed3SPierre Pronchery if (pectx == NULL)
93b077aed3SPierre Pronchery return NULL;
94b077aed3SPierre Pronchery
95b077aed3SPierre Pronchery pectx->libctx = PROV_LIBCTX_OF(provctx);
96b077aed3SPierre Pronchery pectx->cofactor_mode = -1;
97b077aed3SPierre Pronchery pectx->kdf_type = PROV_ECDH_KDF_NONE;
98*e7be843bSPierre Pronchery OSSL_FIPS_IND_INIT(pectx)
99b077aed3SPierre Pronchery
100b077aed3SPierre Pronchery return (void *)pectx;
101b077aed3SPierre Pronchery }
102b077aed3SPierre Pronchery
103b077aed3SPierre Pronchery static
ecdh_init(void * vpecdhctx,void * vecdh,const OSSL_PARAM params[])104b077aed3SPierre Pronchery int ecdh_init(void *vpecdhctx, void *vecdh, const OSSL_PARAM params[])
105b077aed3SPierre Pronchery {
106b077aed3SPierre Pronchery PROV_ECDH_CTX *pecdhctx = (PROV_ECDH_CTX *)vpecdhctx;
107b077aed3SPierre Pronchery
108b077aed3SPierre Pronchery if (!ossl_prov_is_running()
109b077aed3SPierre Pronchery || pecdhctx == NULL
110b077aed3SPierre Pronchery || vecdh == NULL
111*e7be843bSPierre Pronchery || (EC_KEY_get0_group(vecdh) == NULL)
112b077aed3SPierre Pronchery || !EC_KEY_up_ref(vecdh))
113b077aed3SPierre Pronchery return 0;
114b077aed3SPierre Pronchery EC_KEY_free(pecdhctx->k);
115b077aed3SPierre Pronchery pecdhctx->k = vecdh;
116b077aed3SPierre Pronchery pecdhctx->cofactor_mode = -1;
117b077aed3SPierre Pronchery pecdhctx->kdf_type = PROV_ECDH_KDF_NONE;
118*e7be843bSPierre Pronchery
119*e7be843bSPierre Pronchery OSSL_FIPS_IND_SET_APPROVED(pecdhctx)
120*e7be843bSPierre Pronchery if (!ecdh_set_ctx_params(pecdhctx, params))
121*e7be843bSPierre Pronchery return 0;
122*e7be843bSPierre Pronchery #ifdef FIPS_MODULE
123*e7be843bSPierre Pronchery if (!ossl_fips_ind_ec_key_check(OSSL_FIPS_IND_GET(pecdhctx),
124*e7be843bSPierre Pronchery OSSL_FIPS_IND_SETTABLE0, pecdhctx->libctx,
125*e7be843bSPierre Pronchery EC_KEY_get0_group(vecdh), "ECDH Init", 1))
126*e7be843bSPierre Pronchery return 0;
127*e7be843bSPierre Pronchery #endif
128*e7be843bSPierre Pronchery return 1;
129b077aed3SPierre Pronchery }
130b077aed3SPierre Pronchery
131b077aed3SPierre Pronchery static
ecdh_match_params(const EC_KEY * priv,const EC_KEY * peer)132b077aed3SPierre Pronchery int ecdh_match_params(const EC_KEY *priv, const EC_KEY *peer)
133b077aed3SPierre Pronchery {
134b077aed3SPierre Pronchery int ret;
135b077aed3SPierre Pronchery BN_CTX *ctx = NULL;
136b077aed3SPierre Pronchery const EC_GROUP *group_priv = EC_KEY_get0_group(priv);
137b077aed3SPierre Pronchery const EC_GROUP *group_peer = EC_KEY_get0_group(peer);
138b077aed3SPierre Pronchery
139b077aed3SPierre Pronchery ctx = BN_CTX_new_ex(ossl_ec_key_get_libctx(priv));
140b077aed3SPierre Pronchery if (ctx == NULL) {
141*e7be843bSPierre Pronchery ERR_raise(ERR_LIB_PROV, ERR_R_BN_LIB);
142b077aed3SPierre Pronchery return 0;
143b077aed3SPierre Pronchery }
144b077aed3SPierre Pronchery ret = group_priv != NULL
145b077aed3SPierre Pronchery && group_peer != NULL
146b077aed3SPierre Pronchery && EC_GROUP_cmp(group_priv, group_peer, ctx) == 0;
147b077aed3SPierre Pronchery if (!ret)
148b077aed3SPierre Pronchery ERR_raise(ERR_LIB_PROV, PROV_R_MISMATCHING_DOMAIN_PARAMETERS);
149b077aed3SPierre Pronchery BN_CTX_free(ctx);
150b077aed3SPierre Pronchery return ret;
151b077aed3SPierre Pronchery }
152b077aed3SPierre Pronchery
153b077aed3SPierre Pronchery static
ecdh_set_peer(void * vpecdhctx,void * vecdh)154b077aed3SPierre Pronchery int ecdh_set_peer(void *vpecdhctx, void *vecdh)
155b077aed3SPierre Pronchery {
156b077aed3SPierre Pronchery PROV_ECDH_CTX *pecdhctx = (PROV_ECDH_CTX *)vpecdhctx;
157b077aed3SPierre Pronchery
158b077aed3SPierre Pronchery if (!ossl_prov_is_running()
159b077aed3SPierre Pronchery || pecdhctx == NULL
160b077aed3SPierre Pronchery || vecdh == NULL
161*e7be843bSPierre Pronchery || !ecdh_match_params(pecdhctx->k, vecdh))
162*e7be843bSPierre Pronchery return 0;
163*e7be843bSPierre Pronchery #ifdef FIPS_MODULE
164*e7be843bSPierre Pronchery if (!ossl_fips_ind_ec_key_check(OSSL_FIPS_IND_GET(pecdhctx),
165*e7be843bSPierre Pronchery OSSL_FIPS_IND_SETTABLE0, pecdhctx->libctx,
166*e7be843bSPierre Pronchery EC_KEY_get0_group(vecdh), "ECDH Set Peer",
167*e7be843bSPierre Pronchery 1))
168*e7be843bSPierre Pronchery return 0;
169*e7be843bSPierre Pronchery #endif
170*e7be843bSPierre Pronchery if (!EC_KEY_up_ref(vecdh))
171b077aed3SPierre Pronchery return 0;
172b077aed3SPierre Pronchery
173b077aed3SPierre Pronchery EC_KEY_free(pecdhctx->peerk);
174b077aed3SPierre Pronchery pecdhctx->peerk = vecdh;
175b077aed3SPierre Pronchery return 1;
176b077aed3SPierre Pronchery }
177b077aed3SPierre Pronchery
178b077aed3SPierre Pronchery static
ecdh_freectx(void * vpecdhctx)179b077aed3SPierre Pronchery void ecdh_freectx(void *vpecdhctx)
180b077aed3SPierre Pronchery {
181b077aed3SPierre Pronchery PROV_ECDH_CTX *pecdhctx = (PROV_ECDH_CTX *)vpecdhctx;
182b077aed3SPierre Pronchery
183b077aed3SPierre Pronchery EC_KEY_free(pecdhctx->k);
184b077aed3SPierre Pronchery EC_KEY_free(pecdhctx->peerk);
185b077aed3SPierre Pronchery
186b077aed3SPierre Pronchery EVP_MD_free(pecdhctx->kdf_md);
187b077aed3SPierre Pronchery OPENSSL_clear_free(pecdhctx->kdf_ukm, pecdhctx->kdf_ukmlen);
188b077aed3SPierre Pronchery
189b077aed3SPierre Pronchery OPENSSL_free(pecdhctx);
190b077aed3SPierre Pronchery }
191b077aed3SPierre Pronchery
192b077aed3SPierre Pronchery static
ecdh_dupctx(void * vpecdhctx)193b077aed3SPierre Pronchery void *ecdh_dupctx(void *vpecdhctx)
194b077aed3SPierre Pronchery {
195b077aed3SPierre Pronchery PROV_ECDH_CTX *srcctx = (PROV_ECDH_CTX *)vpecdhctx;
196b077aed3SPierre Pronchery PROV_ECDH_CTX *dstctx;
197b077aed3SPierre Pronchery
198b077aed3SPierre Pronchery if (!ossl_prov_is_running())
199b077aed3SPierre Pronchery return NULL;
200b077aed3SPierre Pronchery
201b077aed3SPierre Pronchery dstctx = OPENSSL_zalloc(sizeof(*srcctx));
202b077aed3SPierre Pronchery if (dstctx == NULL)
203b077aed3SPierre Pronchery return NULL;
204b077aed3SPierre Pronchery
205b077aed3SPierre Pronchery *dstctx = *srcctx;
206b077aed3SPierre Pronchery
207b077aed3SPierre Pronchery /* clear all pointers */
208b077aed3SPierre Pronchery
209b077aed3SPierre Pronchery dstctx->k= NULL;
210b077aed3SPierre Pronchery dstctx->peerk = NULL;
211b077aed3SPierre Pronchery dstctx->kdf_md = NULL;
212b077aed3SPierre Pronchery dstctx->kdf_ukm = NULL;
213b077aed3SPierre Pronchery
214b077aed3SPierre Pronchery /* up-ref all ref-counted objects referenced in dstctx */
215b077aed3SPierre Pronchery
216b077aed3SPierre Pronchery if (srcctx->k != NULL && !EC_KEY_up_ref(srcctx->k))
217b077aed3SPierre Pronchery goto err;
218b077aed3SPierre Pronchery else
219b077aed3SPierre Pronchery dstctx->k = srcctx->k;
220b077aed3SPierre Pronchery
221b077aed3SPierre Pronchery if (srcctx->peerk != NULL && !EC_KEY_up_ref(srcctx->peerk))
222b077aed3SPierre Pronchery goto err;
223b077aed3SPierre Pronchery else
224b077aed3SPierre Pronchery dstctx->peerk = srcctx->peerk;
225b077aed3SPierre Pronchery
226b077aed3SPierre Pronchery if (srcctx->kdf_md != NULL && !EVP_MD_up_ref(srcctx->kdf_md))
227b077aed3SPierre Pronchery goto err;
228b077aed3SPierre Pronchery else
229b077aed3SPierre Pronchery dstctx->kdf_md = srcctx->kdf_md;
230b077aed3SPierre Pronchery
231b077aed3SPierre Pronchery /* Duplicate UKM data if present */
232b077aed3SPierre Pronchery if (srcctx->kdf_ukm != NULL && srcctx->kdf_ukmlen > 0) {
233b077aed3SPierre Pronchery dstctx->kdf_ukm = OPENSSL_memdup(srcctx->kdf_ukm,
234b077aed3SPierre Pronchery srcctx->kdf_ukmlen);
235b077aed3SPierre Pronchery if (dstctx->kdf_ukm == NULL)
236b077aed3SPierre Pronchery goto err;
237b077aed3SPierre Pronchery }
238b077aed3SPierre Pronchery
239b077aed3SPierre Pronchery return dstctx;
240b077aed3SPierre Pronchery
241b077aed3SPierre Pronchery err:
242b077aed3SPierre Pronchery ecdh_freectx(dstctx);
243b077aed3SPierre Pronchery return NULL;
244b077aed3SPierre Pronchery }
245b077aed3SPierre Pronchery
246b077aed3SPierre Pronchery static
ecdh_set_ctx_params(void * vpecdhctx,const OSSL_PARAM params[])247b077aed3SPierre Pronchery int ecdh_set_ctx_params(void *vpecdhctx, const OSSL_PARAM params[])
248b077aed3SPierre Pronchery {
249b077aed3SPierre Pronchery char name[80] = { '\0' }; /* should be big enough */
250b077aed3SPierre Pronchery char *str = NULL;
251b077aed3SPierre Pronchery PROV_ECDH_CTX *pectx = (PROV_ECDH_CTX *)vpecdhctx;
252b077aed3SPierre Pronchery const OSSL_PARAM *p;
253b077aed3SPierre Pronchery
254b077aed3SPierre Pronchery if (pectx == NULL)
255b077aed3SPierre Pronchery return 0;
256*e7be843bSPierre Pronchery if (ossl_param_is_empty(params))
257b077aed3SPierre Pronchery return 1;
258b077aed3SPierre Pronchery
259*e7be843bSPierre Pronchery if (!OSSL_FIPS_IND_SET_CTX_PARAM(pectx, OSSL_FIPS_IND_SETTABLE0, params,
260*e7be843bSPierre Pronchery OSSL_EXCHANGE_PARAM_FIPS_KEY_CHECK))
261*e7be843bSPierre Pronchery return 0;
262*e7be843bSPierre Pronchery if (!OSSL_FIPS_IND_SET_CTX_PARAM(pectx, OSSL_FIPS_IND_SETTABLE1, params,
263*e7be843bSPierre Pronchery OSSL_EXCHANGE_PARAM_FIPS_DIGEST_CHECK))
264*e7be843bSPierre Pronchery return 0;
265*e7be843bSPierre Pronchery if (!OSSL_FIPS_IND_SET_CTX_PARAM(pectx, OSSL_FIPS_IND_SETTABLE2, params,
266*e7be843bSPierre Pronchery OSSL_EXCHANGE_PARAM_FIPS_ECDH_COFACTOR_CHECK))
267*e7be843bSPierre Pronchery return 0;
268*e7be843bSPierre Pronchery
269b077aed3SPierre Pronchery p = OSSL_PARAM_locate_const(params, OSSL_EXCHANGE_PARAM_EC_ECDH_COFACTOR_MODE);
270b077aed3SPierre Pronchery if (p != NULL) {
271b077aed3SPierre Pronchery int mode;
272b077aed3SPierre Pronchery
273b077aed3SPierre Pronchery if (!OSSL_PARAM_get_int(p, &mode))
274b077aed3SPierre Pronchery return 0;
275b077aed3SPierre Pronchery
276b077aed3SPierre Pronchery if (mode < -1 || mode > 1)
277b077aed3SPierre Pronchery return 0;
278b077aed3SPierre Pronchery
279b077aed3SPierre Pronchery pectx->cofactor_mode = mode;
280b077aed3SPierre Pronchery }
281b077aed3SPierre Pronchery
282b077aed3SPierre Pronchery p = OSSL_PARAM_locate_const(params, OSSL_EXCHANGE_PARAM_KDF_TYPE);
283b077aed3SPierre Pronchery if (p != NULL) {
284b077aed3SPierre Pronchery str = name;
285b077aed3SPierre Pronchery if (!OSSL_PARAM_get_utf8_string(p, &str, sizeof(name)))
286b077aed3SPierre Pronchery return 0;
287b077aed3SPierre Pronchery
288b077aed3SPierre Pronchery if (name[0] == '\0')
289b077aed3SPierre Pronchery pectx->kdf_type = PROV_ECDH_KDF_NONE;
290b077aed3SPierre Pronchery else if (strcmp(name, OSSL_KDF_NAME_X963KDF) == 0)
291b077aed3SPierre Pronchery pectx->kdf_type = PROV_ECDH_KDF_X9_63;
292b077aed3SPierre Pronchery else
293b077aed3SPierre Pronchery return 0;
294b077aed3SPierre Pronchery }
295b077aed3SPierre Pronchery
296b077aed3SPierre Pronchery p = OSSL_PARAM_locate_const(params, OSSL_EXCHANGE_PARAM_KDF_DIGEST);
297b077aed3SPierre Pronchery if (p != NULL) {
298b077aed3SPierre Pronchery char mdprops[80] = { '\0' }; /* should be big enough */
299b077aed3SPierre Pronchery
300b077aed3SPierre Pronchery str = name;
301b077aed3SPierre Pronchery if (!OSSL_PARAM_get_utf8_string(p, &str, sizeof(name)))
302b077aed3SPierre Pronchery return 0;
303b077aed3SPierre Pronchery
304b077aed3SPierre Pronchery str = mdprops;
305b077aed3SPierre Pronchery p = OSSL_PARAM_locate_const(params,
306b077aed3SPierre Pronchery OSSL_EXCHANGE_PARAM_KDF_DIGEST_PROPS);
307b077aed3SPierre Pronchery
308b077aed3SPierre Pronchery if (p != NULL) {
309b077aed3SPierre Pronchery if (!OSSL_PARAM_get_utf8_string(p, &str, sizeof(mdprops)))
310b077aed3SPierre Pronchery return 0;
311b077aed3SPierre Pronchery }
312b077aed3SPierre Pronchery
313b077aed3SPierre Pronchery EVP_MD_free(pectx->kdf_md);
314b077aed3SPierre Pronchery pectx->kdf_md = EVP_MD_fetch(pectx->libctx, name, mdprops);
315b077aed3SPierre Pronchery if (pectx->kdf_md == NULL)
316b077aed3SPierre Pronchery return 0;
317*e7be843bSPierre Pronchery /* XOF digests are not allowed */
318*e7be843bSPierre Pronchery if (EVP_MD_xof(pectx->kdf_md)) {
319*e7be843bSPierre Pronchery ERR_raise(ERR_LIB_PROV, PROV_R_XOF_DIGESTS_NOT_ALLOWED);
320*e7be843bSPierre Pronchery return 0;
321*e7be843bSPierre Pronchery }
322*e7be843bSPierre Pronchery #ifdef FIPS_MODULE
323*e7be843bSPierre Pronchery if (!ossl_fips_ind_digest_exch_check(OSSL_FIPS_IND_GET(pectx),
324*e7be843bSPierre Pronchery OSSL_FIPS_IND_SETTABLE1, pectx->libctx,
325*e7be843bSPierre Pronchery pectx->kdf_md, "ECDH Set Ctx")) {
326*e7be843bSPierre Pronchery EVP_MD_free(pectx->kdf_md);
327*e7be843bSPierre Pronchery pectx->kdf_md = NULL;
328*e7be843bSPierre Pronchery return 0;
329*e7be843bSPierre Pronchery }
330*e7be843bSPierre Pronchery #endif
331b077aed3SPierre Pronchery }
332b077aed3SPierre Pronchery
333b077aed3SPierre Pronchery p = OSSL_PARAM_locate_const(params, OSSL_EXCHANGE_PARAM_KDF_OUTLEN);
334b077aed3SPierre Pronchery if (p != NULL) {
335b077aed3SPierre Pronchery size_t outlen;
336b077aed3SPierre Pronchery
337b077aed3SPierre Pronchery if (!OSSL_PARAM_get_size_t(p, &outlen))
338b077aed3SPierre Pronchery return 0;
339b077aed3SPierre Pronchery pectx->kdf_outlen = outlen;
340b077aed3SPierre Pronchery }
341b077aed3SPierre Pronchery
342b077aed3SPierre Pronchery p = OSSL_PARAM_locate_const(params, OSSL_EXCHANGE_PARAM_KDF_UKM);
343b077aed3SPierre Pronchery if (p != NULL) {
344b077aed3SPierre Pronchery void *tmp_ukm = NULL;
345b077aed3SPierre Pronchery size_t tmp_ukmlen;
346b077aed3SPierre Pronchery
347b077aed3SPierre Pronchery if (!OSSL_PARAM_get_octet_string(p, &tmp_ukm, 0, &tmp_ukmlen))
348b077aed3SPierre Pronchery return 0;
349b077aed3SPierre Pronchery OPENSSL_free(pectx->kdf_ukm);
350b077aed3SPierre Pronchery pectx->kdf_ukm = tmp_ukm;
351b077aed3SPierre Pronchery pectx->kdf_ukmlen = tmp_ukmlen;
352b077aed3SPierre Pronchery }
353b077aed3SPierre Pronchery
354b077aed3SPierre Pronchery return 1;
355b077aed3SPierre Pronchery }
356b077aed3SPierre Pronchery
357b077aed3SPierre Pronchery static const OSSL_PARAM known_settable_ctx_params[] = {
358b077aed3SPierre Pronchery OSSL_PARAM_int(OSSL_EXCHANGE_PARAM_EC_ECDH_COFACTOR_MODE, NULL),
359b077aed3SPierre Pronchery OSSL_PARAM_utf8_string(OSSL_EXCHANGE_PARAM_KDF_TYPE, NULL, 0),
360b077aed3SPierre Pronchery OSSL_PARAM_utf8_string(OSSL_EXCHANGE_PARAM_KDF_DIGEST, NULL, 0),
361b077aed3SPierre Pronchery OSSL_PARAM_utf8_string(OSSL_EXCHANGE_PARAM_KDF_DIGEST_PROPS, NULL, 0),
362b077aed3SPierre Pronchery OSSL_PARAM_size_t(OSSL_EXCHANGE_PARAM_KDF_OUTLEN, NULL),
363b077aed3SPierre Pronchery OSSL_PARAM_octet_string(OSSL_EXCHANGE_PARAM_KDF_UKM, NULL, 0),
364*e7be843bSPierre Pronchery OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_EXCHANGE_PARAM_FIPS_KEY_CHECK)
365*e7be843bSPierre Pronchery OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_EXCHANGE_PARAM_FIPS_DIGEST_CHECK)
366*e7be843bSPierre Pronchery OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_EXCHANGE_PARAM_FIPS_ECDH_COFACTOR_CHECK)
367b077aed3SPierre Pronchery OSSL_PARAM_END
368b077aed3SPierre Pronchery };
369b077aed3SPierre Pronchery
370b077aed3SPierre Pronchery static
ecdh_settable_ctx_params(ossl_unused void * vpecdhctx,ossl_unused void * provctx)371b077aed3SPierre Pronchery const OSSL_PARAM *ecdh_settable_ctx_params(ossl_unused void *vpecdhctx,
372b077aed3SPierre Pronchery ossl_unused void *provctx)
373b077aed3SPierre Pronchery {
374b077aed3SPierre Pronchery return known_settable_ctx_params;
375b077aed3SPierre Pronchery }
376b077aed3SPierre Pronchery
377b077aed3SPierre Pronchery static
ecdh_get_ctx_params(void * vpecdhctx,OSSL_PARAM params[])378b077aed3SPierre Pronchery int ecdh_get_ctx_params(void *vpecdhctx, OSSL_PARAM params[])
379b077aed3SPierre Pronchery {
380b077aed3SPierre Pronchery PROV_ECDH_CTX *pectx = (PROV_ECDH_CTX *)vpecdhctx;
381b077aed3SPierre Pronchery OSSL_PARAM *p;
382b077aed3SPierre Pronchery
383b077aed3SPierre Pronchery if (pectx == NULL)
384b077aed3SPierre Pronchery return 0;
385b077aed3SPierre Pronchery
386b077aed3SPierre Pronchery p = OSSL_PARAM_locate(params, OSSL_EXCHANGE_PARAM_EC_ECDH_COFACTOR_MODE);
387b077aed3SPierre Pronchery if (p != NULL) {
388b077aed3SPierre Pronchery int mode = pectx->cofactor_mode;
389b077aed3SPierre Pronchery
390b077aed3SPierre Pronchery if (mode == -1) {
391b077aed3SPierre Pronchery /* check what is the default for pecdhctx->k */
392b077aed3SPierre Pronchery mode = EC_KEY_get_flags(pectx->k) & EC_FLAG_COFACTOR_ECDH ? 1 : 0;
393b077aed3SPierre Pronchery }
394b077aed3SPierre Pronchery
395b077aed3SPierre Pronchery if (!OSSL_PARAM_set_int(p, mode))
396b077aed3SPierre Pronchery return 0;
397b077aed3SPierre Pronchery }
398b077aed3SPierre Pronchery
399b077aed3SPierre Pronchery p = OSSL_PARAM_locate(params, OSSL_EXCHANGE_PARAM_KDF_TYPE);
400b077aed3SPierre Pronchery if (p != NULL) {
401b077aed3SPierre Pronchery const char *kdf_type = NULL;
402b077aed3SPierre Pronchery
403b077aed3SPierre Pronchery switch (pectx->kdf_type) {
404b077aed3SPierre Pronchery case PROV_ECDH_KDF_NONE:
405b077aed3SPierre Pronchery kdf_type = "";
406b077aed3SPierre Pronchery break;
407b077aed3SPierre Pronchery case PROV_ECDH_KDF_X9_63:
408b077aed3SPierre Pronchery kdf_type = OSSL_KDF_NAME_X963KDF;
409b077aed3SPierre Pronchery break;
410b077aed3SPierre Pronchery default:
411b077aed3SPierre Pronchery return 0;
412b077aed3SPierre Pronchery }
413b077aed3SPierre Pronchery
414b077aed3SPierre Pronchery if (!OSSL_PARAM_set_utf8_string(p, kdf_type))
415b077aed3SPierre Pronchery return 0;
416b077aed3SPierre Pronchery }
417b077aed3SPierre Pronchery
418b077aed3SPierre Pronchery p = OSSL_PARAM_locate(params, OSSL_EXCHANGE_PARAM_KDF_DIGEST);
419b077aed3SPierre Pronchery if (p != NULL
420b077aed3SPierre Pronchery && !OSSL_PARAM_set_utf8_string(p, pectx->kdf_md == NULL
421b077aed3SPierre Pronchery ? ""
422b077aed3SPierre Pronchery : EVP_MD_get0_name(pectx->kdf_md))) {
423b077aed3SPierre Pronchery return 0;
424b077aed3SPierre Pronchery }
425b077aed3SPierre Pronchery
426b077aed3SPierre Pronchery p = OSSL_PARAM_locate(params, OSSL_EXCHANGE_PARAM_KDF_OUTLEN);
427b077aed3SPierre Pronchery if (p != NULL && !OSSL_PARAM_set_size_t(p, pectx->kdf_outlen))
428b077aed3SPierre Pronchery return 0;
429b077aed3SPierre Pronchery
430b077aed3SPierre Pronchery p = OSSL_PARAM_locate(params, OSSL_EXCHANGE_PARAM_KDF_UKM);
431b077aed3SPierre Pronchery if (p != NULL &&
432b077aed3SPierre Pronchery !OSSL_PARAM_set_octet_ptr(p, pectx->kdf_ukm, pectx->kdf_ukmlen))
433b077aed3SPierre Pronchery return 0;
434*e7be843bSPierre Pronchery if (!OSSL_FIPS_IND_GET_CTX_PARAM(pectx, params))
435*e7be843bSPierre Pronchery return 0;
436b077aed3SPierre Pronchery return 1;
437b077aed3SPierre Pronchery }
438b077aed3SPierre Pronchery
439b077aed3SPierre Pronchery static const OSSL_PARAM known_gettable_ctx_params[] = {
440b077aed3SPierre Pronchery OSSL_PARAM_int(OSSL_EXCHANGE_PARAM_EC_ECDH_COFACTOR_MODE, NULL),
441b077aed3SPierre Pronchery OSSL_PARAM_utf8_string(OSSL_EXCHANGE_PARAM_KDF_TYPE, NULL, 0),
442b077aed3SPierre Pronchery OSSL_PARAM_utf8_string(OSSL_EXCHANGE_PARAM_KDF_DIGEST, NULL, 0),
443b077aed3SPierre Pronchery OSSL_PARAM_size_t(OSSL_EXCHANGE_PARAM_KDF_OUTLEN, NULL),
444b077aed3SPierre Pronchery OSSL_PARAM_DEFN(OSSL_EXCHANGE_PARAM_KDF_UKM, OSSL_PARAM_OCTET_PTR,
445b077aed3SPierre Pronchery NULL, 0),
446*e7be843bSPierre Pronchery OSSL_FIPS_IND_GETTABLE_CTX_PARAM()
447b077aed3SPierre Pronchery OSSL_PARAM_END
448b077aed3SPierre Pronchery };
449b077aed3SPierre Pronchery
450b077aed3SPierre Pronchery static
ecdh_gettable_ctx_params(ossl_unused void * vpecdhctx,ossl_unused void * provctx)451b077aed3SPierre Pronchery const OSSL_PARAM *ecdh_gettable_ctx_params(ossl_unused void *vpecdhctx,
452b077aed3SPierre Pronchery ossl_unused void *provctx)
453b077aed3SPierre Pronchery {
454b077aed3SPierre Pronchery return known_gettable_ctx_params;
455b077aed3SPierre Pronchery }
456b077aed3SPierre Pronchery
457b077aed3SPierre Pronchery static ossl_inline
ecdh_size(const EC_KEY * k)458b077aed3SPierre Pronchery size_t ecdh_size(const EC_KEY *k)
459b077aed3SPierre Pronchery {
460b077aed3SPierre Pronchery size_t degree = 0;
461b077aed3SPierre Pronchery const EC_GROUP *group;
462b077aed3SPierre Pronchery
463b077aed3SPierre Pronchery if (k == NULL
464b077aed3SPierre Pronchery || (group = EC_KEY_get0_group(k)) == NULL)
465b077aed3SPierre Pronchery return 0;
466b077aed3SPierre Pronchery
467b077aed3SPierre Pronchery degree = EC_GROUP_get_degree(group);
468b077aed3SPierre Pronchery
469b077aed3SPierre Pronchery return (degree + 7) / 8;
470b077aed3SPierre Pronchery }
471b077aed3SPierre Pronchery
472b077aed3SPierre Pronchery static ossl_inline
ecdh_plain_derive(void * vpecdhctx,unsigned char * secret,size_t * psecretlen,size_t outlen)473b077aed3SPierre Pronchery int ecdh_plain_derive(void *vpecdhctx, unsigned char *secret,
474b077aed3SPierre Pronchery size_t *psecretlen, size_t outlen)
475b077aed3SPierre Pronchery {
476b077aed3SPierre Pronchery PROV_ECDH_CTX *pecdhctx = (PROV_ECDH_CTX *)vpecdhctx;
477b077aed3SPierre Pronchery int retlen, ret = 0;
478b077aed3SPierre Pronchery size_t ecdhsize, size;
479b077aed3SPierre Pronchery const EC_POINT *ppubkey = NULL;
480b077aed3SPierre Pronchery EC_KEY *privk = NULL;
481b077aed3SPierre Pronchery const EC_GROUP *group;
482b077aed3SPierre Pronchery const BIGNUM *cofactor;
483b077aed3SPierre Pronchery int key_cofactor_mode;
484*e7be843bSPierre Pronchery int has_cofactor;
485*e7be843bSPierre Pronchery #ifdef FIPS_MODULE
486*e7be843bSPierre Pronchery int cofactor_approved = 0;
487*e7be843bSPierre Pronchery #endif
488b077aed3SPierre Pronchery
489b077aed3SPierre Pronchery if (pecdhctx->k == NULL || pecdhctx->peerk == NULL) {
490b077aed3SPierre Pronchery ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY);
491b077aed3SPierre Pronchery return 0;
492b077aed3SPierre Pronchery }
493b077aed3SPierre Pronchery
494b077aed3SPierre Pronchery ecdhsize = ecdh_size(pecdhctx->k);
495b077aed3SPierre Pronchery if (secret == NULL) {
496b077aed3SPierre Pronchery *psecretlen = ecdhsize;
497b077aed3SPierre Pronchery return 1;
498b077aed3SPierre Pronchery }
499b077aed3SPierre Pronchery
500b077aed3SPierre Pronchery if ((group = EC_KEY_get0_group(pecdhctx->k)) == NULL
501b077aed3SPierre Pronchery || (cofactor = EC_GROUP_get0_cofactor(group)) == NULL)
502b077aed3SPierre Pronchery return 0;
503b077aed3SPierre Pronchery
504*e7be843bSPierre Pronchery has_cofactor = !BN_is_one(cofactor);
505*e7be843bSPierre Pronchery
506b077aed3SPierre Pronchery /*
507b077aed3SPierre Pronchery * NB: unlike PKCS#3 DH, if outlen is less than maximum size this is not
508b077aed3SPierre Pronchery * an error, the result is truncated.
509b077aed3SPierre Pronchery */
510b077aed3SPierre Pronchery size = outlen < ecdhsize ? outlen : ecdhsize;
511b077aed3SPierre Pronchery
512b077aed3SPierre Pronchery /*
513b077aed3SPierre Pronchery * The ctx->cofactor_mode flag has precedence over the
514b077aed3SPierre Pronchery * cofactor_mode flag set on ctx->k.
515b077aed3SPierre Pronchery *
516b077aed3SPierre Pronchery * - if ctx->cofactor_mode == -1, use ctx->k directly
517b077aed3SPierre Pronchery * - if ctx->cofactor_mode == key_cofactor_mode, use ctx->k directly
518b077aed3SPierre Pronchery * - if ctx->cofactor_mode != key_cofactor_mode:
519b077aed3SPierre Pronchery * - if ctx->k->cofactor == 1, the cofactor_mode flag is irrelevant, use
520b077aed3SPierre Pronchery * ctx->k directly
521b077aed3SPierre Pronchery * - if ctx->k->cofactor != 1, use a duplicate of ctx->k with the flag
522b077aed3SPierre Pronchery * set to ctx->cofactor_mode
523b077aed3SPierre Pronchery */
524b077aed3SPierre Pronchery key_cofactor_mode =
525b077aed3SPierre Pronchery (EC_KEY_get_flags(pecdhctx->k) & EC_FLAG_COFACTOR_ECDH) ? 1 : 0;
526b077aed3SPierre Pronchery if (pecdhctx->cofactor_mode != -1
527b077aed3SPierre Pronchery && pecdhctx->cofactor_mode != key_cofactor_mode
528*e7be843bSPierre Pronchery && has_cofactor) {
529b077aed3SPierre Pronchery if ((privk = EC_KEY_dup(pecdhctx->k)) == NULL)
530b077aed3SPierre Pronchery return 0;
531b077aed3SPierre Pronchery
532*e7be843bSPierre Pronchery if (pecdhctx->cofactor_mode == 1) {
533b077aed3SPierre Pronchery EC_KEY_set_flags(privk, EC_FLAG_COFACTOR_ECDH);
534*e7be843bSPierre Pronchery #ifdef FIPS_MODULE
535*e7be843bSPierre Pronchery cofactor_approved = 1;
536*e7be843bSPierre Pronchery #endif
537*e7be843bSPierre Pronchery } else {
538b077aed3SPierre Pronchery EC_KEY_clear_flags(privk, EC_FLAG_COFACTOR_ECDH);
539*e7be843bSPierre Pronchery }
540b077aed3SPierre Pronchery } else {
541b077aed3SPierre Pronchery privk = pecdhctx->k;
542*e7be843bSPierre Pronchery #ifdef FIPS_MODULE
543*e7be843bSPierre Pronchery cofactor_approved = key_cofactor_mode;
544*e7be843bSPierre Pronchery #endif
545b077aed3SPierre Pronchery }
546b077aed3SPierre Pronchery
547*e7be843bSPierre Pronchery #ifdef FIPS_MODULE
548*e7be843bSPierre Pronchery /*
549*e7be843bSPierre Pronchery * SP800-56A r3 Section 5.7.1.2 requires ECC Cofactor DH to be used.
550*e7be843bSPierre Pronchery * This applies to the 'B' and 'K' curves that have cofactors that are not 1.
551*e7be843bSPierre Pronchery */
552*e7be843bSPierre Pronchery if (has_cofactor && !cofactor_approved) {
553*e7be843bSPierre Pronchery if (!OSSL_FIPS_IND_ON_UNAPPROVED(pecdhctx, OSSL_FIPS_IND_SETTABLE2,
554*e7be843bSPierre Pronchery pecdhctx->libctx, "ECDH", "Cofactor",
555*e7be843bSPierre Pronchery ossl_fips_config_ecdh_cofactor_check)) {
556*e7be843bSPierre Pronchery ERR_raise(ERR_LIB_PROV, PROV_R_COFACTOR_REQUIRED);
557*e7be843bSPierre Pronchery goto end;
558*e7be843bSPierre Pronchery }
559*e7be843bSPierre Pronchery }
560*e7be843bSPierre Pronchery #endif
561*e7be843bSPierre Pronchery
562b077aed3SPierre Pronchery ppubkey = EC_KEY_get0_public_key(pecdhctx->peerk);
563b077aed3SPierre Pronchery
564b077aed3SPierre Pronchery retlen = ECDH_compute_key(secret, size, ppubkey, privk, NULL);
565b077aed3SPierre Pronchery
566b077aed3SPierre Pronchery if (retlen <= 0)
567b077aed3SPierre Pronchery goto end;
568b077aed3SPierre Pronchery
569b077aed3SPierre Pronchery *psecretlen = retlen;
570b077aed3SPierre Pronchery ret = 1;
571b077aed3SPierre Pronchery
572b077aed3SPierre Pronchery end:
573b077aed3SPierre Pronchery if (privk != pecdhctx->k)
574b077aed3SPierre Pronchery EC_KEY_free(privk);
575b077aed3SPierre Pronchery return ret;
576b077aed3SPierre Pronchery }
577b077aed3SPierre Pronchery
578b077aed3SPierre Pronchery static ossl_inline
ecdh_X9_63_kdf_derive(void * vpecdhctx,unsigned char * secret,size_t * psecretlen,size_t outlen)579b077aed3SPierre Pronchery int ecdh_X9_63_kdf_derive(void *vpecdhctx, unsigned char *secret,
580b077aed3SPierre Pronchery size_t *psecretlen, size_t outlen)
581b077aed3SPierre Pronchery {
582b077aed3SPierre Pronchery PROV_ECDH_CTX *pecdhctx = (PROV_ECDH_CTX *)vpecdhctx;
583b077aed3SPierre Pronchery unsigned char *stmp = NULL;
584b077aed3SPierre Pronchery size_t stmplen;
585b077aed3SPierre Pronchery int ret = 0;
586b077aed3SPierre Pronchery
587b077aed3SPierre Pronchery if (secret == NULL) {
588b077aed3SPierre Pronchery *psecretlen = pecdhctx->kdf_outlen;
589b077aed3SPierre Pronchery return 1;
590b077aed3SPierre Pronchery }
591b077aed3SPierre Pronchery
592b077aed3SPierre Pronchery if (pecdhctx->kdf_outlen > outlen) {
593b077aed3SPierre Pronchery ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL);
594b077aed3SPierre Pronchery return 0;
595b077aed3SPierre Pronchery }
596b077aed3SPierre Pronchery if (!ecdh_plain_derive(vpecdhctx, NULL, &stmplen, 0))
597b077aed3SPierre Pronchery return 0;
598*e7be843bSPierre Pronchery if ((stmp = OPENSSL_secure_malloc(stmplen)) == NULL)
599b077aed3SPierre Pronchery return 0;
600b077aed3SPierre Pronchery if (!ecdh_plain_derive(vpecdhctx, stmp, &stmplen, stmplen))
601b077aed3SPierre Pronchery goto err;
602b077aed3SPierre Pronchery
603b077aed3SPierre Pronchery /* Do KDF stuff */
604b077aed3SPierre Pronchery if (!ossl_ecdh_kdf_X9_63(secret, pecdhctx->kdf_outlen,
605b077aed3SPierre Pronchery stmp, stmplen,
606b077aed3SPierre Pronchery pecdhctx->kdf_ukm,
607b077aed3SPierre Pronchery pecdhctx->kdf_ukmlen,
608b077aed3SPierre Pronchery pecdhctx->kdf_md,
609b077aed3SPierre Pronchery pecdhctx->libctx, NULL))
610b077aed3SPierre Pronchery goto err;
611b077aed3SPierre Pronchery *psecretlen = pecdhctx->kdf_outlen;
612b077aed3SPierre Pronchery ret = 1;
613b077aed3SPierre Pronchery
614b077aed3SPierre Pronchery err:
615b077aed3SPierre Pronchery OPENSSL_secure_clear_free(stmp, stmplen);
616b077aed3SPierre Pronchery return ret;
617b077aed3SPierre Pronchery }
618b077aed3SPierre Pronchery
619b077aed3SPierre Pronchery static
ecdh_derive(void * vpecdhctx,unsigned char * secret,size_t * psecretlen,size_t outlen)620b077aed3SPierre Pronchery int ecdh_derive(void *vpecdhctx, unsigned char *secret,
621b077aed3SPierre Pronchery size_t *psecretlen, size_t outlen)
622b077aed3SPierre Pronchery {
623b077aed3SPierre Pronchery PROV_ECDH_CTX *pecdhctx = (PROV_ECDH_CTX *)vpecdhctx;
624b077aed3SPierre Pronchery
625b077aed3SPierre Pronchery switch (pecdhctx->kdf_type) {
626b077aed3SPierre Pronchery case PROV_ECDH_KDF_NONE:
627b077aed3SPierre Pronchery return ecdh_plain_derive(vpecdhctx, secret, psecretlen, outlen);
628b077aed3SPierre Pronchery case PROV_ECDH_KDF_X9_63:
629b077aed3SPierre Pronchery return ecdh_X9_63_kdf_derive(vpecdhctx, secret, psecretlen, outlen);
630b077aed3SPierre Pronchery default:
631b077aed3SPierre Pronchery break;
632b077aed3SPierre Pronchery }
633b077aed3SPierre Pronchery return 0;
634b077aed3SPierre Pronchery }
635b077aed3SPierre Pronchery
636b077aed3SPierre Pronchery const OSSL_DISPATCH ossl_ecdh_keyexch_functions[] = {
637b077aed3SPierre Pronchery { OSSL_FUNC_KEYEXCH_NEWCTX, (void (*)(void))ecdh_newctx },
638b077aed3SPierre Pronchery { OSSL_FUNC_KEYEXCH_INIT, (void (*)(void))ecdh_init },
639b077aed3SPierre Pronchery { OSSL_FUNC_KEYEXCH_DERIVE, (void (*)(void))ecdh_derive },
640b077aed3SPierre Pronchery { OSSL_FUNC_KEYEXCH_SET_PEER, (void (*)(void))ecdh_set_peer },
641b077aed3SPierre Pronchery { OSSL_FUNC_KEYEXCH_FREECTX, (void (*)(void))ecdh_freectx },
642b077aed3SPierre Pronchery { OSSL_FUNC_KEYEXCH_DUPCTX, (void (*)(void))ecdh_dupctx },
643b077aed3SPierre Pronchery { OSSL_FUNC_KEYEXCH_SET_CTX_PARAMS, (void (*)(void))ecdh_set_ctx_params },
644b077aed3SPierre Pronchery { OSSL_FUNC_KEYEXCH_SETTABLE_CTX_PARAMS,
645b077aed3SPierre Pronchery (void (*)(void))ecdh_settable_ctx_params },
646b077aed3SPierre Pronchery { OSSL_FUNC_KEYEXCH_GET_CTX_PARAMS, (void (*)(void))ecdh_get_ctx_params },
647b077aed3SPierre Pronchery { OSSL_FUNC_KEYEXCH_GETTABLE_CTX_PARAMS,
648b077aed3SPierre Pronchery (void (*)(void))ecdh_gettable_ctx_params },
649*e7be843bSPierre Pronchery OSSL_DISPATCH_END
650b077aed3SPierre Pronchery };
651