Path: blob/main/crypto/openssl/providers/implementations/kem/ecx_kem.c
48383 views
/*1* Copyright 2022-2025 The OpenSSL Project Authors. All Rights Reserved.2*3* Licensed under the Apache License 2.0 (the "License"). You may not use4* this file except in compliance with the License. You can obtain a copy5* in the file LICENSE in the source distribution or at6* https://www.openssl.org/source/license.html7*/89/*10* The following implementation is part of RFC 9180 related to DHKEM using11* ECX keys (i.e. X25519 and X448)12* References to Sections in the comments below refer to RFC 9180.13*/1415#include "internal/deprecated.h"1617#include <string.h>18#include <openssl/crypto.h>19#include <openssl/evp.h>20#include <openssl/core_dispatch.h>21#include <openssl/core_names.h>22#include <openssl/params.h>23#include <openssl/kdf.h>24#include <openssl/err.h>25#include <openssl/sha.h>26#include <openssl/rand.h>27#include <openssl/proverr.h>28#include "prov/provider_ctx.h"29#include "prov/implementations.h"30#include "prov/securitycheck.h"31#include "prov/providercommon.h"32#include "prov/ecx.h"33#include "crypto/ecx.h"34#include <openssl/hpke.h>35#include "internal/hpke_util.h"36#include "eckem.h"3738#define MAX_ECX_KEYLEN X448_KEYLEN3940/* KEM identifiers from Section 7.1 "Table 2 KEM IDs" */41#define KEMID_X25519_HKDF_SHA256 0x2042#define KEMID_X448_HKDF_SHA512 0x214344/* ASCII: "KEM", in hex for EBCDIC compatibility */45static const char LABEL_KEM[] = "\x4b\x45\x4d";4647typedef struct {48ECX_KEY *recipient_key;49ECX_KEY *sender_authkey;50OSSL_LIB_CTX *libctx;51char *propq;52unsigned int mode;53unsigned int op;54unsigned char *ikm;55size_t ikmlen;56const char *kdfname;57const OSSL_HPKE_KEM_INFO *info;58} PROV_ECX_CTX;5960static OSSL_FUNC_kem_newctx_fn ecxkem_newctx;61static OSSL_FUNC_kem_encapsulate_init_fn ecxkem_encapsulate_init;62static OSSL_FUNC_kem_encapsulate_fn ecxkem_encapsulate;63static OSSL_FUNC_kem_decapsulate_init_fn ecxkem_decapsulate_init;64static OSSL_FUNC_kem_decapsulate_fn ecxkem_decapsulate;65static OSSL_FUNC_kem_freectx_fn ecxkem_freectx;66static OSSL_FUNC_kem_set_ctx_params_fn ecxkem_set_ctx_params;67static OSSL_FUNC_kem_auth_encapsulate_init_fn ecxkem_auth_encapsulate_init;68static OSSL_FUNC_kem_auth_decapsulate_init_fn ecxkem_auth_decapsulate_init;6970/*71* Set KEM values as specified in Section 7.1 "Table 2 KEM IDs"72* There is only one set of values for X25519 and X448.73* Additional values could be set via set_params if required.74*/75static const OSSL_HPKE_KEM_INFO *get_kem_info(ECX_KEY *ecx)76{77const char *name = NULL;7879if (ecx->type == ECX_KEY_TYPE_X25519)80name = SN_X25519;81else82name = SN_X448;83return ossl_HPKE_KEM_INFO_find_curve(name);84}8586/*87* Set the recipient key, and free any existing key.88* ecx can be NULL. The ecx key may have only a private or public component.89*/90static int recipient_key_set(PROV_ECX_CTX *ctx, ECX_KEY *ecx)91{92ossl_ecx_key_free(ctx->recipient_key);93ctx->recipient_key = NULL;94if (ecx != NULL) {95ctx->info = get_kem_info(ecx);96if (ctx->info == NULL)97return -2;98ctx->kdfname = "HKDF";99if (!ossl_ecx_key_up_ref(ecx))100return 0;101ctx->recipient_key = ecx;102}103return 1;104}105106/*107* Set the senders auth key, and free any existing auth key.108* ecx can be NULL.109*/110static int sender_authkey_set(PROV_ECX_CTX *ctx, ECX_KEY *ecx)111{112ossl_ecx_key_free(ctx->sender_authkey);113ctx->sender_authkey = NULL;114115if (ecx != NULL) {116if (!ossl_ecx_key_up_ref(ecx))117return 0;118ctx->sender_authkey = ecx;119}120return 1;121}122123/*124* Serialize a public key from byte array's for the encoded public keys.125* ctx is used to access the key type.126* Returns: The created ECX_KEY or NULL on error.127*/128static ECX_KEY *ecxkey_pubfromdata(PROV_ECX_CTX *ctx,129const unsigned char *pubbuf, size_t pubbuflen)130{131ECX_KEY *ecx = NULL;132OSSL_PARAM params[2], *p = params;133134*p++ = OSSL_PARAM_construct_octet_string(OSSL_PKEY_PARAM_PUB_KEY,135(char *)pubbuf, pubbuflen);136*p = OSSL_PARAM_construct_end();137138ecx = ossl_ecx_key_new(ctx->libctx, ctx->recipient_key->type, 1, ctx->propq);139if (ecx == NULL)140return NULL;141if (ossl_ecx_key_fromdata(ecx, params, 0) <= 0) {142ossl_ecx_key_free(ecx);143ecx = NULL;144}145return ecx;146}147148static unsigned char *ecx_pubkey(ECX_KEY *ecx)149{150if (ecx == NULL || !ecx->haspubkey) {151ERR_raise(ERR_LIB_PROV, PROV_R_NOT_A_PUBLIC_KEY);152return 0;153}154return ecx->pubkey;155}156157static void *ecxkem_newctx(void *provctx)158{159PROV_ECX_CTX *ctx = OPENSSL_zalloc(sizeof(PROV_ECX_CTX));160161if (ctx == NULL)162return NULL;163ctx->libctx = PROV_LIBCTX_OF(provctx);164ctx->mode = KEM_MODE_DHKEM;165166return ctx;167}168169static void ecxkem_freectx(void *vectx)170{171PROV_ECX_CTX *ctx = (PROV_ECX_CTX *)vectx;172173OPENSSL_clear_free(ctx->ikm, ctx->ikmlen);174recipient_key_set(ctx, NULL);175sender_authkey_set(ctx, NULL);176OPENSSL_free(ctx);177}178179static int ecx_match_params(const ECX_KEY *key1, const ECX_KEY *key2)180{181return (key1->type == key2->type && key1->keylen == key2->keylen);182}183184static int ecx_key_check(const ECX_KEY *ecx, int requires_privatekey)185{186if (ecx->privkey == NULL)187return (requires_privatekey == 0);188return 1;189}190191static int ecxkem_init(void *vecxctx, int operation, void *vecx, void *vauth,192ossl_unused const OSSL_PARAM params[])193{194int rv;195PROV_ECX_CTX *ctx = (PROV_ECX_CTX *)vecxctx;196ECX_KEY *ecx = vecx;197ECX_KEY *auth = vauth;198199if (!ossl_prov_is_running())200return 0;201202if (!ecx_key_check(ecx, operation == EVP_PKEY_OP_DECAPSULATE))203return 0;204rv = recipient_key_set(ctx, ecx);205if (rv <= 0)206return rv;207208if (auth != NULL) {209if (!ecx_match_params(auth, ctx->recipient_key)210|| !ecx_key_check(auth, operation == EVP_PKEY_OP_ENCAPSULATE)211|| !sender_authkey_set(ctx, auth))212return 0;213}214215ctx->op = operation;216return ecxkem_set_ctx_params(vecxctx, params);217}218219static int ecxkem_encapsulate_init(void *vecxctx, void *vecx,220const OSSL_PARAM params[])221{222return ecxkem_init(vecxctx, EVP_PKEY_OP_ENCAPSULATE, vecx, NULL, params);223}224225static int ecxkem_decapsulate_init(void *vecxctx, void *vecx,226const OSSL_PARAM params[])227{228return ecxkem_init(vecxctx, EVP_PKEY_OP_DECAPSULATE, vecx, NULL, params);229}230231static int ecxkem_auth_encapsulate_init(void *vctx, void *vecx, void *vauthpriv,232const OSSL_PARAM params[])233{234return ecxkem_init(vctx, EVP_PKEY_OP_ENCAPSULATE, vecx, vauthpriv, params);235}236237static int ecxkem_auth_decapsulate_init(void *vctx, void *vecx, void *vauthpub,238const OSSL_PARAM params[])239{240return ecxkem_init(vctx, EVP_PKEY_OP_DECAPSULATE, vecx, vauthpub, params);241}242243static int ecxkem_set_ctx_params(void *vctx, const OSSL_PARAM params[])244{245PROV_ECX_CTX *ctx = (PROV_ECX_CTX *)vctx;246const OSSL_PARAM *p;247int mode;248249if (ctx == NULL)250return 0;251if (ossl_param_is_empty(params))252return 1;253254p = OSSL_PARAM_locate_const(params, OSSL_KEM_PARAM_IKME);255if (p != NULL) {256void *tmp = NULL;257size_t tmplen = 0;258259if (p->data != NULL && p->data_size != 0) {260if (!OSSL_PARAM_get_octet_string(p, &tmp, 0, &tmplen))261return 0;262}263OPENSSL_clear_free(ctx->ikm, ctx->ikmlen);264ctx->ikm = tmp;265ctx->ikmlen = tmplen;266}267p = OSSL_PARAM_locate_const(params, OSSL_KEM_PARAM_OPERATION);268if (p != NULL) {269if (p->data_type != OSSL_PARAM_UTF8_STRING)270return 0;271mode = ossl_eckem_modename2id(p->data);272if (mode == KEM_MODE_UNDEFINED)273return 0;274ctx->mode = mode;275}276return 1;277}278279static const OSSL_PARAM known_settable_ecxkem_ctx_params[] = {280OSSL_PARAM_utf8_string(OSSL_KEM_PARAM_OPERATION, NULL, 0),281OSSL_PARAM_octet_string(OSSL_KEM_PARAM_IKME, NULL, 0),282OSSL_PARAM_END283};284285static const OSSL_PARAM *ecxkem_settable_ctx_params(ossl_unused void *vctx,286ossl_unused void *provctx)287{288return known_settable_ecxkem_ctx_params;289}290291/*292* See Section 4.1 DH-Based KEM (DHKEM) ExtractAndExpand293*/294static int dhkem_extract_and_expand(EVP_KDF_CTX *kctx,295unsigned char *okm, size_t okmlen,296uint16_t kemid,297const unsigned char *dhkm, size_t dhkmlen,298const unsigned char *kemctx,299size_t kemctxlen)300{301uint8_t suiteid[2];302uint8_t prk[EVP_MAX_MD_SIZE];303size_t prklen = okmlen; /* Nh */304int ret;305306if (prklen > sizeof(prk))307return 0;308309suiteid[0] = (kemid >> 8) &0xff;310suiteid[1] = kemid & 0xff;311312ret = ossl_hpke_labeled_extract(kctx, prk, prklen,313NULL, 0, LABEL_KEM, suiteid, sizeof(suiteid),314OSSL_DHKEM_LABEL_EAE_PRK, dhkm, dhkmlen)315&& ossl_hpke_labeled_expand(kctx, okm, okmlen, prk, prklen,316LABEL_KEM, suiteid, sizeof(suiteid),317OSSL_DHKEM_LABEL_SHARED_SECRET,318kemctx, kemctxlen);319OPENSSL_cleanse(prk, prklen);320return ret;321}322323/*324* See Section 7.1.3 DeriveKeyPair.325*326* This function is used by ecx keygen.327* (For this reason it does not use any of the state stored in PROV_ECX_CTX).328*329* Params:330* ecx An initialized ecx key.331* privout The buffer to store the generated private key into (it is assumed332* this is of length ecx->keylen).333* ikm buffer containing the input key material (seed). This must be non NULL.334* ikmlen size of the ikm buffer in bytes335* Returns:336* 1 if successful or 0 otherwise.337*/338int ossl_ecx_dhkem_derive_private(ECX_KEY *ecx, unsigned char *privout,339const unsigned char *ikm, size_t ikmlen)340{341int ret = 0;342EVP_KDF_CTX *kdfctx = NULL;343unsigned char prk[EVP_MAX_MD_SIZE];344uint8_t suiteid[2];345const OSSL_HPKE_KEM_INFO *info = get_kem_info(ecx);346347/* ikmlen should have a length of at least Nsk */348if (ikmlen < info->Nsk) {349ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_INPUT_LENGTH,350"ikm length is :%zu, should be at least %zu",351ikmlen, info->Nsk);352goto err;353}354355kdfctx = ossl_kdf_ctx_create("HKDF", info->mdname, ecx->libctx, ecx->propq);356if (kdfctx == NULL)357return 0;358359suiteid[0] = info->kem_id / 256;360suiteid[1] = info->kem_id % 256;361362if (!ossl_hpke_labeled_extract(kdfctx, prk, info->Nsecret,363NULL, 0, LABEL_KEM, suiteid, sizeof(suiteid),364OSSL_DHKEM_LABEL_DKP_PRK, ikm, ikmlen))365goto err;366367if (!ossl_hpke_labeled_expand(kdfctx, privout, info->Nsk, prk, info->Nsecret,368LABEL_KEM, suiteid, sizeof(suiteid),369OSSL_DHKEM_LABEL_SK, NULL, 0))370goto err;371ret = 1;372err:373OPENSSL_cleanse(prk, sizeof(prk));374EVP_KDF_CTX_free(kdfctx);375return ret;376}377378/*379* Do a keygen operation without having to use EVP_PKEY.380* Params:381* ctx Context object382* ikm The seed material - if this is NULL, then a random seed is used.383* Returns:384* The generated ECX key, or NULL on failure.385*/386static ECX_KEY *derivekey(PROV_ECX_CTX *ctx,387const unsigned char *ikm, size_t ikmlen)388{389int ok = 0;390ECX_KEY *key;391unsigned char *privkey;392unsigned char *seed = (unsigned char *)ikm;393size_t seedlen = ikmlen;394unsigned char tmpbuf[OSSL_HPKE_MAX_PRIVATE];395const OSSL_HPKE_KEM_INFO *info = ctx->info;396397key = ossl_ecx_key_new(ctx->libctx, ctx->recipient_key->type, 0, ctx->propq);398if (key == NULL)399return NULL;400privkey = ossl_ecx_key_allocate_privkey(key);401if (privkey == NULL)402goto err;403404/* Generate a random seed if there is no input ikm */405if (seed == NULL || seedlen == 0) {406if (info->Nsk > sizeof(tmpbuf))407goto err;408if (RAND_priv_bytes_ex(ctx->libctx, tmpbuf, info->Nsk, 0) <= 0)409goto err;410seed = tmpbuf;411seedlen = info->Nsk;412}413if (!ossl_ecx_dhkem_derive_private(key, privkey, seed, seedlen))414goto err;415if (!ossl_ecx_public_from_private(key))416goto err;417key->haspubkey = 1;418ok = 1;419err:420if (!ok) {421ossl_ecx_key_free(key);422key = NULL;423}424if (seed != ikm)425OPENSSL_cleanse(seed, seedlen);426return key;427}428429/*430* Do an ecxdh key exchange.431* dhkm = DH(sender, peer)432*433* NOTE: Instead of using EVP_PKEY_derive() API's, we use ECX_KEY operations434* to avoid messy conversions back to EVP_PKEY.435*436* Returns the size of the secret if successful, or 0 otherwise,437*/438static int generate_ecxdhkm(const ECX_KEY *sender, const ECX_KEY *peer,439unsigned char *out, size_t maxout,440unsigned int secretsz)441{442size_t len = 0;443444/* NOTE: ossl_ecx_compute_key checks for shared secret being all zeros */445return ossl_ecx_compute_key((ECX_KEY *)peer, (ECX_KEY *)sender,446sender->keylen, out, &len, maxout);447}448449/*450* Derive a secret using ECXDH (code is shared by the encap and decap)451*452* dhkm = Concat(ecxdh(privkey1, peerkey1), ecdh(privkey2, peerkey2)453* kemctx = Concat(sender_pub, recipient_pub, ctx->sender_authkey)454* secret = dhkem_extract_and_expand(kemid, dhkm, kemctx);455*456* Params:457* ctx Object that contains algorithm state and constants.458* secret The returned secret (with a length ctx->alg->secretlen bytes).459* privkey1 A private key used for ECXDH key derivation.460* peerkey1 A public key used for ECXDH key derivation with privkey1461* privkey2 A optional private key used for a second ECXDH key derivation.462* It can be NULL.463* peerkey2 A optional public key used for a second ECXDH key derivation464* with privkey2,. It can be NULL.465* sender_pub The senders public key in encoded form.466* recipient_pub The recipients public key in encoded form.467* Notes:468* The second ecdh() is only used for the HPKE auth modes when both privkey2469* and peerkey2 are non NULL (i.e. ctx->sender_authkey is not NULL).470*/471static int derive_secret(PROV_ECX_CTX *ctx, unsigned char *secret,472const ECX_KEY *privkey1, const ECX_KEY *peerkey1,473const ECX_KEY *privkey2, const ECX_KEY *peerkey2,474const unsigned char *sender_pub,475const unsigned char *recipient_pub)476{477int ret = 0;478EVP_KDF_CTX *kdfctx = NULL;479unsigned char *sender_authpub = NULL;480unsigned char dhkm[MAX_ECX_KEYLEN * 2];481unsigned char kemctx[MAX_ECX_KEYLEN * 3];482size_t kemctxlen = 0, dhkmlen = 0;483const OSSL_HPKE_KEM_INFO *info = ctx->info;484int auth = ctx->sender_authkey != NULL;485size_t encodedkeylen = info->Npk;486487if (!generate_ecxdhkm(privkey1, peerkey1, dhkm, sizeof(dhkm), encodedkeylen))488goto err;489dhkmlen = encodedkeylen;490491/* Concat the optional second ECXDH (used for Auth) */492if (auth) {493if (!generate_ecxdhkm(privkey2, peerkey2,494dhkm + dhkmlen, sizeof(dhkm) - dhkmlen,495encodedkeylen))496goto err;497/* Get the public key of the auth sender in encoded form */498sender_authpub = ecx_pubkey(ctx->sender_authkey);499if (sender_authpub == NULL)500goto err;501dhkmlen += encodedkeylen;502}503kemctxlen = encodedkeylen + dhkmlen;504if (kemctxlen > sizeof(kemctx))505goto err;506507/* kemctx is the concat of both sides encoded public key */508memcpy(kemctx, sender_pub, encodedkeylen);509memcpy(kemctx + encodedkeylen, recipient_pub, encodedkeylen);510if (auth)511memcpy(kemctx + 2 * encodedkeylen, sender_authpub, encodedkeylen);512kdfctx = ossl_kdf_ctx_create(ctx->kdfname, info->mdname,513ctx->libctx, ctx->propq);514if (kdfctx == NULL)515goto err;516if (!dhkem_extract_and_expand(kdfctx, secret, info->Nsecret,517info->kem_id, dhkm, dhkmlen,518kemctx, kemctxlen))519goto err;520ret = 1;521err:522OPENSSL_cleanse(dhkm, dhkmlen);523EVP_KDF_CTX_free(kdfctx);524return ret;525}526527/*528* Do a DHKEM encapsulate operation.529*530* See Section 4.1 Encap() and AuthEncap()531*532* Params:533* ctx A context object holding the recipients public key and the534* optional senders auth private key.535* enc A buffer to return the senders ephemeral public key.536* Setting this to NULL allows the enclen and secretlen to return537* values, without calculating the secret.538* enclen Passes in the max size of the enc buffer and returns the539* encoded public key length.540* secret A buffer to return the calculated shared secret.541* secretlen Passes in the max size of the secret buffer and returns the542* secret length.543* Returns: 1 on success or 0 otherwise.544*/545static int dhkem_encap(PROV_ECX_CTX *ctx,546unsigned char *enc, size_t *enclen,547unsigned char *secret, size_t *secretlen)548{549int ret = 0;550ECX_KEY *sender_ephemkey = NULL;551unsigned char *sender_ephempub, *recipient_pub;552const OSSL_HPKE_KEM_INFO *info = ctx->info;553554if (enc == NULL) {555if (enclen == NULL && secretlen == NULL)556return 0;557if (enclen != NULL)558*enclen = info->Nenc;559if (secretlen != NULL)560*secretlen = info->Nsecret;561return 1;562}563564if (*secretlen < info->Nsecret) {565ERR_raise_data(ERR_LIB_PROV, PROV_R_BAD_LENGTH, "*secretlen too small");566return 0;567}568if (*enclen < info->Nenc) {569ERR_raise_data(ERR_LIB_PROV, PROV_R_BAD_LENGTH, "*enclen too small");570return 0;571}572573/* Create an ephemeral key */574sender_ephemkey = derivekey(ctx, ctx->ikm, ctx->ikmlen);575576sender_ephempub = ecx_pubkey(sender_ephemkey);577recipient_pub = ecx_pubkey(ctx->recipient_key);578if (sender_ephempub == NULL || recipient_pub == NULL)579goto err;580581if (!derive_secret(ctx, secret,582sender_ephemkey, ctx->recipient_key,583ctx->sender_authkey, ctx->recipient_key,584sender_ephempub, recipient_pub))585goto err;586587/* Return the public part of the ephemeral key */588memcpy(enc, sender_ephempub, info->Nenc);589*enclen = info->Nenc;590*secretlen = info->Nsecret;591ret = 1;592err:593ossl_ecx_key_free(sender_ephemkey);594return ret;595}596597/*598* Do a DHKEM decapsulate operation.599* See Section 4.1 Decap() and Auth Decap()600*601* Params:602* ctx A context object holding the recipients private key and the603* optional senders auth public key.604* secret A buffer to return the calculated shared secret. Setting this to605* NULL can be used to return the secretlen.606* secretlen Passes in the max size of the secret buffer and returns the607* secret length.608* enc A buffer containing the senders ephemeral public key that was returned609* from dhkem_encap().610* enclen The length in bytes of enc.611* Returns: 1 If the shared secret is returned or 0 on error.612*/613static int dhkem_decap(PROV_ECX_CTX *ctx,614unsigned char *secret, size_t *secretlen,615const unsigned char *enc, size_t enclen)616{617int ret = 0;618ECX_KEY *recipient_privkey = ctx->recipient_key;619ECX_KEY *sender_ephempubkey = NULL;620const OSSL_HPKE_KEM_INFO *info = ctx->info;621unsigned char *recipient_pub;622623if (secret == NULL) {624*secretlen = info->Nsecret;625return 1;626}627if (*secretlen < info->Nsecret) {628ERR_raise_data(ERR_LIB_PROV, PROV_R_BAD_LENGTH, "*secretlen too small");629return 0;630}631if (enclen != info->Nenc) {632ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_KEY, "Invalid enc public key");633return 0;634}635636/* Get the public part of the ephemeral key created by encap */637sender_ephempubkey = ecxkey_pubfromdata(ctx, enc, enclen);638if (sender_ephempubkey == NULL)639goto err;640641recipient_pub = ecx_pubkey(recipient_privkey);642if (recipient_pub == NULL)643goto err;644645if (!derive_secret(ctx, secret,646ctx->recipient_key, sender_ephempubkey,647ctx->recipient_key, ctx->sender_authkey,648enc, recipient_pub))649goto err;650651*secretlen = info->Nsecret;652ret = 1;653err:654ossl_ecx_key_free(sender_ephempubkey);655return ret;656}657658static int ecxkem_encapsulate(void *vctx, unsigned char *out, size_t *outlen,659unsigned char *secret, size_t *secretlen)660{661PROV_ECX_CTX *ctx = (PROV_ECX_CTX *)vctx;662663switch (ctx->mode) {664case KEM_MODE_DHKEM:665return dhkem_encap(ctx, out, outlen, secret, secretlen);666default:667ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_MODE);668return -2;669}670}671672static int ecxkem_decapsulate(void *vctx, unsigned char *out, size_t *outlen,673const unsigned char *in, size_t inlen)674{675PROV_ECX_CTX *ctx = (PROV_ECX_CTX *)vctx;676677switch (ctx->mode) {678case KEM_MODE_DHKEM:679return dhkem_decap(vctx, out, outlen, in, inlen);680default:681ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_MODE);682return -2;683}684}685686const OSSL_DISPATCH ossl_ecx_asym_kem_functions[] = {687{ OSSL_FUNC_KEM_NEWCTX, (void (*)(void))ecxkem_newctx },688{ OSSL_FUNC_KEM_ENCAPSULATE_INIT,689(void (*)(void))ecxkem_encapsulate_init },690{ OSSL_FUNC_KEM_ENCAPSULATE, (void (*)(void))ecxkem_encapsulate },691{ OSSL_FUNC_KEM_DECAPSULATE_INIT,692(void (*)(void))ecxkem_decapsulate_init },693{ OSSL_FUNC_KEM_DECAPSULATE, (void (*)(void))ecxkem_decapsulate },694{ OSSL_FUNC_KEM_FREECTX, (void (*)(void))ecxkem_freectx },695{ OSSL_FUNC_KEM_SET_CTX_PARAMS,696(void (*)(void))ecxkem_set_ctx_params },697{ OSSL_FUNC_KEM_SETTABLE_CTX_PARAMS,698(void (*)(void))ecxkem_settable_ctx_params },699{ OSSL_FUNC_KEM_AUTH_ENCAPSULATE_INIT,700(void (*)(void))ecxkem_auth_encapsulate_init },701{ OSSL_FUNC_KEM_AUTH_DECAPSULATE_INIT,702(void (*)(void))ecxkem_auth_decapsulate_init },703OSSL_DISPATCH_END704};705706707