1 /*
2  * Copyright 2022-2023 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 
201     return ctx;
202 }
203 
eckem_freectx(void * vectx)204 static void eckem_freectx(void *vectx)
205 {
206     PROV_EC_CTX *ctx = (PROV_EC_CTX *)vectx;
207 
208     OPENSSL_clear_free(ctx->ikm, ctx->ikmlen);
209     recipient_key_set(ctx, NULL);
210     sender_authkey_set(ctx, NULL);
211     OPENSSL_free(ctx);
212 }
213 
ossl_ec_match_params(const EC_KEY * key1,const EC_KEY * key2)214 static int ossl_ec_match_params(const EC_KEY *key1, const EC_KEY *key2)
215 {
216     int ret;
217     BN_CTX *ctx = NULL;
218     const EC_GROUP *group1 = EC_KEY_get0_group(key1);
219     const EC_GROUP *group2 = EC_KEY_get0_group(key2);
220 
221     ctx = BN_CTX_new_ex(ossl_ec_key_get_libctx(key1));
222     if (ctx == NULL)
223         return 0;
224 
225     ret = group1 != NULL
226           && group2 != NULL
227           && EC_GROUP_cmp(group1, group2, ctx) == 0;
228     if (!ret)
229         ERR_raise(ERR_LIB_PROV, PROV_R_MISMATCHING_DOMAIN_PARAMETERS);
230     BN_CTX_free(ctx);
231     return ret;
232 }
233 
eckem_init(void * vctx,int operation,void * vec,void * vauth,const OSSL_PARAM params[])234 static int eckem_init(void *vctx, int operation, void *vec, void *vauth,
235                       const OSSL_PARAM params[])
236 {
237     int rv;
238     PROV_EC_CTX *ctx = (PROV_EC_CTX *)vctx;
239     EC_KEY *ec = vec;
240     EC_KEY *auth = vauth;
241 
242     if (!ossl_prov_is_running())
243         return 0;
244 
245     if (!eckey_check(ec, operation == EVP_PKEY_OP_DECAPSULATE))
246         return 0;
247     rv = recipient_key_set(ctx, ec);
248     if (rv <= 0)
249         return rv;
250 
251     if (auth != NULL) {
252         if (!ossl_ec_match_params(ec, auth)
253             || !eckey_check(auth, operation == EVP_PKEY_OP_ENCAPSULATE)
254             || !sender_authkey_set(ctx, auth))
255         return 0;
256     }
257 
258     ctx->op = operation;
259     return eckem_set_ctx_params(vctx, params);
260 }
261 
eckem_encapsulate_init(void * vctx,void * vec,const OSSL_PARAM params[])262 static int eckem_encapsulate_init(void *vctx, void *vec,
263                                    const OSSL_PARAM params[])
264 {
265     return eckem_init(vctx, EVP_PKEY_OP_ENCAPSULATE, vec, NULL, params);
266 }
267 
eckem_decapsulate_init(void * vctx,void * vec,const OSSL_PARAM params[])268 static int eckem_decapsulate_init(void *vctx, void *vec,
269                                    const OSSL_PARAM params[])
270 {
271     return eckem_init(vctx, EVP_PKEY_OP_DECAPSULATE, vec, NULL, params);
272 }
273 
eckem_auth_encapsulate_init(void * vctx,void * vecx,void * vauthpriv,const OSSL_PARAM params[])274 static int eckem_auth_encapsulate_init(void *vctx, void *vecx, void *vauthpriv,
275                                        const OSSL_PARAM params[])
276 {
277     return eckem_init(vctx, EVP_PKEY_OP_ENCAPSULATE, vecx, vauthpriv, params);
278 }
279 
eckem_auth_decapsulate_init(void * vctx,void * vecx,void * vauthpub,const OSSL_PARAM params[])280 static int eckem_auth_decapsulate_init(void *vctx, void *vecx, void *vauthpub,
281                                        const OSSL_PARAM params[])
282 {
283     return eckem_init(vctx, EVP_PKEY_OP_DECAPSULATE, vecx, vauthpub, params);
284 }
285 
eckem_set_ctx_params(void * vctx,const OSSL_PARAM params[])286 static int eckem_set_ctx_params(void *vctx, const OSSL_PARAM params[])
287 {
288     PROV_EC_CTX *ctx = (PROV_EC_CTX *)vctx;
289     const OSSL_PARAM *p;
290     int mode;
291 
292     if (ossl_param_is_empty(params))
293         return 1;
294 
295     p = OSSL_PARAM_locate_const(params, OSSL_KEM_PARAM_IKME);
296     if (p != NULL) {
297         void *tmp = NULL;
298         size_t tmplen = 0;
299 
300         if (p->data != NULL && p->data_size != 0) {
301             if (!OSSL_PARAM_get_octet_string(p, &tmp, 0, &tmplen))
302                 return 0;
303         }
304         OPENSSL_clear_free(ctx->ikm, ctx->ikmlen);
305         /* Set the ephemeral seed */
306         ctx->ikm = tmp;
307         ctx->ikmlen = tmplen;
308     }
309 
310     p = OSSL_PARAM_locate_const(params, OSSL_KEM_PARAM_OPERATION);
311     if (p != NULL) {
312         if (p->data_type != OSSL_PARAM_UTF8_STRING)
313             return 0;
314         mode = ossl_eckem_modename2id(p->data);
315         if (mode == KEM_MODE_UNDEFINED)
316             return 0;
317         ctx->mode = mode;
318     }
319     return 1;
320 }
321 
322 static const OSSL_PARAM known_settable_eckem_ctx_params[] = {
323     OSSL_PARAM_utf8_string(OSSL_KEM_PARAM_OPERATION, NULL, 0),
324     OSSL_PARAM_octet_string(OSSL_KEM_PARAM_IKME, NULL, 0),
325     OSSL_PARAM_END
326 };
327 
eckem_settable_ctx_params(ossl_unused void * vctx,ossl_unused void * provctx)328 static const OSSL_PARAM *eckem_settable_ctx_params(ossl_unused void *vctx,
329                                                    ossl_unused void *provctx)
330 {
331     return known_settable_eckem_ctx_params;
332 }
333 
334 /*
335  * See Section 4.1 DH-Based KEM (DHKEM) ExtractAndExpand
336  */
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)337 static int dhkem_extract_and_expand(EVP_KDF_CTX *kctx,
338                                     unsigned char *okm, size_t okmlen,
339                                     uint16_t kemid,
340                                     const unsigned char *dhkm, size_t dhkmlen,
341                                     const unsigned char *kemctx,
342                                     size_t kemctxlen)
343 {
344     uint8_t suiteid[2];
345     uint8_t prk[EVP_MAX_MD_SIZE];
346     size_t prklen = okmlen;
347     int ret;
348 
349     if (prklen > sizeof(prk))
350         return 0;
351 
352     suiteid[0] = (kemid >> 8) & 0xff;
353     suiteid[1] = kemid & 0xff;
354 
355     ret = ossl_hpke_labeled_extract(kctx, prk, prklen,
356                                     NULL, 0, LABEL_KEM, suiteid, sizeof(suiteid),
357                                     OSSL_DHKEM_LABEL_EAE_PRK, dhkm, dhkmlen)
358           && ossl_hpke_labeled_expand(kctx, okm, okmlen, prk, prklen,
359                                       LABEL_KEM, suiteid, sizeof(suiteid),
360                                       OSSL_DHKEM_LABEL_SHARED_SECRET,
361                                       kemctx, kemctxlen);
362     OPENSSL_cleanse(prk, prklen);
363     return ret;
364 }
365 
366 /*
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 assumed
375  *          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 bytes
378  * Returns:
379  *     1 if successful or 0 otherwise.
380  */
ossl_ec_dhkem_derive_private(EC_KEY * ec,BIGNUM * priv,const unsigned char * ikm,size_t ikmlen)381 int ossl_ec_dhkem_derive_private(EC_KEY *ec, BIGNUM *priv,
382                                  const unsigned char *ikm, size_t ikmlen)
383 {
384     int ret = 0;
385     EVP_KDF_CTX *kdfctx = NULL;
386     uint8_t suiteid[2];
387     unsigned char prk[OSSL_HPKE_MAX_SECRET];
388     unsigned char privbuf[OSSL_HPKE_MAX_PRIVATE];
389     const BIGNUM *order;
390     unsigned char counter = 0;
391     const char *curve = ec_curvename_get0(ec);
392     const OSSL_HPKE_KEM_INFO *info;
393 
394     if (curve == NULL)
395         return -2;
396 
397     info = ossl_HPKE_KEM_INFO_find_curve(curve);
398     if (info == NULL)
399         return -2;
400 
401     kdfctx = ossl_kdf_ctx_create("HKDF", info->mdname,
402                                  ossl_ec_key_get_libctx(ec),
403                                  ossl_ec_key_get0_propq(ec));
404     if (kdfctx == NULL)
405         return 0;
406 
407     /* ikmlen should have a length of at least Nsk */
408     if (ikmlen < info->Nsecret) {
409         ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_INPUT_LENGTH,
410                        "ikm length is :%zu, should be at least %zu",
411                        ikmlen, info->Nsecret);
412         goto err;
413     }
414 
415     suiteid[0] = info->kem_id / 256;
416     suiteid[1] = info->kem_id % 256;
417 
418     if (!ossl_hpke_labeled_extract(kdfctx, prk, info->Nsecret,
419                                    NULL, 0, LABEL_KEM, suiteid, sizeof(suiteid),
420                                    OSSL_DHKEM_LABEL_DKP_PRK, ikm, ikmlen))
421         goto err;
422 
423     order = EC_GROUP_get0_order(EC_KEY_get0_group(ec));
424     do {
425         if (!ossl_hpke_labeled_expand(kdfctx, privbuf, info->Nsk,
426                                       prk, info->Nsecret,
427                                       LABEL_KEM, suiteid, sizeof(suiteid),
428                                       OSSL_DHKEM_LABEL_CANDIDATE,
429                                       &counter, 1))
430             goto err;
431         privbuf[0] &= info->bitmask;
432         if (BN_bin2bn(privbuf, info->Nsk, priv) == NULL)
433             goto err;
434         if (counter == 0xFF) {
435             ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GENERATE_KEY);
436             goto err;
437         }
438         counter++;
439     } while (BN_is_zero(priv) || BN_cmp(priv, order) >= 0);
440     ret = 1;
441 err:
442     OPENSSL_cleanse(prk, sizeof(prk));
443     OPENSSL_cleanse(privbuf, sizeof(privbuf));
444     EVP_KDF_CTX_free(kdfctx);
445     return ret;
446 }
447 
448 /*
449  * Do a keygen operation without having to use EVP_PKEY.
450  * Params:
451  *     ctx Context object
452  *     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  */
derivekey(PROV_EC_CTX * ctx,const unsigned char * ikm,size_t ikmlen)456 static EC_KEY *derivekey(PROV_EC_CTX *ctx,
457                          const unsigned char *ikm, size_t ikmlen)
458 {
459     int ret = 0;
460     EC_KEY *key;
461     unsigned char *seed = (unsigned char *)ikm;
462     size_t seedlen = ikmlen;
463     unsigned char tmpbuf[OSSL_HPKE_MAX_PRIVATE];
464 
465     key = EC_KEY_new_ex(ctx->libctx, ctx->propq);
466     if (key == NULL)
467         goto err;
468     if (!EC_KEY_set_group(key, EC_KEY_get0_group(ctx->recipient_key)))
469         goto err;
470 
471     /* Generate a random seed if there is no input ikm */
472     if (seed == NULL || seedlen == 0) {
473         seedlen = ctx->info->Nsk;
474         if (seedlen > sizeof(tmpbuf))
475             goto err;
476         if (RAND_priv_bytes_ex(ctx->libctx, tmpbuf, seedlen, 0) <= 0)
477             goto err;
478         seed = tmpbuf;
479     }
480     ret = ossl_ec_generate_key_dhkem(key, seed, seedlen);
481 err:
482     if (seed != ikm)
483         OPENSSL_cleanse(seed, seedlen);
484     if (ret <= 0) {
485         EC_KEY_free(key);
486         key = NULL;
487     }
488     return key;
489 }
490 
491 /*
492  * Before doing a key exchange the public key of the peer needs to be checked
493  * Note that the group check is not done here as we have already checked
494  * 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  */
check_publickey(const EC_KEY * pub)498 static int check_publickey(const EC_KEY *pub)
499 {
500     int ret = 0;
501     BN_CTX *bnctx = BN_CTX_new_ex(ossl_ec_key_get_libctx(pub));
502 
503     if (bnctx == NULL)
504         return 0;
505     ret = ossl_ec_key_public_check(pub, bnctx);
506     BN_CTX_free(bnctx);
507 
508     return ret;
509 }
510 
511 /*
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 operations
516  *       to avoid messy conversions back to EVP_PKEY.
517  *
518  * Returns the size of the secret if successful, or 0 otherwise,
519  */
generate_ecdhkm(const EC_KEY * sender,const EC_KEY * peer,unsigned char * out,size_t maxout,unsigned int secretsz)520 static int generate_ecdhkm(const EC_KEY *sender, const EC_KEY *peer,
521                            unsigned char *out, size_t maxout,
522                            unsigned int secretsz)
523 {
524     const EC_GROUP *group = EC_KEY_get0_group(sender);
525     size_t secretlen = (EC_GROUP_get_degree(group) + 7) / 8;
526 
527     if (secretlen != secretsz || secretlen > maxout) {
528         ERR_raise_data(ERR_LIB_PROV,  PROV_R_BAD_LENGTH, "secretsz invalid");
529         return 0;
530     }
531 
532     if (!check_publickey(peer))
533         return 0;
534     return ECDH_compute_key(out, secretlen, EC_KEY_get0_public_key(peer),
535                             sender, NULL) > 0;
536 }
537 
538 /*
539  * Derive a secret using ECDH (code is shared by the encap and decap)
540  *
541  * dhkm = Concat(ecdh(privkey1, peerkey1), ecdh(privkey2, peerkey2)
542  * kemctx = Concat(sender_pub, recipient_pub, ctx->sender_authkey)
543  * secret = dhkem_extract_and_expand(kemid, dhkm, kemctx);
544  *
545  * Params:
546  *     ctx Object that contains algorithm state and constants.
547  *     secret The returned secret (with a length ctx->alg->secretlen bytes).
548  *     privkey1 A private key used for ECDH key derivation.
549  *     peerkey1 A public key used for ECDH key derivation with privkey1
550  *     privkey2 A optional private key used for a second ECDH key derivation.
551  *              It can be NULL.
552  *     peerkey2 A optional public key used for a second ECDH key derivation
553  *              with privkey2,. It can be NULL.
554  *     sender_pub The senders public key in encoded form.
555  *     recipient_pub The recipients public key in encoded form.
556  * Notes:
557  *     The second ecdh() is only used for the HPKE auth modes when both privkey2
558  *     and peerkey2 are non NULL (i.e. ctx->sender_authkey is not NULL).
559  */
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)560 static int derive_secret(PROV_EC_CTX *ctx, unsigned char *secret,
561                          const EC_KEY *privkey1, const EC_KEY *peerkey1,
562                          const EC_KEY *privkey2, const EC_KEY *peerkey2,
563                          const unsigned char *sender_pub,
564                          const unsigned char *recipient_pub)
565 {
566     int ret = 0;
567     EVP_KDF_CTX *kdfctx = NULL;
568     unsigned char sender_authpub[OSSL_HPKE_MAX_PUBLIC];
569     unsigned char dhkm[OSSL_HPKE_MAX_PRIVATE * 2];
570     unsigned char kemctx[OSSL_HPKE_MAX_PUBLIC * 3];
571     size_t sender_authpublen;
572     size_t kemctxlen = 0, dhkmlen = 0;
573     const OSSL_HPKE_KEM_INFO *info = ctx->info;
574     size_t encodedpublen = info->Npk;
575     size_t encodedprivlen = info->Nsk;
576     int auth = ctx->sender_authkey != NULL;
577 
578     if (!generate_ecdhkm(privkey1, peerkey1, dhkm, sizeof(dhkm), encodedprivlen))
579         goto err;
580     dhkmlen = encodedprivlen;
581     kemctxlen = 2 * encodedpublen;
582 
583     /* Concat the optional second ECDH (used for Auth) */
584     if (auth) {
585         /* Get the public key of the auth sender in encoded form */
586         if (!ecpubkey_todata(ctx->sender_authkey, sender_authpub,
587                              &sender_authpublen, sizeof(sender_authpub)))
588             goto err;
589         if (sender_authpublen != encodedpublen) {
590             ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_KEY,
591                            "Invalid sender auth public key");
592             goto err;
593         }
594         if (!generate_ecdhkm(privkey2, peerkey2,
595                              dhkm + dhkmlen, sizeof(dhkm) - dhkmlen,
596                              encodedprivlen))
597             goto err;
598         dhkmlen += encodedprivlen;
599         kemctxlen += encodedpublen;
600     }
601     if (kemctxlen > sizeof(kemctx))
602         goto err;
603 
604     /* kemctx is the concat of both sides encoded public key */
605     memcpy(kemctx, sender_pub, info->Npk);
606     memcpy(kemctx + info->Npk, recipient_pub, info->Npk);
607     if (auth)
608         memcpy(kemctx + 2 * encodedpublen, sender_authpub, encodedpublen);
609     kdfctx = ossl_kdf_ctx_create(ctx->kdfname, info->mdname,
610                                  ctx->libctx, ctx->propq);
611     if (kdfctx == NULL)
612         goto err;
613     if (!dhkem_extract_and_expand(kdfctx, secret, info->Nsecret,
614                                   info->kem_id, dhkm, dhkmlen,
615                                   kemctx, kemctxlen))
616         goto err;
617     ret = 1;
618 err:
619     OPENSSL_cleanse(dhkm, dhkmlen);
620     EVP_KDF_CTX_free(kdfctx);
621     return ret;
622 }
623 
624 /*
625  * Do a DHKEM encapsulate operation.
626  *
627  * See Section 4.1 Encap() and AuthEncap()
628  *
629  * Params:
630  *     ctx A context object holding the recipients public key and the
631  *         optional senders auth private key.
632  *     enc A buffer to return the senders ephemeral public key.
633  *         Setting this to NULL allows the enclen and secretlen to return
634  *         values, without calculating the secret.
635  *     enclen Passes in the max size of the enc buffer and returns the
636  *            encoded public key length.
637  *     secret A buffer to return the calculated shared secret.
638  *     secretlen Passes in the max size of the secret buffer and returns the
639  *               secret length.
640  * Returns: 1 on success or 0 otherwise.
641  */
dhkem_encap(PROV_EC_CTX * ctx,unsigned char * enc,size_t * enclen,unsigned char * secret,size_t * secretlen)642 static int dhkem_encap(PROV_EC_CTX *ctx,
643                        unsigned char *enc, size_t *enclen,
644                        unsigned char *secret, size_t *secretlen)
645 {
646     int ret = 0;
647     EC_KEY *sender_ephemkey = NULL;
648     unsigned char sender_pub[OSSL_HPKE_MAX_PUBLIC];
649     unsigned char recipient_pub[OSSL_HPKE_MAX_PUBLIC];
650     size_t sender_publen, recipient_publen;
651     const OSSL_HPKE_KEM_INFO *info = ctx->info;
652 
653     if (enc == NULL) {
654         if (enclen == NULL && secretlen == NULL)
655             return 0;
656         if (enclen != NULL)
657             *enclen = info->Nenc;
658         if (secretlen != NULL)
659             *secretlen = info->Nsecret;
660        return 1;
661     }
662 
663     if (*secretlen < info->Nsecret) {
664         ERR_raise_data(ERR_LIB_PROV, PROV_R_BAD_LENGTH, "*secretlen too small");
665         return 0;
666     }
667     if (*enclen < info->Nenc) {
668         ERR_raise_data(ERR_LIB_PROV, PROV_R_BAD_LENGTH, "*enclen too small");
669         return 0;
670     }
671 
672     /* Create an ephemeral key */
673     sender_ephemkey = derivekey(ctx, ctx->ikm, ctx->ikmlen);
674     if (sender_ephemkey == NULL)
675         goto err;
676     if (!ecpubkey_todata(sender_ephemkey, sender_pub, &sender_publen,
677                          sizeof(sender_pub))
678             || !ecpubkey_todata(ctx->recipient_key, recipient_pub,
679                                 &recipient_publen, sizeof(recipient_pub)))
680         goto err;
681 
682     if (sender_publen != info->Npk
683             || recipient_publen != sender_publen) {
684         ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_KEY, "Invalid public key");
685         goto err;
686     }
687 
688     if (!derive_secret(ctx, secret,
689                        sender_ephemkey, ctx->recipient_key,
690                        ctx->sender_authkey, ctx->recipient_key,
691                        sender_pub, recipient_pub))
692         goto err;
693 
694     /* Return the senders ephemeral public key in encoded form */
695     memcpy(enc, sender_pub, sender_publen);
696     *enclen = sender_publen;
697     *secretlen = info->Nsecret;
698     ret = 1;
699 err:
700     EC_KEY_free(sender_ephemkey);
701     return ret;
702 }
703 
704 /*
705  * Do a DHKEM decapsulate operation.
706  * See Section 4.1 Decap() and Auth Decap()
707  *
708  * Params:
709  *     ctx A context object holding the recipients private key and the
710  *         optional senders auth public key.
711  *     secret A buffer to return the calculated shared secret. Setting this to
712  *            NULL can be used to return the secretlen.
713  *     secretlen Passes in the max size of the secret buffer and returns the
714  *               secret length.
715  *     enc A buffer containing the senders ephemeral public key that was returned
716  *         from dhkem_encap().
717  *     enclen The length in bytes of enc.
718  * Returns: 1 If the shared secret is returned or 0 on error.
719  */
dhkem_decap(PROV_EC_CTX * ctx,unsigned char * secret,size_t * secretlen,const unsigned char * enc,size_t enclen)720 static int dhkem_decap(PROV_EC_CTX *ctx,
721                        unsigned char *secret, size_t *secretlen,
722                        const unsigned char *enc, size_t enclen)
723 {
724     int ret = 0;
725     EC_KEY *sender_ephempubkey = NULL;
726     const OSSL_HPKE_KEM_INFO *info = ctx->info;
727     unsigned char recipient_pub[OSSL_HPKE_MAX_PUBLIC];
728     size_t recipient_publen;
729     size_t encodedpublen = info->Npk;
730 
731     if (secret == NULL) {
732         *secretlen = info->Nsecret;
733         return 1;
734     }
735 
736     if (*secretlen < info->Nsecret) {
737         ERR_raise_data(ERR_LIB_PROV, PROV_R_BAD_LENGTH, "*secretlen too small");
738         return 0;
739     }
740     if (enclen != encodedpublen) {
741         ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_KEY, "Invalid enc public key");
742         return 0;
743     }
744 
745     sender_ephempubkey = eckey_frompub(ctx->recipient_key, enc, enclen);
746     if (sender_ephempubkey == NULL)
747         goto err;
748     if (!ecpubkey_todata(ctx->recipient_key, recipient_pub, &recipient_publen,
749                          sizeof(recipient_pub)))
750         goto err;
751     if (recipient_publen != encodedpublen) {
752         ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_KEY, "Invalid recipient public key");
753         goto err;
754     }
755 
756     if (!derive_secret(ctx, secret,
757                        ctx->recipient_key, sender_ephempubkey,
758                        ctx->recipient_key, ctx->sender_authkey,
759                        enc, recipient_pub))
760         goto err;
761     *secretlen = info->Nsecret;
762     ret = 1;
763 err:
764     EC_KEY_free(sender_ephempubkey);
765     return ret;
766 }
767 
eckem_encapsulate(void * vctx,unsigned char * out,size_t * outlen,unsigned char * secret,size_t * secretlen)768 static int eckem_encapsulate(void *vctx, unsigned char *out, size_t *outlen,
769                              unsigned char *secret, size_t *secretlen)
770 {
771     PROV_EC_CTX *ctx = (PROV_EC_CTX *)vctx;
772 
773     switch (ctx->mode) {
774         case KEM_MODE_DHKEM:
775             return dhkem_encap(ctx, out, outlen, secret, secretlen);
776         default:
777             ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_MODE);
778             return -2;
779     }
780 }
781 
eckem_decapsulate(void * vctx,unsigned char * out,size_t * outlen,const unsigned char * in,size_t inlen)782 static int eckem_decapsulate(void *vctx, unsigned char *out, size_t *outlen,
783                              const unsigned char *in, size_t inlen)
784 {
785     PROV_EC_CTX *ctx = (PROV_EC_CTX *)vctx;
786 
787     switch (ctx->mode) {
788         case KEM_MODE_DHKEM:
789             return dhkem_decap(ctx, out, outlen, in, inlen);
790         default:
791             ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_MODE);
792             return -2;
793     }
794 }
795 
796 const OSSL_DISPATCH ossl_ec_asym_kem_functions[] = {
797     { OSSL_FUNC_KEM_NEWCTX, (void (*)(void))eckem_newctx },
798     { OSSL_FUNC_KEM_ENCAPSULATE_INIT,
799       (void (*)(void))eckem_encapsulate_init },
800     { OSSL_FUNC_KEM_ENCAPSULATE, (void (*)(void))eckem_encapsulate },
801     { OSSL_FUNC_KEM_DECAPSULATE_INIT,
802       (void (*)(void))eckem_decapsulate_init },
803     { OSSL_FUNC_KEM_DECAPSULATE, (void (*)(void))eckem_decapsulate },
804     { OSSL_FUNC_KEM_FREECTX, (void (*)(void))eckem_freectx },
805     { OSSL_FUNC_KEM_SET_CTX_PARAMS,
806       (void (*)(void))eckem_set_ctx_params },
807     { OSSL_FUNC_KEM_SETTABLE_CTX_PARAMS,
808       (void (*)(void))eckem_settable_ctx_params },
809     { OSSL_FUNC_KEM_AUTH_ENCAPSULATE_INIT,
810       (void (*)(void))eckem_auth_encapsulate_init },
811     { OSSL_FUNC_KEM_AUTH_DECAPSULATE_INIT,
812       (void (*)(void))eckem_auth_decapsulate_init },
813     OSSL_DISPATCH_END
814 };
815