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