Coverage Report

Created: 2025-06-13 06:58

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