Path: blob/main/crypto/openssl/providers/implementations/kem/ec_kem.c
109057 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* EC keys (i.e. P-256, P-384 and P-521)12* References to Sections in the comments below refer to RFC 9180.13*/1415#include "internal/deprecated.h"1617#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 <openssl/kdf.h>26#include <openssl/rand.h>27#include "prov/provider_ctx.h"28#include "prov/implementations.h"29#include "prov/securitycheck.h"30#include "prov/providercommon.h"3132#include <openssl/hpke.h>33#include "internal/hpke_util.h"34#include "crypto/ec.h"35#include "prov/ecx.h"36#include "eckem.h"3738typedef struct {39EC_KEY *recipient_key;40EC_KEY *sender_authkey;41OSSL_LIB_CTX *libctx;42char *propq;43unsigned int mode;44unsigned int op;45unsigned char *ikm;46size_t ikmlen;47const char *kdfname;48const OSSL_HPKE_KEM_INFO *info;49} PROV_EC_CTX;5051static OSSL_FUNC_kem_newctx_fn eckem_newctx;52static OSSL_FUNC_kem_encapsulate_init_fn eckem_encapsulate_init;53static OSSL_FUNC_kem_auth_encapsulate_init_fn eckem_auth_encapsulate_init;54static OSSL_FUNC_kem_encapsulate_fn eckem_encapsulate;55static OSSL_FUNC_kem_decapsulate_init_fn eckem_decapsulate_init;56static OSSL_FUNC_kem_auth_decapsulate_init_fn eckem_auth_decapsulate_init;57static OSSL_FUNC_kem_decapsulate_fn eckem_decapsulate;58static OSSL_FUNC_kem_freectx_fn eckem_freectx;59static OSSL_FUNC_kem_set_ctx_params_fn eckem_set_ctx_params;60static OSSL_FUNC_kem_settable_ctx_params_fn eckem_settable_ctx_params;6162/* ASCII: "KEM", in hex for EBCDIC compatibility */63static const char LABEL_KEM[] = "\x4b\x45\x4d";6465static int eckey_check(const EC_KEY *ec, int requires_privatekey)66{67int rv = 0;68BN_CTX *bnctx = NULL;69BIGNUM *rem = NULL;70const BIGNUM *priv = EC_KEY_get0_private_key(ec);71const EC_POINT *pub = EC_KEY_get0_public_key(ec);7273/* Keys always require a public component */74if (pub == NULL) {75ERR_raise(ERR_LIB_PROV, PROV_R_NOT_A_PUBLIC_KEY);76return 0;77}78if (priv == NULL) {79return (requires_privatekey == 0);80} else {81/* If there is a private key, check that is non zero (mod order) */82const EC_GROUP *group = EC_KEY_get0_group(ec);83const BIGNUM *order = EC_GROUP_get0_order(group);8485bnctx = BN_CTX_new_ex(ossl_ec_key_get_libctx(ec));86rem = BN_new();8788if (order != NULL && rem != NULL && bnctx != NULL) {89rv = BN_mod(rem, priv, order, bnctx)90&& !BN_is_zero(rem);91}92}93BN_free(rem);94BN_CTX_free(bnctx);95return rv;96}9798/* Returns NULL if the curve is not supported */99static const char *ec_curvename_get0(const EC_KEY *ec)100{101const EC_GROUP *group = EC_KEY_get0_group(ec);102103return EC_curve_nid2nist(EC_GROUP_get_curve_name(group));104}105106/*107* Set the recipient key, and free any existing key.108* ec can be NULL.109* The ec key may have only a private or public component110* (but it must have a group).111*/112static int recipient_key_set(PROV_EC_CTX *ctx, EC_KEY *ec)113{114EC_KEY_free(ctx->recipient_key);115ctx->recipient_key = NULL;116117if (ec != NULL) {118const char *curve = ec_curvename_get0(ec);119120if (curve == NULL)121return -2;122ctx->info = ossl_HPKE_KEM_INFO_find_curve(curve);123if (ctx->info == NULL)124return -2;125if (!EC_KEY_up_ref(ec))126return 0;127ctx->recipient_key = ec;128ctx->kdfname = "HKDF";129}130return 1;131}132133/*134* Set the senders auth key, and free any existing auth key.135* ec can be NULL.136*/137static int sender_authkey_set(PROV_EC_CTX *ctx, EC_KEY *ec)138{139EC_KEY_free(ctx->sender_authkey);140ctx->sender_authkey = NULL;141142if (ec != NULL) {143if (!EC_KEY_up_ref(ec))144return 0;145ctx->sender_authkey = ec;146}147return 1;148}149150/*151* Serializes a encoded public key buffer into a EC public key.152* Params:153* in Contains the group.154* pubbuf The encoded public key buffer155* Returns: The created public EC key, or NULL if there is an error.156*/157static EC_KEY *eckey_frompub(EC_KEY *in,158const unsigned char *pubbuf, size_t pubbuflen)159{160EC_KEY *key;161162key = EC_KEY_new_ex(ossl_ec_key_get_libctx(in), ossl_ec_key_get0_propq(in));163if (key == NULL)164goto err;165if (!EC_KEY_set_group(key, EC_KEY_get0_group(in)))166goto err;167if (!EC_KEY_oct2key(key, pubbuf, pubbuflen, NULL))168goto err;169return key;170err:171EC_KEY_free(key);172return NULL;173}174175/*176* Deserialises a EC public key into a encoded byte array.177* Returns: 1 if successful or 0 otherwise.178*/179static int ecpubkey_todata(const EC_KEY *ec, unsigned char *out, size_t *outlen,180size_t maxoutlen)181{182const EC_POINT *pub;183const EC_GROUP *group;184185group = EC_KEY_get0_group(ec);186pub = EC_KEY_get0_public_key(ec);187*outlen = EC_POINT_point2oct(group, pub, POINT_CONVERSION_UNCOMPRESSED,188out, maxoutlen, NULL);189return *outlen != 0;190}191192static void *eckem_newctx(void *provctx)193{194PROV_EC_CTX *ctx = OPENSSL_zalloc(sizeof(PROV_EC_CTX));195196if (ctx == NULL)197return NULL;198ctx->libctx = PROV_LIBCTX_OF(provctx);199ctx->mode = KEM_MODE_DHKEM;200201return ctx;202}203204static void eckem_freectx(void *vectx)205{206PROV_EC_CTX *ctx = (PROV_EC_CTX *)vectx;207208OPENSSL_clear_free(ctx->ikm, ctx->ikmlen);209recipient_key_set(ctx, NULL);210sender_authkey_set(ctx, NULL);211OPENSSL_free(ctx);212}213214static int ossl_ec_match_params(const EC_KEY *key1, const EC_KEY *key2)215{216int ret;217BN_CTX *ctx = NULL;218const EC_GROUP *group1 = EC_KEY_get0_group(key1);219const EC_GROUP *group2 = EC_KEY_get0_group(key2);220221ctx = BN_CTX_new_ex(ossl_ec_key_get_libctx(key1));222if (ctx == NULL)223return 0;224225ret = group1 != NULL226&& group2 != NULL227&& EC_GROUP_cmp(group1, group2, ctx) == 0;228if (!ret)229ERR_raise(ERR_LIB_PROV, PROV_R_MISMATCHING_DOMAIN_PARAMETERS);230BN_CTX_free(ctx);231return ret;232}233234static int eckem_init(void *vctx, int operation, void *vec, void *vauth,235const OSSL_PARAM params[])236{237int rv;238PROV_EC_CTX *ctx = (PROV_EC_CTX *)vctx;239EC_KEY *ec = vec;240EC_KEY *auth = vauth;241242if (!ossl_prov_is_running())243return 0;244245if (!eckey_check(ec, operation == EVP_PKEY_OP_DECAPSULATE))246return 0;247rv = recipient_key_set(ctx, ec);248if (rv <= 0)249return rv;250251if (auth != NULL) {252if (!ossl_ec_match_params(ec, auth)253|| !eckey_check(auth, operation == EVP_PKEY_OP_ENCAPSULATE)254|| !sender_authkey_set(ctx, auth))255return 0;256}257258ctx->op = operation;259return eckem_set_ctx_params(vctx, params);260}261262static int eckem_encapsulate_init(void *vctx, void *vec,263const OSSL_PARAM params[])264{265return eckem_init(vctx, EVP_PKEY_OP_ENCAPSULATE, vec, NULL, params);266}267268static int eckem_decapsulate_init(void *vctx, void *vec,269const OSSL_PARAM params[])270{271return eckem_init(vctx, EVP_PKEY_OP_DECAPSULATE, vec, NULL, params);272}273274static int eckem_auth_encapsulate_init(void *vctx, void *vecx, void *vauthpriv,275const OSSL_PARAM params[])276{277return eckem_init(vctx, EVP_PKEY_OP_ENCAPSULATE, vecx, vauthpriv, params);278}279280static int eckem_auth_decapsulate_init(void *vctx, void *vecx, void *vauthpub,281const OSSL_PARAM params[])282{283return eckem_init(vctx, EVP_PKEY_OP_DECAPSULATE, vecx, vauthpub, params);284}285286static int eckem_set_ctx_params(void *vctx, const OSSL_PARAM params[])287{288PROV_EC_CTX *ctx = (PROV_EC_CTX *)vctx;289const OSSL_PARAM *p;290int mode;291292if (ossl_param_is_empty(params))293return 1;294295p = OSSL_PARAM_locate_const(params, OSSL_KEM_PARAM_IKME);296if (p != NULL) {297void *tmp = NULL;298size_t tmplen = 0;299300if (p->data != NULL && p->data_size != 0) {301if (!OSSL_PARAM_get_octet_string(p, &tmp, 0, &tmplen))302return 0;303}304OPENSSL_clear_free(ctx->ikm, ctx->ikmlen);305/* Set the ephemeral seed */306ctx->ikm = tmp;307ctx->ikmlen = tmplen;308}309310p = OSSL_PARAM_locate_const(params, OSSL_KEM_PARAM_OPERATION);311if (p != NULL) {312if (p->data_type != OSSL_PARAM_UTF8_STRING)313return 0;314mode = ossl_eckem_modename2id(p->data);315if (mode == KEM_MODE_UNDEFINED)316return 0;317ctx->mode = mode;318}319return 1;320}321322static const OSSL_PARAM known_settable_eckem_ctx_params[] = {323OSSL_PARAM_utf8_string(OSSL_KEM_PARAM_OPERATION, NULL, 0),324OSSL_PARAM_octet_string(OSSL_KEM_PARAM_IKME, NULL, 0),325OSSL_PARAM_END326};327328static const OSSL_PARAM *eckem_settable_ctx_params(ossl_unused void *vctx,329ossl_unused void *provctx)330{331return known_settable_eckem_ctx_params;332}333334/*335* See Section 4.1 DH-Based KEM (DHKEM) ExtractAndExpand336*/337static int dhkem_extract_and_expand(EVP_KDF_CTX *kctx,338unsigned char *okm, size_t okmlen,339uint16_t kemid,340const unsigned char *dhkm, size_t dhkmlen,341const unsigned char *kemctx,342size_t kemctxlen)343{344uint8_t suiteid[2];345uint8_t prk[EVP_MAX_MD_SIZE];346size_t prklen = okmlen;347int ret;348349if (prklen > sizeof(prk))350return 0;351352suiteid[0] = (kemid >> 8) & 0xff;353suiteid[1] = kemid & 0xff;354355ret = ossl_hpke_labeled_extract(kctx, prk, prklen,356NULL, 0, LABEL_KEM, suiteid, sizeof(suiteid),357OSSL_DHKEM_LABEL_EAE_PRK, dhkm, dhkmlen)358&& ossl_hpke_labeled_expand(kctx, okm, okmlen, prk, prklen,359LABEL_KEM, suiteid, sizeof(suiteid),360OSSL_DHKEM_LABEL_SHARED_SECRET,361kemctx, kemctxlen);362OPENSSL_cleanse(prk, prklen);363return ret;364}365366/*367* See Section 7.1.3 DeriveKeyPair.368*369* This function is used by ec keygen.370* (For this reason it does not use any of the state stored in PROV_EC_CTX).371*372* Params:373* ec An initialized ec key.374* priv The buffer to store the generated private key into (it is assumed375* this is of length alg->encodedprivlen).376* ikm buffer containing the input key material (seed). This must be set.377* ikmlen size of the ikm buffer in bytes378* Returns:379* 1 if successful or 0 otherwise.380*/381int ossl_ec_dhkem_derive_private(EC_KEY *ec, BIGNUM *priv,382const unsigned char *ikm, size_t ikmlen)383{384int ret = 0;385EVP_KDF_CTX *kdfctx = NULL;386uint8_t suiteid[2];387unsigned char prk[OSSL_HPKE_MAX_SECRET];388unsigned char privbuf[OSSL_HPKE_MAX_PRIVATE];389const BIGNUM *order;390unsigned char counter = 0;391const char *curve = ec_curvename_get0(ec);392const OSSL_HPKE_KEM_INFO *info;393394if (curve == NULL)395return -2;396397info = ossl_HPKE_KEM_INFO_find_curve(curve);398if (info == NULL)399return -2;400401kdfctx = ossl_kdf_ctx_create("HKDF", info->mdname,402ossl_ec_key_get_libctx(ec),403ossl_ec_key_get0_propq(ec));404if (kdfctx == NULL)405return 0;406407/* ikmlen should have a length of at least Nsk */408if (ikmlen < info->Nsk) {409ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_INPUT_LENGTH,410"ikm length is :%zu, should be at least %zu",411ikmlen, info->Nsk);412goto err;413}414415suiteid[0] = info->kem_id / 256;416suiteid[1] = info->kem_id % 256;417418if (!ossl_hpke_labeled_extract(kdfctx, prk, info->Nsecret,419NULL, 0, LABEL_KEM, suiteid, sizeof(suiteid),420OSSL_DHKEM_LABEL_DKP_PRK, ikm, ikmlen))421goto err;422423order = EC_GROUP_get0_order(EC_KEY_get0_group(ec));424do {425if (!ossl_hpke_labeled_expand(kdfctx, privbuf, info->Nsk,426prk, info->Nsecret,427LABEL_KEM, suiteid, sizeof(suiteid),428OSSL_DHKEM_LABEL_CANDIDATE,429&counter, 1))430goto err;431privbuf[0] &= info->bitmask;432if (BN_bin2bn(privbuf, info->Nsk, priv) == NULL)433goto err;434if (counter == 0xFF) {435ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GENERATE_KEY);436goto err;437}438counter++;439} while (BN_is_zero(priv) || BN_cmp(priv, order) >= 0);440ret = 1;441err:442OPENSSL_cleanse(prk, sizeof(prk));443OPENSSL_cleanse(privbuf, sizeof(privbuf));444EVP_KDF_CTX_free(kdfctx);445return ret;446}447448/*449* Do a keygen operation without having to use EVP_PKEY.450* Params:451* ctx Context object452* ikm The seed material - if this is NULL, then a random seed is used.453* Returns:454* The generated EC key, or NULL on failure.455*/456static EC_KEY *derivekey(PROV_EC_CTX *ctx,457const unsigned char *ikm, size_t ikmlen)458{459int ret = 0;460EC_KEY *key;461unsigned char *seed = (unsigned char *)ikm;462size_t seedlen = ikmlen;463unsigned char tmpbuf[OSSL_HPKE_MAX_PRIVATE];464465key = EC_KEY_new_ex(ctx->libctx, ctx->propq);466if (key == NULL)467goto err;468if (!EC_KEY_set_group(key, EC_KEY_get0_group(ctx->recipient_key)))469goto err;470471/* Generate a random seed if there is no input ikm */472if (seed == NULL || seedlen == 0) {473seedlen = ctx->info->Nsk;474if (seedlen > sizeof(tmpbuf))475goto err;476if (RAND_priv_bytes_ex(ctx->libctx, tmpbuf, seedlen, 0) <= 0)477goto err;478seed = tmpbuf;479}480ret = ossl_ec_generate_key_dhkem(key, seed, seedlen);481err:482if (seed != ikm)483OPENSSL_cleanse(seed, seedlen);484if (ret <= 0) {485EC_KEY_free(key);486key = NULL;487}488return key;489}490491/*492* Before doing a key exchange the public key of the peer needs to be checked493* Note that the group check is not done here as we have already checked494* that it only uses one of the approved curve names when the key was set.495*496* Returns 1 if the public key is valid, or 0 if it fails.497*/498static int check_publickey(const EC_KEY *pub)499{500int ret = 0;501BN_CTX *bnctx = BN_CTX_new_ex(ossl_ec_key_get_libctx(pub));502503if (bnctx == NULL)504return 0;505ret = ossl_ec_key_public_check(pub, bnctx);506BN_CTX_free(bnctx);507508return ret;509}510511/*512* Do an ecdh key exchange.513* dhkm = DH(sender, peer)514*515* NOTE: Instead of using EVP_PKEY_derive() API's, we use EC_KEY operations516* to avoid messy conversions back to EVP_PKEY.517*518* Returns the size of the secret if successful, or 0 otherwise,519*/520static int generate_ecdhkm(const EC_KEY *sender, const EC_KEY *peer,521unsigned char *out, size_t maxout,522unsigned int secretsz)523{524const EC_GROUP *group = EC_KEY_get0_group(sender);525size_t secretlen = (EC_GROUP_get_degree(group) + 7) / 8;526527if (secretlen != secretsz || secretlen > maxout) {528ERR_raise_data(ERR_LIB_PROV, PROV_R_BAD_LENGTH, "secretsz invalid");529return 0;530}531532if (!check_publickey(peer))533return 0;534return ECDH_compute_key(out, secretlen, EC_KEY_get0_public_key(peer),535sender, NULL)536> 0;537}538539/*540* Derive a secret using ECDH (code is shared by the encap and decap)541*542* dhkm = Concat(ecdh(privkey1, peerkey1), ecdh(privkey2, peerkey2)543* kemctx = Concat(sender_pub, recipient_pub, ctx->sender_authkey)544* secret = dhkem_extract_and_expand(kemid, dhkm, kemctx);545*546* Params:547* ctx Object that contains algorithm state and constants.548* secret The returned secret (with a length ctx->alg->secretlen bytes).549* privkey1 A private key used for ECDH key derivation.550* peerkey1 A public key used for ECDH key derivation with privkey1551* privkey2 A optional private key used for a second ECDH key derivation.552* It can be NULL.553* peerkey2 A optional public key used for a second ECDH key derivation554* with privkey2,. It can be NULL.555* sender_pub The senders public key in encoded form.556* recipient_pub The recipients public key in encoded form.557* Notes:558* The second ecdh() is only used for the HPKE auth modes when both privkey2559* and peerkey2 are non NULL (i.e. ctx->sender_authkey is not NULL).560*/561static int derive_secret(PROV_EC_CTX *ctx, unsigned char *secret,562const EC_KEY *privkey1, const EC_KEY *peerkey1,563const EC_KEY *privkey2, const EC_KEY *peerkey2,564const unsigned char *sender_pub,565const unsigned char *recipient_pub)566{567int ret = 0;568EVP_KDF_CTX *kdfctx = NULL;569unsigned char sender_authpub[OSSL_HPKE_MAX_PUBLIC];570unsigned char dhkm[OSSL_HPKE_MAX_PRIVATE * 2];571unsigned char kemctx[OSSL_HPKE_MAX_PUBLIC * 3];572size_t sender_authpublen;573size_t kemctxlen = 0, dhkmlen = 0;574const OSSL_HPKE_KEM_INFO *info = ctx->info;575size_t encodedpublen = info->Npk;576size_t encodedprivlen = info->Nsk;577int auth = ctx->sender_authkey != NULL;578579if (!generate_ecdhkm(privkey1, peerkey1, dhkm, sizeof(dhkm), encodedprivlen))580goto err;581dhkmlen = encodedprivlen;582kemctxlen = 2 * encodedpublen;583584/* Concat the optional second ECDH (used for Auth) */585if (auth) {586/* Get the public key of the auth sender in encoded form */587if (!ecpubkey_todata(ctx->sender_authkey, sender_authpub,588&sender_authpublen, sizeof(sender_authpub)))589goto err;590if (sender_authpublen != encodedpublen) {591ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_KEY,592"Invalid sender auth public key");593goto err;594}595if (!generate_ecdhkm(privkey2, peerkey2,596dhkm + dhkmlen, sizeof(dhkm) - dhkmlen,597encodedprivlen))598goto err;599dhkmlen += encodedprivlen;600kemctxlen += encodedpublen;601}602if (kemctxlen > sizeof(kemctx))603goto err;604605/* kemctx is the concat of both sides encoded public key */606memcpy(kemctx, sender_pub, info->Npk);607memcpy(kemctx + info->Npk, recipient_pub, info->Npk);608if (auth)609memcpy(kemctx + 2 * encodedpublen, sender_authpub, encodedpublen);610kdfctx = ossl_kdf_ctx_create(ctx->kdfname, info->mdname,611ctx->libctx, ctx->propq);612if (kdfctx == NULL)613goto err;614if (!dhkem_extract_and_expand(kdfctx, secret, info->Nsecret,615info->kem_id, dhkm, dhkmlen,616kemctx, kemctxlen))617goto err;618ret = 1;619err:620OPENSSL_cleanse(dhkm, dhkmlen);621EVP_KDF_CTX_free(kdfctx);622return ret;623}624625/*626* Do a DHKEM encapsulate operation.627*628* See Section 4.1 Encap() and AuthEncap()629*630* Params:631* ctx A context object holding the recipients public key and the632* optional senders auth private key.633* enc A buffer to return the senders ephemeral public key.634* Setting this to NULL allows the enclen and secretlen to return635* values, without calculating the secret.636* enclen Passes in the max size of the enc buffer and returns the637* encoded public key length.638* secret A buffer to return the calculated shared secret.639* secretlen Passes in the max size of the secret buffer and returns the640* secret length.641* Returns: 1 on success or 0 otherwise.642*/643static int dhkem_encap(PROV_EC_CTX *ctx,644unsigned char *enc, size_t *enclen,645unsigned char *secret, size_t *secretlen)646{647int ret = 0;648EC_KEY *sender_ephemkey = NULL;649unsigned char sender_pub[OSSL_HPKE_MAX_PUBLIC];650unsigned char recipient_pub[OSSL_HPKE_MAX_PUBLIC];651size_t sender_publen, recipient_publen;652const OSSL_HPKE_KEM_INFO *info = ctx->info;653654if (enc == NULL) {655if (enclen == NULL && secretlen == NULL)656return 0;657if (enclen != NULL)658*enclen = info->Nenc;659if (secretlen != NULL)660*secretlen = info->Nsecret;661return 1;662}663664if (*secretlen < info->Nsecret) {665ERR_raise_data(ERR_LIB_PROV, PROV_R_BAD_LENGTH, "*secretlen too small");666return 0;667}668if (*enclen < info->Nenc) {669ERR_raise_data(ERR_LIB_PROV, PROV_R_BAD_LENGTH, "*enclen too small");670return 0;671}672673/* Create an ephemeral key */674sender_ephemkey = derivekey(ctx, ctx->ikm, ctx->ikmlen);675if (sender_ephemkey == NULL)676goto err;677if (!ecpubkey_todata(sender_ephemkey, sender_pub, &sender_publen,678sizeof(sender_pub))679|| !ecpubkey_todata(ctx->recipient_key, recipient_pub,680&recipient_publen, sizeof(recipient_pub)))681goto err;682683if (sender_publen != info->Npk684|| recipient_publen != sender_publen) {685ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_KEY, "Invalid public key");686goto err;687}688689if (!derive_secret(ctx, secret,690sender_ephemkey, ctx->recipient_key,691ctx->sender_authkey, ctx->recipient_key,692sender_pub, recipient_pub))693goto err;694695/* Return the senders ephemeral public key in encoded form */696memcpy(enc, sender_pub, sender_publen);697*enclen = sender_publen;698*secretlen = info->Nsecret;699ret = 1;700err:701EC_KEY_free(sender_ephemkey);702return ret;703}704705/*706* Do a DHKEM decapsulate operation.707* See Section 4.1 Decap() and Auth Decap()708*709* Params:710* ctx A context object holding the recipients private key and the711* optional senders auth public key.712* secret A buffer to return the calculated shared secret. Setting this to713* NULL can be used to return the secretlen.714* secretlen Passes in the max size of the secret buffer and returns the715* secret length.716* enc A buffer containing the senders ephemeral public key that was returned717* from dhkem_encap().718* enclen The length in bytes of enc.719* Returns: 1 If the shared secret is returned or 0 on error.720*/721static int dhkem_decap(PROV_EC_CTX *ctx,722unsigned char *secret, size_t *secretlen,723const unsigned char *enc, size_t enclen)724{725int ret = 0;726EC_KEY *sender_ephempubkey = NULL;727const OSSL_HPKE_KEM_INFO *info = ctx->info;728unsigned char recipient_pub[OSSL_HPKE_MAX_PUBLIC];729size_t recipient_publen;730size_t encodedpublen = info->Npk;731732if (secret == NULL) {733*secretlen = info->Nsecret;734return 1;735}736737if (*secretlen < info->Nsecret) {738ERR_raise_data(ERR_LIB_PROV, PROV_R_BAD_LENGTH, "*secretlen too small");739return 0;740}741if (enclen != encodedpublen) {742ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_KEY, "Invalid enc public key");743return 0;744}745746sender_ephempubkey = eckey_frompub(ctx->recipient_key, enc, enclen);747if (sender_ephempubkey == NULL)748goto err;749if (!ecpubkey_todata(ctx->recipient_key, recipient_pub, &recipient_publen,750sizeof(recipient_pub)))751goto err;752if (recipient_publen != encodedpublen) {753ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_KEY, "Invalid recipient public key");754goto err;755}756757if (!derive_secret(ctx, secret,758ctx->recipient_key, sender_ephempubkey,759ctx->recipient_key, ctx->sender_authkey,760enc, recipient_pub))761goto err;762*secretlen = info->Nsecret;763ret = 1;764err:765EC_KEY_free(sender_ephempubkey);766return ret;767}768769static int eckem_encapsulate(void *vctx, unsigned char *out, size_t *outlen,770unsigned char *secret, size_t *secretlen)771{772PROV_EC_CTX *ctx = (PROV_EC_CTX *)vctx;773774switch (ctx->mode) {775case KEM_MODE_DHKEM:776return dhkem_encap(ctx, out, outlen, secret, secretlen);777default:778ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_MODE);779return -2;780}781}782783static int eckem_decapsulate(void *vctx, unsigned char *out, size_t *outlen,784const unsigned char *in, size_t inlen)785{786PROV_EC_CTX *ctx = (PROV_EC_CTX *)vctx;787788switch (ctx->mode) {789case KEM_MODE_DHKEM:790return dhkem_decap(ctx, out, outlen, in, inlen);791default:792ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_MODE);793return -2;794}795}796797const OSSL_DISPATCH ossl_ec_asym_kem_functions[] = {798{ OSSL_FUNC_KEM_NEWCTX, (void (*)(void))eckem_newctx },799{ OSSL_FUNC_KEM_ENCAPSULATE_INIT,800(void (*)(void))eckem_encapsulate_init },801{ OSSL_FUNC_KEM_ENCAPSULATE, (void (*)(void))eckem_encapsulate },802{ OSSL_FUNC_KEM_DECAPSULATE_INIT,803(void (*)(void))eckem_decapsulate_init },804{ OSSL_FUNC_KEM_DECAPSULATE, (void (*)(void))eckem_decapsulate },805{ OSSL_FUNC_KEM_FREECTX, (void (*)(void))eckem_freectx },806{ OSSL_FUNC_KEM_SET_CTX_PARAMS,807(void (*)(void))eckem_set_ctx_params },808{ OSSL_FUNC_KEM_SETTABLE_CTX_PARAMS,809(void (*)(void))eckem_settable_ctx_params },810{ OSSL_FUNC_KEM_AUTH_ENCAPSULATE_INIT,811(void (*)(void))eckem_auth_encapsulate_init },812{ OSSL_FUNC_KEM_AUTH_DECAPSULATE_INIT,813(void (*)(void))eckem_auth_decapsulate_init },814OSSL_DISPATCH_END815};816817818