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