Coverage Report

Created: 2025-12-31 06:58

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