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