xref: /freebsd/crypto/openssl/providers/implementations/kem/ecx_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  * ECX keys (i.e. X25519 and X448)
13  * References to Sections in the comments below refer to RFC 9180.
14  */
15 
16 #include "internal/deprecated.h"
17 
18 #include <string.h>
19 #include <openssl/crypto.h>
20 #include <openssl/evp.h>
21 #include <openssl/core_dispatch.h>
22 #include <openssl/core_names.h>
23 #include <openssl/params.h>
24 #include <openssl/kdf.h>
25 #include <openssl/err.h>
26 #include <openssl/sha.h>
27 #include <openssl/rand.h>
28 #include <openssl/proverr.h>
29 #include "prov/provider_ctx.h"
30 #include "prov/implementations.h"
31 #include "prov/securitycheck.h"
32 #include "prov/providercommon.h"
33 #include "prov/ecx.h"
34 #include "crypto/ecx.h"
35 #include <openssl/hpke.h>
36 #include "internal/hpke_util.h"
37 #include "eckem.h"
38 
39 #define MAX_ECX_KEYLEN X448_KEYLEN
40 
41 /* KEM identifiers from Section 7.1 "Table 2 KEM IDs" */
42 #define KEMID_X25519_HKDF_SHA256 0x20
43 #define KEMID_X448_HKDF_SHA512   0x21
44 
45 /* ASCII: "KEM", in hex for EBCDIC compatibility */
46 static const char LABEL_KEM[] = "\x4b\x45\x4d";
47 
48 typedef struct {
49     ECX_KEY *recipient_key;
50     ECX_KEY *sender_authkey;
51     OSSL_LIB_CTX *libctx;
52     char *propq;
53     unsigned int mode;
54     unsigned int op;
55     unsigned char *ikm;
56     size_t ikmlen;
57     const char *kdfname;
58     const OSSL_HPKE_KEM_INFO *info;
59 } PROV_ECX_CTX;
60 
61 static OSSL_FUNC_kem_newctx_fn ecxkem_newctx;
62 static OSSL_FUNC_kem_encapsulate_init_fn ecxkem_encapsulate_init;
63 static OSSL_FUNC_kem_encapsulate_fn ecxkem_encapsulate;
64 static OSSL_FUNC_kem_decapsulate_init_fn ecxkem_decapsulate_init;
65 static OSSL_FUNC_kem_decapsulate_fn ecxkem_decapsulate;
66 static OSSL_FUNC_kem_freectx_fn ecxkem_freectx;
67 static OSSL_FUNC_kem_set_ctx_params_fn ecxkem_set_ctx_params;
68 static OSSL_FUNC_kem_auth_encapsulate_init_fn ecxkem_auth_encapsulate_init;
69 static OSSL_FUNC_kem_auth_decapsulate_init_fn ecxkem_auth_decapsulate_init;
70 
71 /*
72  * Set KEM values as specified in Section 7.1 "Table 2 KEM IDs"
73  * There is only one set of values for X25519 and X448.
74  * Additional values could be set via set_params if required.
75  */
get_kem_info(ECX_KEY * ecx)76 static const OSSL_HPKE_KEM_INFO *get_kem_info(ECX_KEY *ecx)
77 {
78     const char *name = NULL;
79 
80     if (ecx->type == ECX_KEY_TYPE_X25519)
81         name = SN_X25519;
82     else
83         name = SN_X448;
84     return ossl_HPKE_KEM_INFO_find_curve(name);
85 }
86 
87 /*
88  * Set the recipient key, and free any existing key.
89  * ecx can be NULL. The ecx key may have only a private or public component.
90  */
recipient_key_set(PROV_ECX_CTX * ctx,ECX_KEY * ecx)91 static int recipient_key_set(PROV_ECX_CTX *ctx, ECX_KEY *ecx)
92 {
93     ossl_ecx_key_free(ctx->recipient_key);
94     ctx->recipient_key = NULL;
95     if (ecx != NULL) {
96         ctx->info = get_kem_info(ecx);
97         if (ctx->info == NULL)
98             return -2;
99         ctx->kdfname = "HKDF";
100         if (!ossl_ecx_key_up_ref(ecx))
101             return 0;
102         ctx->recipient_key = ecx;
103     }
104     return 1;
105 }
106 
107 /*
108  * Set the senders auth key, and free any existing auth key.
109  * ecx can be NULL.
110  */
sender_authkey_set(PROV_ECX_CTX * ctx,ECX_KEY * ecx)111 static int sender_authkey_set(PROV_ECX_CTX *ctx, ECX_KEY *ecx)
112 {
113     ossl_ecx_key_free(ctx->sender_authkey);
114     ctx->sender_authkey = NULL;
115 
116     if (ecx != NULL) {
117         if (!ossl_ecx_key_up_ref(ecx))
118             return 0;
119         ctx->sender_authkey = ecx;
120     }
121     return 1;
122 }
123 
124 /*
125  * Serialize a public key from byte array's for the encoded public keys.
126  * ctx is used to access the key type.
127  * Returns: The created ECX_KEY or NULL on error.
128  */
ecxkey_pubfromdata(PROV_ECX_CTX * ctx,const unsigned char * pubbuf,size_t pubbuflen)129 static ECX_KEY *ecxkey_pubfromdata(PROV_ECX_CTX *ctx,
130                                    const unsigned char *pubbuf, size_t pubbuflen)
131 {
132     ECX_KEY *ecx = NULL;
133     OSSL_PARAM params[2], *p = params;
134 
135     *p++ = OSSL_PARAM_construct_octet_string(OSSL_PKEY_PARAM_PUB_KEY,
136                                              (char *)pubbuf, pubbuflen);
137     *p = OSSL_PARAM_construct_end();
138 
139     ecx = ossl_ecx_key_new(ctx->libctx, ctx->recipient_key->type, 1, ctx->propq);
140     if (ecx == NULL)
141         return NULL;
142     if (ossl_ecx_key_fromdata(ecx, params, 0) <= 0) {
143         ossl_ecx_key_free(ecx);
144         ecx = NULL;
145     }
146     return ecx;
147 }
148 
ecx_pubkey(ECX_KEY * ecx)149 static unsigned char *ecx_pubkey(ECX_KEY *ecx)
150 {
151     if (ecx == NULL || !ecx->haspubkey) {
152         ERR_raise(ERR_LIB_PROV, PROV_R_NOT_A_PUBLIC_KEY);
153         return 0;
154     }
155     return ecx->pubkey;
156 }
157 
ecxkem_newctx(void * provctx)158 static void *ecxkem_newctx(void *provctx)
159 {
160     PROV_ECX_CTX *ctx =  OPENSSL_zalloc(sizeof(PROV_ECX_CTX));
161 
162     if (ctx == NULL)
163         return NULL;
164     ctx->libctx = PROV_LIBCTX_OF(provctx);
165     ctx->mode = KEM_MODE_DHKEM;
166 
167     return ctx;
168 }
169 
ecxkem_freectx(void * vectx)170 static void ecxkem_freectx(void *vectx)
171 {
172     PROV_ECX_CTX *ctx = (PROV_ECX_CTX *)vectx;
173 
174     OPENSSL_clear_free(ctx->ikm, ctx->ikmlen);
175     recipient_key_set(ctx, NULL);
176     sender_authkey_set(ctx, NULL);
177     OPENSSL_free(ctx);
178 }
179 
ecx_match_params(const ECX_KEY * key1,const ECX_KEY * key2)180 static int ecx_match_params(const ECX_KEY *key1, const ECX_KEY *key2)
181 {
182     return (key1->type == key2->type && key1->keylen == key2->keylen);
183 }
184 
ecx_key_check(const ECX_KEY * ecx,int requires_privatekey)185 static int ecx_key_check(const ECX_KEY *ecx, int requires_privatekey)
186 {
187     if (ecx->privkey == NULL)
188         return (requires_privatekey == 0);
189     return 1;
190 }
191 
ecxkem_init(void * vecxctx,int operation,void * vecx,void * vauth,ossl_unused const OSSL_PARAM params[])192 static int ecxkem_init(void *vecxctx, int operation, void *vecx, void *vauth,
193                        ossl_unused const OSSL_PARAM params[])
194 {
195     int rv;
196     PROV_ECX_CTX *ctx = (PROV_ECX_CTX *)vecxctx;
197     ECX_KEY *ecx = vecx;
198     ECX_KEY *auth = vauth;
199 
200     if (!ossl_prov_is_running())
201         return 0;
202 
203     if (!ecx_key_check(ecx, operation == EVP_PKEY_OP_DECAPSULATE))
204         return 0;
205     rv = recipient_key_set(ctx, ecx);
206     if (rv <= 0)
207         return rv;
208 
209     if (auth != NULL) {
210         if (!ecx_match_params(auth, ctx->recipient_key)
211                 || !ecx_key_check(auth, operation == EVP_PKEY_OP_ENCAPSULATE)
212                 || !sender_authkey_set(ctx, auth))
213             return 0;
214     }
215 
216     ctx->op = operation;
217     return ecxkem_set_ctx_params(vecxctx, params);
218 }
219 
ecxkem_encapsulate_init(void * vecxctx,void * vecx,const OSSL_PARAM params[])220 static int ecxkem_encapsulate_init(void *vecxctx, void *vecx,
221                                    const OSSL_PARAM params[])
222 {
223     return ecxkem_init(vecxctx, EVP_PKEY_OP_ENCAPSULATE, vecx, NULL, params);
224 }
225 
ecxkem_decapsulate_init(void * vecxctx,void * vecx,const OSSL_PARAM params[])226 static int ecxkem_decapsulate_init(void *vecxctx, void *vecx,
227                                    const OSSL_PARAM params[])
228 {
229     return ecxkem_init(vecxctx, EVP_PKEY_OP_DECAPSULATE, vecx, NULL, params);
230 }
231 
ecxkem_auth_encapsulate_init(void * vctx,void * vecx,void * vauthpriv,const OSSL_PARAM params[])232 static int ecxkem_auth_encapsulate_init(void *vctx, void *vecx, void *vauthpriv,
233                                         const OSSL_PARAM params[])
234 {
235     return ecxkem_init(vctx, EVP_PKEY_OP_ENCAPSULATE, vecx, vauthpriv, params);
236 }
237 
ecxkem_auth_decapsulate_init(void * vctx,void * vecx,void * vauthpub,const OSSL_PARAM params[])238 static int ecxkem_auth_decapsulate_init(void *vctx, void *vecx, void *vauthpub,
239                                         const OSSL_PARAM params[])
240 {
241     return ecxkem_init(vctx, EVP_PKEY_OP_DECAPSULATE, vecx, vauthpub, params);
242 }
243 
ecxkem_set_ctx_params(void * vctx,const OSSL_PARAM params[])244 static int ecxkem_set_ctx_params(void *vctx, const OSSL_PARAM params[])
245 {
246     PROV_ECX_CTX *ctx = (PROV_ECX_CTX *)vctx;
247     const OSSL_PARAM *p;
248     int mode;
249 
250     if (ctx == NULL)
251         return 0;
252     if (ossl_param_is_empty(params))
253         return 1;
254 
255     p = OSSL_PARAM_locate_const(params, OSSL_KEM_PARAM_IKME);
256     if (p != NULL) {
257         void *tmp = NULL;
258         size_t tmplen = 0;
259 
260         if (p->data != NULL && p->data_size != 0) {
261             if (!OSSL_PARAM_get_octet_string(p, &tmp, 0, &tmplen))
262                 return 0;
263         }
264         OPENSSL_clear_free(ctx->ikm, ctx->ikmlen);
265         ctx->ikm = tmp;
266         ctx->ikmlen = tmplen;
267     }
268     p = OSSL_PARAM_locate_const(params, OSSL_KEM_PARAM_OPERATION);
269     if (p != NULL) {
270         if (p->data_type != OSSL_PARAM_UTF8_STRING)
271             return 0;
272         mode = ossl_eckem_modename2id(p->data);
273         if (mode == KEM_MODE_UNDEFINED)
274             return 0;
275         ctx->mode = mode;
276     }
277     return 1;
278 }
279 
280 static const OSSL_PARAM known_settable_ecxkem_ctx_params[] = {
281     OSSL_PARAM_utf8_string(OSSL_KEM_PARAM_OPERATION, NULL, 0),
282     OSSL_PARAM_octet_string(OSSL_KEM_PARAM_IKME, NULL, 0),
283     OSSL_PARAM_END
284 };
285 
ecxkem_settable_ctx_params(ossl_unused void * vctx,ossl_unused void * provctx)286 static const OSSL_PARAM *ecxkem_settable_ctx_params(ossl_unused void *vctx,
287                                                    ossl_unused void *provctx)
288 {
289     return known_settable_ecxkem_ctx_params;
290 }
291 
292 /*
293  * See Section 4.1 DH-Based KEM (DHKEM) ExtractAndExpand
294  */
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)295 static int dhkem_extract_and_expand(EVP_KDF_CTX *kctx,
296                                     unsigned char *okm, size_t okmlen,
297                                     uint16_t kemid,
298                                     const unsigned char *dhkm, size_t dhkmlen,
299                                     const unsigned char *kemctx,
300                                     size_t kemctxlen)
301 {
302     uint8_t suiteid[2];
303     uint8_t prk[EVP_MAX_MD_SIZE];
304     size_t prklen = okmlen; /* Nh */
305     int ret;
306 
307     if (prklen > sizeof(prk))
308         return 0;
309 
310     suiteid[0] = (kemid >> 8) &0xff;
311     suiteid[1] = kemid & 0xff;
312 
313     ret = ossl_hpke_labeled_extract(kctx, prk, prklen,
314                                     NULL, 0, LABEL_KEM, suiteid, sizeof(suiteid),
315                                     OSSL_DHKEM_LABEL_EAE_PRK, dhkm, dhkmlen)
316           && ossl_hpke_labeled_expand(kctx, okm, okmlen, prk, prklen,
317                                       LABEL_KEM, suiteid, sizeof(suiteid),
318                                       OSSL_DHKEM_LABEL_SHARED_SECRET,
319                                       kemctx, kemctxlen);
320     OPENSSL_cleanse(prk, prklen);
321     return ret;
322 }
323 
324 /*
325  * See Section 7.1.3 DeriveKeyPair.
326  *
327  * This function is used by ecx keygen.
328  * (For this reason it does not use any of the state stored in PROV_ECX_CTX).
329  *
330  * Params:
331  *     ecx An initialized ecx key.
332  *     privout The buffer to store the generated private key into (it is assumed
333  *             this is of length ecx->keylen).
334  *     ikm buffer containing the input key material (seed). This must be non NULL.
335  *     ikmlen size of the ikm buffer in bytes
336  * Returns:
337  *     1 if successful or 0 otherwise.
338  */
ossl_ecx_dhkem_derive_private(ECX_KEY * ecx,unsigned char * privout,const unsigned char * ikm,size_t ikmlen)339 int ossl_ecx_dhkem_derive_private(ECX_KEY *ecx, unsigned char *privout,
340                                   const unsigned char *ikm, size_t ikmlen)
341 {
342     int ret = 0;
343     EVP_KDF_CTX *kdfctx = NULL;
344     unsigned char prk[EVP_MAX_MD_SIZE];
345     uint8_t suiteid[2];
346     const OSSL_HPKE_KEM_INFO *info = get_kem_info(ecx);
347 
348     /* ikmlen should have a length of at least Nsk */
349     if (ikmlen < info->Nsk) {
350         ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_INPUT_LENGTH,
351                        "ikm length is :%zu, should be at least %zu",
352                        ikmlen, info->Nsk);
353         goto err;
354     }
355 
356     kdfctx = ossl_kdf_ctx_create("HKDF", info->mdname, ecx->libctx, ecx->propq);
357     if (kdfctx == NULL)
358         return 0;
359 
360     suiteid[0] = info->kem_id / 256;
361     suiteid[1] = info->kem_id % 256;
362 
363     if (!ossl_hpke_labeled_extract(kdfctx, prk, info->Nsecret,
364                                    NULL, 0, LABEL_KEM, suiteid, sizeof(suiteid),
365                                    OSSL_DHKEM_LABEL_DKP_PRK, ikm, ikmlen))
366         goto err;
367 
368     if (!ossl_hpke_labeled_expand(kdfctx, privout, info->Nsk, prk, info->Nsecret,
369                                   LABEL_KEM, suiteid, sizeof(suiteid),
370                                   OSSL_DHKEM_LABEL_SK, NULL, 0))
371         goto err;
372     ret = 1;
373 err:
374     OPENSSL_cleanse(prk, sizeof(prk));
375     EVP_KDF_CTX_free(kdfctx);
376     return ret;
377 }
378 
379 /*
380  * Do a keygen operation without having to use EVP_PKEY.
381  * Params:
382  *     ctx Context object
383  *     ikm The seed material - if this is NULL, then a random seed is used.
384  * Returns:
385  *     The generated ECX key, or NULL on failure.
386  */
derivekey(PROV_ECX_CTX * ctx,const unsigned char * ikm,size_t ikmlen)387 static ECX_KEY *derivekey(PROV_ECX_CTX *ctx,
388                           const unsigned char *ikm, size_t ikmlen)
389 {
390     int ok = 0;
391     ECX_KEY *key;
392     unsigned char *privkey;
393     unsigned char *seed = (unsigned char *)ikm;
394     size_t seedlen = ikmlen;
395     unsigned char tmpbuf[OSSL_HPKE_MAX_PRIVATE];
396     const OSSL_HPKE_KEM_INFO *info = ctx->info;
397 
398     key = ossl_ecx_key_new(ctx->libctx, ctx->recipient_key->type, 0, ctx->propq);
399     if (key == NULL)
400         return NULL;
401     privkey = ossl_ecx_key_allocate_privkey(key);
402     if (privkey == NULL)
403         goto err;
404 
405     /* Generate a random seed if there is no input ikm */
406     if (seed == NULL || seedlen == 0) {
407         if (info->Nsk > sizeof(tmpbuf))
408             goto err;
409         if (RAND_priv_bytes_ex(ctx->libctx, tmpbuf, info->Nsk, 0) <= 0)
410             goto err;
411         seed = tmpbuf;
412         seedlen = info->Nsk;
413     }
414     if (!ossl_ecx_dhkem_derive_private(key, privkey, seed, seedlen))
415         goto err;
416     if (!ossl_ecx_public_from_private(key))
417         goto err;
418     key->haspubkey = 1;
419     ok = 1;
420 err:
421     if (!ok) {
422         ossl_ecx_key_free(key);
423         key = NULL;
424     }
425     if (seed != ikm)
426         OPENSSL_cleanse(seed, seedlen);
427     return key;
428 }
429 
430 /*
431  * Do an ecxdh key exchange.
432  * dhkm = DH(sender, peer)
433  *
434  * NOTE: Instead of using EVP_PKEY_derive() API's, we use ECX_KEY operations
435  *       to avoid messy conversions back to EVP_PKEY.
436  *
437  * Returns the size of the secret if successful, or 0 otherwise,
438  */
generate_ecxdhkm(const ECX_KEY * sender,const ECX_KEY * peer,unsigned char * out,size_t maxout,unsigned int secretsz)439 static int generate_ecxdhkm(const ECX_KEY *sender, const ECX_KEY *peer,
440                            unsigned char *out,  size_t maxout,
441                            unsigned int secretsz)
442 {
443     size_t len = 0;
444 
445     /* NOTE: ossl_ecx_compute_key checks for shared secret being all zeros */
446     return ossl_ecx_compute_key((ECX_KEY *)peer, (ECX_KEY *)sender,
447                                  sender->keylen, out, &len, maxout);
448 }
449 
450 /*
451  * Derive a secret using ECXDH (code is shared by the encap and decap)
452  *
453  * dhkm = Concat(ecxdh(privkey1, peerkey1), ecdh(privkey2, peerkey2)
454  * kemctx = Concat(sender_pub, recipient_pub, ctx->sender_authkey)
455  * secret = dhkem_extract_and_expand(kemid, dhkm, kemctx);
456  *
457  * Params:
458  *     ctx Object that contains algorithm state and constants.
459  *     secret The returned secret (with a length ctx->alg->secretlen bytes).
460  *     privkey1 A private key used for ECXDH key derivation.
461  *     peerkey1 A public key used for ECXDH key derivation with privkey1
462  *     privkey2 A optional private key used for a second ECXDH key derivation.
463  *              It can be NULL.
464  *     peerkey2 A optional public key used for a second ECXDH key derivation
465  *              with privkey2,. It can be NULL.
466  *     sender_pub The senders public key in encoded form.
467  *     recipient_pub The recipients public key in encoded form.
468  * Notes:
469  *     The second ecdh() is only used for the HPKE auth modes when both privkey2
470  *     and peerkey2 are non NULL (i.e. ctx->sender_authkey is not NULL).
471  */
derive_secret(PROV_ECX_CTX * ctx,unsigned char * secret,const ECX_KEY * privkey1,const ECX_KEY * peerkey1,const ECX_KEY * privkey2,const ECX_KEY * peerkey2,const unsigned char * sender_pub,const unsigned char * recipient_pub)472 static int derive_secret(PROV_ECX_CTX *ctx, unsigned char *secret,
473                          const ECX_KEY *privkey1, const ECX_KEY *peerkey1,
474                          const ECX_KEY *privkey2, const ECX_KEY *peerkey2,
475                          const unsigned char *sender_pub,
476                          const unsigned char *recipient_pub)
477 {
478     int ret = 0;
479     EVP_KDF_CTX *kdfctx = NULL;
480     unsigned char *sender_authpub = NULL;
481     unsigned char dhkm[MAX_ECX_KEYLEN * 2];
482     unsigned char kemctx[MAX_ECX_KEYLEN * 3];
483     size_t kemctxlen = 0, dhkmlen = 0;
484     const OSSL_HPKE_KEM_INFO *info = ctx->info;
485     int auth = ctx->sender_authkey != NULL;
486     size_t encodedkeylen = info->Npk;
487 
488     if (!generate_ecxdhkm(privkey1, peerkey1, dhkm, sizeof(dhkm), encodedkeylen))
489         goto err;
490     dhkmlen = encodedkeylen;
491 
492     /* Concat the optional second ECXDH (used for Auth) */
493     if (auth) {
494         if (!generate_ecxdhkm(privkey2, peerkey2,
495                               dhkm + dhkmlen, sizeof(dhkm) - dhkmlen,
496                               encodedkeylen))
497             goto err;
498         /* Get the public key of the auth sender in encoded form */
499         sender_authpub = ecx_pubkey(ctx->sender_authkey);
500         if (sender_authpub == NULL)
501             goto err;
502         dhkmlen += encodedkeylen;
503     }
504     kemctxlen = encodedkeylen + dhkmlen;
505     if (kemctxlen > sizeof(kemctx))
506         goto err;
507 
508     /* kemctx is the concat of both sides encoded public key */
509     memcpy(kemctx, sender_pub, encodedkeylen);
510     memcpy(kemctx + encodedkeylen, recipient_pub, encodedkeylen);
511     if (auth)
512         memcpy(kemctx + 2 * encodedkeylen, sender_authpub, encodedkeylen);
513     kdfctx = ossl_kdf_ctx_create(ctx->kdfname, info->mdname,
514                                  ctx->libctx, ctx->propq);
515     if (kdfctx == NULL)
516         goto err;
517     if (!dhkem_extract_and_expand(kdfctx, secret, info->Nsecret,
518                                   info->kem_id, dhkm, dhkmlen,
519                                   kemctx, kemctxlen))
520         goto err;
521     ret = 1;
522 err:
523     OPENSSL_cleanse(dhkm, dhkmlen);
524     EVP_KDF_CTX_free(kdfctx);
525     return ret;
526 }
527 
528 /*
529  * Do a DHKEM encapsulate operation.
530  *
531  * See Section 4.1 Encap() and AuthEncap()
532  *
533  * Params:
534  *     ctx A context object holding the recipients public key and the
535  *         optional senders auth private key.
536  *     enc A buffer to return the senders ephemeral public key.
537  *         Setting this to NULL allows the enclen and secretlen to return
538  *         values, without calculating the secret.
539  *     enclen Passes in the max size of the enc buffer and returns the
540  *            encoded public key length.
541  *     secret A buffer to return the calculated shared secret.
542  *     secretlen Passes in the max size of the secret buffer and returns the
543  *               secret length.
544  * Returns: 1 on success or 0 otherwise.
545  */
dhkem_encap(PROV_ECX_CTX * ctx,unsigned char * enc,size_t * enclen,unsigned char * secret,size_t * secretlen)546 static int dhkem_encap(PROV_ECX_CTX *ctx,
547                        unsigned char *enc, size_t *enclen,
548                        unsigned char *secret, size_t *secretlen)
549 {
550     int ret = 0;
551     ECX_KEY *sender_ephemkey = NULL;
552     unsigned char *sender_ephempub, *recipient_pub;
553     const OSSL_HPKE_KEM_INFO *info = ctx->info;
554 
555     if (enc == NULL) {
556         if (enclen == NULL && secretlen == NULL)
557             return 0;
558         if (enclen != NULL)
559             *enclen = info->Nenc;
560         if (secretlen != NULL)
561             *secretlen = info->Nsecret;
562        return 1;
563     }
564 
565     if (*secretlen < info->Nsecret) {
566         ERR_raise_data(ERR_LIB_PROV, PROV_R_BAD_LENGTH, "*secretlen too small");
567         return 0;
568     }
569     if (*enclen < info->Nenc) {
570         ERR_raise_data(ERR_LIB_PROV, PROV_R_BAD_LENGTH, "*enclen too small");
571         return 0;
572     }
573 
574     /* Create an ephemeral key */
575     sender_ephemkey = derivekey(ctx, ctx->ikm, ctx->ikmlen);
576 
577     sender_ephempub = ecx_pubkey(sender_ephemkey);
578     recipient_pub = ecx_pubkey(ctx->recipient_key);
579     if (sender_ephempub == NULL || recipient_pub == NULL)
580         goto err;
581 
582     if (!derive_secret(ctx, secret,
583                        sender_ephemkey, ctx->recipient_key,
584                        ctx->sender_authkey, ctx->recipient_key,
585                        sender_ephempub, recipient_pub))
586         goto err;
587 
588     /* Return the public part of the ephemeral key */
589     memcpy(enc, sender_ephempub, info->Nenc);
590     *enclen = info->Nenc;
591     *secretlen = info->Nsecret;
592     ret = 1;
593 err:
594     ossl_ecx_key_free(sender_ephemkey);
595     return ret;
596 }
597 
598 /*
599  * Do a DHKEM decapsulate operation.
600  * See Section 4.1 Decap() and Auth Decap()
601  *
602  * Params:
603  *     ctx A context object holding the recipients private key and the
604  *         optional senders auth public key.
605  *     secret A buffer to return the calculated shared secret. Setting this to
606  *            NULL can be used to return the secretlen.
607  *     secretlen Passes in the max size of the secret buffer and returns the
608  *               secret length.
609  *     enc A buffer containing the senders ephemeral public key that was returned
610  *         from dhkem_encap().
611  *     enclen The length in bytes of enc.
612  * Returns: 1 If the shared secret is returned or 0 on error.
613  */
dhkem_decap(PROV_ECX_CTX * ctx,unsigned char * secret,size_t * secretlen,const unsigned char * enc,size_t enclen)614 static int dhkem_decap(PROV_ECX_CTX *ctx,
615                        unsigned char *secret, size_t *secretlen,
616                        const unsigned char *enc, size_t enclen)
617 {
618     int ret = 0;
619     ECX_KEY *recipient_privkey = ctx->recipient_key;
620     ECX_KEY *sender_ephempubkey = NULL;
621     const OSSL_HPKE_KEM_INFO *info = ctx->info;
622     unsigned char *recipient_pub;
623 
624     if (secret == NULL) {
625         *secretlen = info->Nsecret;
626         return 1;
627     }
628     if (*secretlen < info->Nsecret) {
629         ERR_raise_data(ERR_LIB_PROV, PROV_R_BAD_LENGTH, "*secretlen too small");
630         return 0;
631     }
632     if (enclen != info->Nenc) {
633         ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_KEY, "Invalid enc public key");
634         return 0;
635     }
636 
637     /* Get the public part of the ephemeral key created by encap */
638     sender_ephempubkey = ecxkey_pubfromdata(ctx, enc, enclen);
639     if (sender_ephempubkey == NULL)
640         goto err;
641 
642     recipient_pub = ecx_pubkey(recipient_privkey);
643     if (recipient_pub == NULL)
644         goto err;
645 
646     if (!derive_secret(ctx, secret,
647                        ctx->recipient_key, sender_ephempubkey,
648                        ctx->recipient_key, ctx->sender_authkey,
649                        enc, recipient_pub))
650         goto err;
651 
652     *secretlen = info->Nsecret;
653     ret = 1;
654 err:
655     ossl_ecx_key_free(sender_ephempubkey);
656     return ret;
657 }
658 
ecxkem_encapsulate(void * vctx,unsigned char * out,size_t * outlen,unsigned char * secret,size_t * secretlen)659 static int ecxkem_encapsulate(void *vctx, unsigned char *out, size_t *outlen,
660                               unsigned char *secret, size_t *secretlen)
661 {
662     PROV_ECX_CTX *ctx = (PROV_ECX_CTX *)vctx;
663 
664     switch (ctx->mode) {
665         case KEM_MODE_DHKEM:
666             return dhkem_encap(ctx, out, outlen, secret, secretlen);
667         default:
668             ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_MODE);
669             return -2;
670     }
671 }
672 
ecxkem_decapsulate(void * vctx,unsigned char * out,size_t * outlen,const unsigned char * in,size_t inlen)673 static int ecxkem_decapsulate(void *vctx, unsigned char *out, size_t *outlen,
674                               const unsigned char *in, size_t inlen)
675 {
676     PROV_ECX_CTX *ctx = (PROV_ECX_CTX *)vctx;
677 
678     switch (ctx->mode) {
679         case KEM_MODE_DHKEM:
680             return dhkem_decap(vctx, out, outlen, in, inlen);
681         default:
682             ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_MODE);
683             return -2;
684     }
685 }
686 
687 const OSSL_DISPATCH ossl_ecx_asym_kem_functions[] = {
688     { OSSL_FUNC_KEM_NEWCTX, (void (*)(void))ecxkem_newctx },
689     { OSSL_FUNC_KEM_ENCAPSULATE_INIT,
690       (void (*)(void))ecxkem_encapsulate_init },
691     { OSSL_FUNC_KEM_ENCAPSULATE, (void (*)(void))ecxkem_encapsulate },
692     { OSSL_FUNC_KEM_DECAPSULATE_INIT,
693       (void (*)(void))ecxkem_decapsulate_init },
694     { OSSL_FUNC_KEM_DECAPSULATE, (void (*)(void))ecxkem_decapsulate },
695     { OSSL_FUNC_KEM_FREECTX, (void (*)(void))ecxkem_freectx },
696     { OSSL_FUNC_KEM_SET_CTX_PARAMS,
697       (void (*)(void))ecxkem_set_ctx_params },
698     { OSSL_FUNC_KEM_SETTABLE_CTX_PARAMS,
699       (void (*)(void))ecxkem_settable_ctx_params },
700     { OSSL_FUNC_KEM_AUTH_ENCAPSULATE_INIT,
701       (void (*)(void))ecxkem_auth_encapsulate_init },
702     { OSSL_FUNC_KEM_AUTH_DECAPSULATE_INIT,
703       (void (*)(void))ecxkem_auth_decapsulate_init },
704     OSSL_DISPATCH_END
705 };
706