xref: /freebsd/crypto/openssl/providers/implementations/kem/ec_kem.c (revision e7be843b4a162e68651d3911f0357ed464915629)
1 /*
2  * Copyright 2022-2025 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  * The following implementation is part of RFC 9180 related to DHKEM using
12  * EC keys (i.e. P-256, P-384 and P-521)
13  * References to Sections in the comments below refer to RFC 9180.
14  */
15 
16 #include "internal/deprecated.h"
17 
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/ec.h>
23 #include <openssl/params.h>
24 #include <openssl/err.h>
25 #include <openssl/proverr.h>
26 #include <openssl/kdf.h>
27 #include <openssl/rand.h>
28 #include "prov/provider_ctx.h"
29 #include "prov/implementations.h"
30 #include "prov/securitycheck.h"
31 #include "prov/providercommon.h"
32 
33 #include <openssl/hpke.h>
34 #include "internal/hpke_util.h"
35 #include "crypto/ec.h"
36 #include "prov/ecx.h"
37 #include "eckem.h"
38 
39 typedef struct {
40     EC_KEY *recipient_key;
41     EC_KEY *sender_authkey;
42     OSSL_LIB_CTX *libctx;
43     char *propq;
44     unsigned int mode;
45     unsigned int op;
46     unsigned char *ikm;
47     size_t ikmlen;
48     const char *kdfname;
49     const OSSL_HPKE_KEM_INFO *info;
50 } PROV_EC_CTX;
51 
52 static OSSL_FUNC_kem_newctx_fn eckem_newctx;
53 static OSSL_FUNC_kem_encapsulate_init_fn eckem_encapsulate_init;
54 static OSSL_FUNC_kem_auth_encapsulate_init_fn eckem_auth_encapsulate_init;
55 static OSSL_FUNC_kem_encapsulate_fn eckem_encapsulate;
56 static OSSL_FUNC_kem_decapsulate_init_fn eckem_decapsulate_init;
57 static OSSL_FUNC_kem_auth_decapsulate_init_fn eckem_auth_decapsulate_init;
58 static OSSL_FUNC_kem_decapsulate_fn eckem_decapsulate;
59 static OSSL_FUNC_kem_freectx_fn eckem_freectx;
60 static OSSL_FUNC_kem_set_ctx_params_fn eckem_set_ctx_params;
61 static OSSL_FUNC_kem_settable_ctx_params_fn eckem_settable_ctx_params;
62 
63 /* ASCII: "KEM", in hex for EBCDIC compatibility */
64 static const char LABEL_KEM[] = "\x4b\x45\x4d";
65 
eckey_check(const EC_KEY * ec,int requires_privatekey)66 static int eckey_check(const EC_KEY *ec, int requires_privatekey)
67 {
68     int rv = 0;
69     BN_CTX *bnctx = NULL;
70     BIGNUM *rem = NULL;
71     const BIGNUM *priv = EC_KEY_get0_private_key(ec);
72     const EC_POINT *pub = EC_KEY_get0_public_key(ec);
73 
74     /* Keys always require a public component */
75     if (pub == NULL) {
76         ERR_raise(ERR_LIB_PROV, PROV_R_NOT_A_PUBLIC_KEY);
77         return 0;
78     }
79     if (priv == NULL) {
80         return (requires_privatekey == 0);
81     } else {
82         /* If there is a private key, check that is non zero (mod order) */
83         const EC_GROUP *group = EC_KEY_get0_group(ec);
84         const BIGNUM *order = EC_GROUP_get0_order(group);
85 
86         bnctx = BN_CTX_new_ex(ossl_ec_key_get_libctx(ec));
87         rem = BN_new();
88 
89         if (order != NULL && rem != NULL && bnctx != NULL) {
90              rv = BN_mod(rem, priv, order, bnctx)
91                   && !BN_is_zero(rem);
92         }
93     }
94     BN_free(rem);
95     BN_CTX_free(bnctx);
96     return rv;
97 }
98 
99 /* Returns NULL if the curve is not supported */
ec_curvename_get0(const EC_KEY * ec)100 static const char *ec_curvename_get0(const EC_KEY *ec)
101 {
102     const EC_GROUP *group = EC_KEY_get0_group(ec);
103 
104     return EC_curve_nid2nist(EC_GROUP_get_curve_name(group));
105 }
106 
107 /*
108  * Set the recipient key, and free any existing key.
109  * ec can be NULL.
110  * The ec key may have only a private or public component
111  * (but it must have a group).
112  */
recipient_key_set(PROV_EC_CTX * ctx,EC_KEY * ec)113 static int recipient_key_set(PROV_EC_CTX *ctx, EC_KEY *ec)
114 {
115     EC_KEY_free(ctx->recipient_key);
116     ctx->recipient_key = NULL;
117 
118     if (ec != NULL) {
119         const char *curve = ec_curvename_get0(ec);
120 
121         if (curve == NULL)
122             return -2;
123         ctx->info = ossl_HPKE_KEM_INFO_find_curve(curve);
124         if (ctx->info == NULL)
125             return -2;
126         if (!EC_KEY_up_ref(ec))
127             return 0;
128         ctx->recipient_key = ec;
129         ctx->kdfname = "HKDF";
130     }
131     return 1;
132 }
133 
134 /*
135  * Set the senders auth key, and free any existing auth key.
136  * ec can be NULL.
137  */
sender_authkey_set(PROV_EC_CTX * ctx,EC_KEY * ec)138 static int sender_authkey_set(PROV_EC_CTX *ctx, EC_KEY *ec)
139 {
140     EC_KEY_free(ctx->sender_authkey);
141     ctx->sender_authkey = NULL;
142 
143     if (ec != NULL) {
144         if (!EC_KEY_up_ref(ec))
145             return 0;
146         ctx->sender_authkey = ec;
147     }
148     return 1;
149 }
150 
151 /*
152  * Serializes a encoded public key buffer into a EC public key.
153  * Params:
154  *     in Contains the group.
155  *     pubbuf The encoded public key buffer
156  * Returns: The created public EC key, or NULL if there is an error.
157  */
eckey_frompub(EC_KEY * in,const unsigned char * pubbuf,size_t pubbuflen)158 static EC_KEY *eckey_frompub(EC_KEY *in,
159                              const unsigned char *pubbuf, size_t pubbuflen)
160 {
161     EC_KEY *key;
162 
163     key = EC_KEY_new_ex(ossl_ec_key_get_libctx(in), ossl_ec_key_get0_propq(in));
164     if (key == NULL)
165         goto err;
166     if (!EC_KEY_set_group(key, EC_KEY_get0_group(in)))
167         goto err;
168     if (!EC_KEY_oct2key(key, pubbuf, pubbuflen, NULL))
169         goto err;
170     return key;
171 err:
172     EC_KEY_free(key);
173     return NULL;
174 }
175 
176 /*
177  * Deserialises a EC public key into a encoded byte array.
178  * Returns: 1 if successful or 0 otherwise.
179  */
ecpubkey_todata(const EC_KEY * ec,unsigned char * out,size_t * outlen,size_t maxoutlen)180 static int ecpubkey_todata(const EC_KEY *ec, unsigned char *out, size_t *outlen,
181                            size_t maxoutlen)
182 {
183     const EC_POINT *pub;
184     const EC_GROUP *group;
185 
186     group = EC_KEY_get0_group(ec);
187     pub = EC_KEY_get0_public_key(ec);
188     *outlen = EC_POINT_point2oct(group, pub, POINT_CONVERSION_UNCOMPRESSED,
189                                  out, maxoutlen, NULL);
190     return *outlen != 0;
191 }
192 
eckem_newctx(void * provctx)193 static void *eckem_newctx(void *provctx)
194 {
195     PROV_EC_CTX *ctx =  OPENSSL_zalloc(sizeof(PROV_EC_CTX));
196 
197     if (ctx == NULL)
198         return NULL;
199     ctx->libctx = PROV_LIBCTX_OF(provctx);
200     ctx->mode = KEM_MODE_DHKEM;
201 
202     return ctx;
203 }
204 
eckem_freectx(void * vectx)205 static void eckem_freectx(void *vectx)
206 {
207     PROV_EC_CTX *ctx = (PROV_EC_CTX *)vectx;
208 
209     OPENSSL_clear_free(ctx->ikm, ctx->ikmlen);
210     recipient_key_set(ctx, NULL);
211     sender_authkey_set(ctx, NULL);
212     OPENSSL_free(ctx);
213 }
214 
ossl_ec_match_params(const EC_KEY * key1,const EC_KEY * key2)215 static int ossl_ec_match_params(const EC_KEY *key1, const EC_KEY *key2)
216 {
217     int ret;
218     BN_CTX *ctx = NULL;
219     const EC_GROUP *group1 = EC_KEY_get0_group(key1);
220     const EC_GROUP *group2 = EC_KEY_get0_group(key2);
221 
222     ctx = BN_CTX_new_ex(ossl_ec_key_get_libctx(key1));
223     if (ctx == NULL)
224         return 0;
225 
226     ret = group1 != NULL
227           && group2 != NULL
228           && EC_GROUP_cmp(group1, group2, ctx) == 0;
229     if (!ret)
230         ERR_raise(ERR_LIB_PROV, PROV_R_MISMATCHING_DOMAIN_PARAMETERS);
231     BN_CTX_free(ctx);
232     return ret;
233 }
234 
eckem_init(void * vctx,int operation,void * vec,void * vauth,const OSSL_PARAM params[])235 static int eckem_init(void *vctx, int operation, void *vec, void *vauth,
236                       const OSSL_PARAM params[])
237 {
238     int rv;
239     PROV_EC_CTX *ctx = (PROV_EC_CTX *)vctx;
240     EC_KEY *ec = vec;
241     EC_KEY *auth = vauth;
242 
243     if (!ossl_prov_is_running())
244         return 0;
245 
246     if (!eckey_check(ec, operation == EVP_PKEY_OP_DECAPSULATE))
247         return 0;
248     rv = recipient_key_set(ctx, ec);
249     if (rv <= 0)
250         return rv;
251 
252     if (auth != NULL) {
253         if (!ossl_ec_match_params(ec, auth)
254             || !eckey_check(auth, operation == EVP_PKEY_OP_ENCAPSULATE)
255             || !sender_authkey_set(ctx, auth))
256         return 0;
257     }
258 
259     ctx->op = operation;
260     return eckem_set_ctx_params(vctx, params);
261 }
262 
eckem_encapsulate_init(void * vctx,void * vec,const OSSL_PARAM params[])263 static int eckem_encapsulate_init(void *vctx, void *vec,
264                                    const OSSL_PARAM params[])
265 {
266     return eckem_init(vctx, EVP_PKEY_OP_ENCAPSULATE, vec, NULL, params);
267 }
268 
eckem_decapsulate_init(void * vctx,void * vec,const OSSL_PARAM params[])269 static int eckem_decapsulate_init(void *vctx, void *vec,
270                                    const OSSL_PARAM params[])
271 {
272     return eckem_init(vctx, EVP_PKEY_OP_DECAPSULATE, vec, NULL, params);
273 }
274 
eckem_auth_encapsulate_init(void * vctx,void * vecx,void * vauthpriv,const OSSL_PARAM params[])275 static int eckem_auth_encapsulate_init(void *vctx, void *vecx, void *vauthpriv,
276                                        const OSSL_PARAM params[])
277 {
278     return eckem_init(vctx, EVP_PKEY_OP_ENCAPSULATE, vecx, vauthpriv, params);
279 }
280 
eckem_auth_decapsulate_init(void * vctx,void * vecx,void * vauthpub,const OSSL_PARAM params[])281 static int eckem_auth_decapsulate_init(void *vctx, void *vecx, void *vauthpub,
282                                        const OSSL_PARAM params[])
283 {
284     return eckem_init(vctx, EVP_PKEY_OP_DECAPSULATE, vecx, vauthpub, params);
285 }
286 
eckem_set_ctx_params(void * vctx,const OSSL_PARAM params[])287 static int eckem_set_ctx_params(void *vctx, const OSSL_PARAM params[])
288 {
289     PROV_EC_CTX *ctx = (PROV_EC_CTX *)vctx;
290     const OSSL_PARAM *p;
291     int mode;
292 
293     if (ossl_param_is_empty(params))
294         return 1;
295 
296     p = OSSL_PARAM_locate_const(params, OSSL_KEM_PARAM_IKME);
297     if (p != NULL) {
298         void *tmp = NULL;
299         size_t tmplen = 0;
300 
301         if (p->data != NULL && p->data_size != 0) {
302             if (!OSSL_PARAM_get_octet_string(p, &tmp, 0, &tmplen))
303                 return 0;
304         }
305         OPENSSL_clear_free(ctx->ikm, ctx->ikmlen);
306         /* Set the ephemeral seed */
307         ctx->ikm = tmp;
308         ctx->ikmlen = tmplen;
309     }
310 
311     p = OSSL_PARAM_locate_const(params, OSSL_KEM_PARAM_OPERATION);
312     if (p != NULL) {
313         if (p->data_type != OSSL_PARAM_UTF8_STRING)
314             return 0;
315         mode = ossl_eckem_modename2id(p->data);
316         if (mode == KEM_MODE_UNDEFINED)
317             return 0;
318         ctx->mode = mode;
319     }
320     return 1;
321 }
322 
323 static const OSSL_PARAM known_settable_eckem_ctx_params[] = {
324     OSSL_PARAM_utf8_string(OSSL_KEM_PARAM_OPERATION, NULL, 0),
325     OSSL_PARAM_octet_string(OSSL_KEM_PARAM_IKME, NULL, 0),
326     OSSL_PARAM_END
327 };
328 
eckem_settable_ctx_params(ossl_unused void * vctx,ossl_unused void * provctx)329 static const OSSL_PARAM *eckem_settable_ctx_params(ossl_unused void *vctx,
330                                                    ossl_unused void *provctx)
331 {
332     return known_settable_eckem_ctx_params;
333 }
334 
335 /*
336  * See Section 4.1 DH-Based KEM (DHKEM) ExtractAndExpand
337  */
dhkem_extract_and_expand(EVP_KDF_CTX * kctx,unsigned char * okm,size_t okmlen,uint16_t kemid,const unsigned char * dhkm,size_t dhkmlen,const unsigned char * kemctx,size_t kemctxlen)338 static int dhkem_extract_and_expand(EVP_KDF_CTX *kctx,
339                                     unsigned char *okm, size_t okmlen,
340                                     uint16_t kemid,
341                                     const unsigned char *dhkm, size_t dhkmlen,
342                                     const unsigned char *kemctx,
343                                     size_t kemctxlen)
344 {
345     uint8_t suiteid[2];
346     uint8_t prk[EVP_MAX_MD_SIZE];
347     size_t prklen = okmlen;
348     int ret;
349 
350     if (prklen > sizeof(prk))
351         return 0;
352 
353     suiteid[0] = (kemid >> 8) & 0xff;
354     suiteid[1] = kemid & 0xff;
355 
356     ret = ossl_hpke_labeled_extract(kctx, prk, prklen,
357                                     NULL, 0, LABEL_KEM, suiteid, sizeof(suiteid),
358                                     OSSL_DHKEM_LABEL_EAE_PRK, dhkm, dhkmlen)
359           && ossl_hpke_labeled_expand(kctx, okm, okmlen, prk, prklen,
360                                       LABEL_KEM, suiteid, sizeof(suiteid),
361                                       OSSL_DHKEM_LABEL_SHARED_SECRET,
362                                       kemctx, kemctxlen);
363     OPENSSL_cleanse(prk, prklen);
364     return ret;
365 }
366 
367 /*
368  * See Section 7.1.3 DeriveKeyPair.
369  *
370  * This function is used by ec keygen.
371  * (For this reason it does not use any of the state stored in PROV_EC_CTX).
372  *
373  * Params:
374  *     ec An initialized ec key.
375  *     priv The buffer to store the generated private key into (it is assumed
376  *          this is of length alg->encodedprivlen).
377  *     ikm buffer containing the input key material (seed). This must be set.
378  *     ikmlen size of the ikm buffer in bytes
379  * Returns:
380  *     1 if successful or 0 otherwise.
381  */
ossl_ec_dhkem_derive_private(EC_KEY * ec,BIGNUM * priv,const unsigned char * ikm,size_t ikmlen)382 int ossl_ec_dhkem_derive_private(EC_KEY *ec, BIGNUM *priv,
383                                  const unsigned char *ikm, size_t ikmlen)
384 {
385     int ret = 0;
386     EVP_KDF_CTX *kdfctx = NULL;
387     uint8_t suiteid[2];
388     unsigned char prk[OSSL_HPKE_MAX_SECRET];
389     unsigned char privbuf[OSSL_HPKE_MAX_PRIVATE];
390     const BIGNUM *order;
391     unsigned char counter = 0;
392     const char *curve = ec_curvename_get0(ec);
393     const OSSL_HPKE_KEM_INFO *info;
394 
395     if (curve == NULL)
396         return -2;
397 
398     info = ossl_HPKE_KEM_INFO_find_curve(curve);
399     if (info == NULL)
400         return -2;
401 
402     kdfctx = ossl_kdf_ctx_create("HKDF", info->mdname,
403                                  ossl_ec_key_get_libctx(ec),
404                                  ossl_ec_key_get0_propq(ec));
405     if (kdfctx == NULL)
406         return 0;
407 
408     /* ikmlen should have a length of at least Nsk */
409     if (ikmlen < info->Nsk) {
410         ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_INPUT_LENGTH,
411                        "ikm length is :%zu, should be at least %zu",
412                        ikmlen, info->Nsk);
413         goto err;
414     }
415 
416     suiteid[0] = info->kem_id / 256;
417     suiteid[1] = info->kem_id % 256;
418 
419     if (!ossl_hpke_labeled_extract(kdfctx, prk, info->Nsecret,
420                                    NULL, 0, LABEL_KEM, suiteid, sizeof(suiteid),
421                                    OSSL_DHKEM_LABEL_DKP_PRK, ikm, ikmlen))
422         goto err;
423 
424     order = EC_GROUP_get0_order(EC_KEY_get0_group(ec));
425     do {
426         if (!ossl_hpke_labeled_expand(kdfctx, privbuf, info->Nsk,
427                                       prk, info->Nsecret,
428                                       LABEL_KEM, suiteid, sizeof(suiteid),
429                                       OSSL_DHKEM_LABEL_CANDIDATE,
430                                       &counter, 1))
431             goto err;
432         privbuf[0] &= info->bitmask;
433         if (BN_bin2bn(privbuf, info->Nsk, priv) == NULL)
434             goto err;
435         if (counter == 0xFF) {
436             ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GENERATE_KEY);
437             goto err;
438         }
439         counter++;
440     } while (BN_is_zero(priv) || BN_cmp(priv, order) >= 0);
441     ret = 1;
442 err:
443     OPENSSL_cleanse(prk, sizeof(prk));
444     OPENSSL_cleanse(privbuf, sizeof(privbuf));
445     EVP_KDF_CTX_free(kdfctx);
446     return ret;
447 }
448 
449 /*
450  * Do a keygen operation without having to use EVP_PKEY.
451  * Params:
452  *     ctx Context object
453  *     ikm The seed material - if this is NULL, then a random seed is used.
454  * Returns:
455  *     The generated EC key, or NULL on failure.
456  */
derivekey(PROV_EC_CTX * ctx,const unsigned char * ikm,size_t ikmlen)457 static EC_KEY *derivekey(PROV_EC_CTX *ctx,
458                          const unsigned char *ikm, size_t ikmlen)
459 {
460     int ret = 0;
461     EC_KEY *key;
462     unsigned char *seed = (unsigned char *)ikm;
463     size_t seedlen = ikmlen;
464     unsigned char tmpbuf[OSSL_HPKE_MAX_PRIVATE];
465 
466     key = EC_KEY_new_ex(ctx->libctx, ctx->propq);
467     if (key == NULL)
468         goto err;
469     if (!EC_KEY_set_group(key, EC_KEY_get0_group(ctx->recipient_key)))
470         goto err;
471 
472     /* Generate a random seed if there is no input ikm */
473     if (seed == NULL || seedlen == 0) {
474         seedlen = ctx->info->Nsk;
475         if (seedlen > sizeof(tmpbuf))
476             goto err;
477         if (RAND_priv_bytes_ex(ctx->libctx, tmpbuf, seedlen, 0) <= 0)
478             goto err;
479         seed = tmpbuf;
480     }
481     ret = ossl_ec_generate_key_dhkem(key, seed, seedlen);
482 err:
483     if (seed != ikm)
484         OPENSSL_cleanse(seed, seedlen);
485     if (ret <= 0) {
486         EC_KEY_free(key);
487         key = NULL;
488     }
489     return key;
490 }
491 
492 /*
493  * Before doing a key exchange the public key of the peer needs to be checked
494  * Note that the group check is not done here as we have already checked
495  * that it only uses one of the approved curve names when the key was set.
496  *
497  * Returns 1 if the public key is valid, or 0 if it fails.
498  */
check_publickey(const EC_KEY * pub)499 static int check_publickey(const EC_KEY *pub)
500 {
501     int ret = 0;
502     BN_CTX *bnctx = BN_CTX_new_ex(ossl_ec_key_get_libctx(pub));
503 
504     if (bnctx == NULL)
505         return 0;
506     ret = ossl_ec_key_public_check(pub, bnctx);
507     BN_CTX_free(bnctx);
508 
509     return ret;
510 }
511 
512 /*
513  * Do an ecdh key exchange.
514  * dhkm = DH(sender, peer)
515  *
516  * NOTE: Instead of using EVP_PKEY_derive() API's, we use EC_KEY operations
517  *       to avoid messy conversions back to EVP_PKEY.
518  *
519  * Returns the size of the secret if successful, or 0 otherwise,
520  */
generate_ecdhkm(const EC_KEY * sender,const EC_KEY * peer,unsigned char * out,size_t maxout,unsigned int secretsz)521 static int generate_ecdhkm(const EC_KEY *sender, const EC_KEY *peer,
522                            unsigned char *out, size_t maxout,
523                            unsigned int secretsz)
524 {
525     const EC_GROUP *group = EC_KEY_get0_group(sender);
526     size_t secretlen = (EC_GROUP_get_degree(group) + 7) / 8;
527 
528     if (secretlen != secretsz || secretlen > maxout) {
529         ERR_raise_data(ERR_LIB_PROV,  PROV_R_BAD_LENGTH, "secretsz invalid");
530         return 0;
531     }
532 
533     if (!check_publickey(peer))
534         return 0;
535     return ECDH_compute_key(out, secretlen, EC_KEY_get0_public_key(peer),
536                             sender, NULL) > 0;
537 }
538 
539 /*
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 privkey1
551  *     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 derivation
554  *              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 privkey2
559  *     and peerkey2 are non NULL (i.e. ctx->sender_authkey is not NULL).
560  */
derive_secret(PROV_EC_CTX * ctx,unsigned char * secret,const EC_KEY * privkey1,const EC_KEY * peerkey1,const EC_KEY * privkey2,const EC_KEY * peerkey2,const unsigned char * sender_pub,const unsigned char * recipient_pub)561 static int derive_secret(PROV_EC_CTX *ctx, unsigned char *secret,
562                          const EC_KEY *privkey1, const EC_KEY *peerkey1,
563                          const EC_KEY *privkey2, const EC_KEY *peerkey2,
564                          const unsigned char *sender_pub,
565                          const unsigned char *recipient_pub)
566 {
567     int ret = 0;
568     EVP_KDF_CTX *kdfctx = NULL;
569     unsigned char sender_authpub[OSSL_HPKE_MAX_PUBLIC];
570     unsigned char dhkm[OSSL_HPKE_MAX_PRIVATE * 2];
571     unsigned char kemctx[OSSL_HPKE_MAX_PUBLIC * 3];
572     size_t sender_authpublen;
573     size_t kemctxlen = 0, dhkmlen = 0;
574     const OSSL_HPKE_KEM_INFO *info = ctx->info;
575     size_t encodedpublen = info->Npk;
576     size_t encodedprivlen = info->Nsk;
577     int auth = ctx->sender_authkey != NULL;
578 
579     if (!generate_ecdhkm(privkey1, peerkey1, dhkm, sizeof(dhkm), encodedprivlen))
580         goto err;
581     dhkmlen = encodedprivlen;
582     kemctxlen = 2 * encodedpublen;
583 
584     /* Concat the optional second ECDH (used for Auth) */
585     if (auth) {
586         /* Get the public key of the auth sender in encoded form */
587         if (!ecpubkey_todata(ctx->sender_authkey, sender_authpub,
588                              &sender_authpublen, sizeof(sender_authpub)))
589             goto err;
590         if (sender_authpublen != encodedpublen) {
591             ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_KEY,
592                            "Invalid sender auth public key");
593             goto err;
594         }
595         if (!generate_ecdhkm(privkey2, peerkey2,
596                              dhkm + dhkmlen, sizeof(dhkm) - dhkmlen,
597                              encodedprivlen))
598             goto err;
599         dhkmlen += encodedprivlen;
600         kemctxlen += encodedpublen;
601     }
602     if (kemctxlen > sizeof(kemctx))
603         goto err;
604 
605     /* kemctx is the concat of both sides encoded public key */
606     memcpy(kemctx, sender_pub, info->Npk);
607     memcpy(kemctx + info->Npk, recipient_pub, info->Npk);
608     if (auth)
609         memcpy(kemctx + 2 * encodedpublen, sender_authpub, encodedpublen);
610     kdfctx = ossl_kdf_ctx_create(ctx->kdfname, info->mdname,
611                                  ctx->libctx, ctx->propq);
612     if (kdfctx == NULL)
613         goto err;
614     if (!dhkem_extract_and_expand(kdfctx, secret, info->Nsecret,
615                                   info->kem_id, dhkm, dhkmlen,
616                                   kemctx, kemctxlen))
617         goto err;
618     ret = 1;
619 err:
620     OPENSSL_cleanse(dhkm, dhkmlen);
621     EVP_KDF_CTX_free(kdfctx);
622     return ret;
623 }
624 
625 /*
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 the
632  *         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 return
635  *         values, without calculating the secret.
636  *     enclen Passes in the max size of the enc buffer and returns the
637  *            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 the
640  *               secret length.
641  * Returns: 1 on success or 0 otherwise.
642  */
dhkem_encap(PROV_EC_CTX * ctx,unsigned char * enc,size_t * enclen,unsigned char * secret,size_t * secretlen)643 static int dhkem_encap(PROV_EC_CTX *ctx,
644                        unsigned char *enc, size_t *enclen,
645                        unsigned char *secret, size_t *secretlen)
646 {
647     int ret = 0;
648     EC_KEY *sender_ephemkey = NULL;
649     unsigned char sender_pub[OSSL_HPKE_MAX_PUBLIC];
650     unsigned char recipient_pub[OSSL_HPKE_MAX_PUBLIC];
651     size_t sender_publen, recipient_publen;
652     const OSSL_HPKE_KEM_INFO *info = ctx->info;
653 
654     if (enc == NULL) {
655         if (enclen == NULL && secretlen == NULL)
656             return 0;
657         if (enclen != NULL)
658             *enclen = info->Nenc;
659         if (secretlen != NULL)
660             *secretlen = info->Nsecret;
661        return 1;
662     }
663 
664     if (*secretlen < info->Nsecret) {
665         ERR_raise_data(ERR_LIB_PROV, PROV_R_BAD_LENGTH, "*secretlen too small");
666         return 0;
667     }
668     if (*enclen < info->Nenc) {
669         ERR_raise_data(ERR_LIB_PROV, PROV_R_BAD_LENGTH, "*enclen too small");
670         return 0;
671     }
672 
673     /* Create an ephemeral key */
674     sender_ephemkey = derivekey(ctx, ctx->ikm, ctx->ikmlen);
675     if (sender_ephemkey == NULL)
676         goto err;
677     if (!ecpubkey_todata(sender_ephemkey, sender_pub, &sender_publen,
678                          sizeof(sender_pub))
679             || !ecpubkey_todata(ctx->recipient_key, recipient_pub,
680                                 &recipient_publen, sizeof(recipient_pub)))
681         goto err;
682 
683     if (sender_publen != info->Npk
684             || recipient_publen != sender_publen) {
685         ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_KEY, "Invalid public key");
686         goto err;
687     }
688 
689     if (!derive_secret(ctx, secret,
690                        sender_ephemkey, ctx->recipient_key,
691                        ctx->sender_authkey, ctx->recipient_key,
692                        sender_pub, recipient_pub))
693         goto err;
694 
695     /* Return the senders ephemeral public key in encoded form */
696     memcpy(enc, sender_pub, sender_publen);
697     *enclen = sender_publen;
698     *secretlen = info->Nsecret;
699     ret = 1;
700 err:
701     EC_KEY_free(sender_ephemkey);
702     return ret;
703 }
704 
705 /*
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 the
711  *         optional senders auth public key.
712  *     secret A buffer to return the calculated shared secret. Setting this to
713  *            NULL can be used to return the secretlen.
714  *     secretlen Passes in the max size of the secret buffer and returns the
715  *               secret length.
716  *     enc A buffer containing the senders ephemeral public key that was returned
717  *         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  */
dhkem_decap(PROV_EC_CTX * ctx,unsigned char * secret,size_t * secretlen,const unsigned char * enc,size_t enclen)721 static int dhkem_decap(PROV_EC_CTX *ctx,
722                        unsigned char *secret, size_t *secretlen,
723                        const unsigned char *enc, size_t enclen)
724 {
725     int ret = 0;
726     EC_KEY *sender_ephempubkey = NULL;
727     const OSSL_HPKE_KEM_INFO *info = ctx->info;
728     unsigned char recipient_pub[OSSL_HPKE_MAX_PUBLIC];
729     size_t recipient_publen;
730     size_t encodedpublen = info->Npk;
731 
732     if (secret == NULL) {
733         *secretlen = info->Nsecret;
734         return 1;
735     }
736 
737     if (*secretlen < info->Nsecret) {
738         ERR_raise_data(ERR_LIB_PROV, PROV_R_BAD_LENGTH, "*secretlen too small");
739         return 0;
740     }
741     if (enclen != encodedpublen) {
742         ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_KEY, "Invalid enc public key");
743         return 0;
744     }
745 
746     sender_ephempubkey = eckey_frompub(ctx->recipient_key, enc, enclen);
747     if (sender_ephempubkey == NULL)
748         goto err;
749     if (!ecpubkey_todata(ctx->recipient_key, recipient_pub, &recipient_publen,
750                          sizeof(recipient_pub)))
751         goto err;
752     if (recipient_publen != encodedpublen) {
753         ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_KEY, "Invalid recipient public key");
754         goto err;
755     }
756 
757     if (!derive_secret(ctx, secret,
758                        ctx->recipient_key, sender_ephempubkey,
759                        ctx->recipient_key, ctx->sender_authkey,
760                        enc, recipient_pub))
761         goto err;
762     *secretlen = info->Nsecret;
763     ret = 1;
764 err:
765     EC_KEY_free(sender_ephempubkey);
766     return ret;
767 }
768 
eckem_encapsulate(void * vctx,unsigned char * out,size_t * outlen,unsigned char * secret,size_t * secretlen)769 static int eckem_encapsulate(void *vctx, unsigned char *out, size_t *outlen,
770                              unsigned char *secret, size_t *secretlen)
771 {
772     PROV_EC_CTX *ctx = (PROV_EC_CTX *)vctx;
773 
774     switch (ctx->mode) {
775         case KEM_MODE_DHKEM:
776             return dhkem_encap(ctx, out, outlen, secret, secretlen);
777         default:
778             ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_MODE);
779             return -2;
780     }
781 }
782 
eckem_decapsulate(void * vctx,unsigned char * out,size_t * outlen,const unsigned char * in,size_t inlen)783 static int eckem_decapsulate(void *vctx, unsigned char *out, size_t *outlen,
784                              const unsigned char *in, size_t inlen)
785 {
786     PROV_EC_CTX *ctx = (PROV_EC_CTX *)vctx;
787 
788     switch (ctx->mode) {
789         case KEM_MODE_DHKEM:
790             return dhkem_decap(ctx, out, outlen, in, inlen);
791         default:
792             ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_MODE);
793             return -2;
794     }
795 }
796 
797 const 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 },
814     OSSL_DISPATCH_END
815 };
816