Coverage Report

Created: 2025-06-13 06:58

/src/openssl32/providers/implementations/kem/ec_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
 * 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
66
static int eckey_check(const EC_KEY *ec, int requires_privatekey)
67
0
{
68
0
    int rv = 0;
69
0
    BN_CTX *bnctx = NULL;
70
0
    BIGNUM *rem = NULL;
71
0
    const BIGNUM *priv = EC_KEY_get0_private_key(ec);
72
0
    const EC_POINT *pub = EC_KEY_get0_public_key(ec);
73
74
    /* Keys always require a public component */
75
0
    if (pub == NULL) {
76
0
        ERR_raise(ERR_LIB_PROV, PROV_R_NOT_A_PUBLIC_KEY);
77
0
        return 0;
78
0
    }
79
0
    if (priv == NULL) {
80
0
        return (requires_privatekey == 0);
81
0
    } else {
82
        /* If there is a private key, check that is non zero (mod order) */
83
0
        const EC_GROUP *group = EC_KEY_get0_group(ec);
84
0
        const BIGNUM *order = EC_GROUP_get0_order(group);
85
86
0
        bnctx = BN_CTX_new_ex(ossl_ec_key_get_libctx(ec));
87
0
        rem = BN_new();
88
89
0
        if (order != NULL && rem != NULL && bnctx != NULL) {
90
0
             rv = BN_mod(rem, priv, order, bnctx)
91
0
                  && !BN_is_zero(rem);
92
0
        }
93
0
    }
94
0
    BN_free(rem);
95
0
    BN_CTX_free(bnctx);
96
0
    return rv;
97
0
}
98
99
/* Returns NULL if the curve is not supported */
100
static const char *ec_curvename_get0(const EC_KEY *ec)
101
0
{
102
0
    const EC_GROUP *group = EC_KEY_get0_group(ec);
103
104
0
    return EC_curve_nid2nist(EC_GROUP_get_curve_name(group));
105
0
}
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
 */
113
static int recipient_key_set(PROV_EC_CTX *ctx, EC_KEY *ec)
114
0
{
115
0
    EC_KEY_free(ctx->recipient_key);
116
0
    ctx->recipient_key = NULL;
117
118
0
    if (ec != NULL) {
119
0
        const char *curve = ec_curvename_get0(ec);
120
121
0
        if (curve == NULL)
122
0
            return -2;
123
0
        ctx->info = ossl_HPKE_KEM_INFO_find_curve(curve);
124
0
        if (ctx->info == NULL)
125
0
            return -2;
126
0
        if (!EC_KEY_up_ref(ec))
127
0
            return 0;
128
0
        ctx->recipient_key = ec;
129
0
        ctx->kdfname = "HKDF";
130
0
    }
131
0
    return 1;
132
0
}
133
134
/*
135
 * Set the senders auth key, and free any existing auth key.
136
 * ec can be NULL.
137
 */
138
static int sender_authkey_set(PROV_EC_CTX *ctx, EC_KEY *ec)
139
0
{
140
0
    EC_KEY_free(ctx->sender_authkey);
141
0
    ctx->sender_authkey = NULL;
142
143
0
    if (ec != NULL) {
144
0
        if (!EC_KEY_up_ref(ec))
145
0
            return 0;
146
0
        ctx->sender_authkey = ec;
147
0
    }
148
0
    return 1;
149
0
}
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
 */
158
static EC_KEY *eckey_frompub(EC_KEY *in,
159
                             const unsigned char *pubbuf, size_t pubbuflen)
160
0
{
161
0
    EC_KEY *key;
162
163
0
    key = EC_KEY_new_ex(ossl_ec_key_get_libctx(in), ossl_ec_key_get0_propq(in));
164
0
    if (key == NULL)
165
0
        goto err;
166
0
    if (!EC_KEY_set_group(key, EC_KEY_get0_group(in)))
167
0
        goto err;
168
0
    if (!EC_KEY_oct2key(key, pubbuf, pubbuflen, NULL))
169
0
        goto err;
170
0
    return key;
171
0
err:
172
0
    EC_KEY_free(key);
173
0
    return NULL;
174
0
}
175
176
/*
177
 * Deserialises a EC public key into a encoded byte array.
178
 * Returns: 1 if successful or 0 otherwise.
179
 */
180
static int ecpubkey_todata(const EC_KEY *ec, unsigned char *out, size_t *outlen,
181
                           size_t maxoutlen)
182
0
{
183
0
    const EC_POINT *pub;
184
0
    const EC_GROUP *group;
185
186
0
    group = EC_KEY_get0_group(ec);
187
0
    pub = EC_KEY_get0_public_key(ec);
188
0
    *outlen = EC_POINT_point2oct(group, pub, POINT_CONVERSION_UNCOMPRESSED,
189
0
                                 out, maxoutlen, NULL);
190
0
    return *outlen != 0;
191
0
}
192
193
static void *eckem_newctx(void *provctx)
194
0
{
195
0
    PROV_EC_CTX *ctx =  OPENSSL_zalloc(sizeof(PROV_EC_CTX));
196
197
0
    if (ctx == NULL)
198
0
        return NULL;
199
0
    ctx->libctx = PROV_LIBCTX_OF(provctx);
200
201
0
    return ctx;
202
0
}
203
204
static void eckem_freectx(void *vectx)
205
0
{
206
0
    PROV_EC_CTX *ctx = (PROV_EC_CTX *)vectx;
207
208
0
    OPENSSL_clear_free(ctx->ikm, ctx->ikmlen);
209
0
    recipient_key_set(ctx, NULL);
210
0
    sender_authkey_set(ctx, NULL);
211
0
    OPENSSL_free(ctx);
212
0
}
213
214
static int ossl_ec_match_params(const EC_KEY *key1, const EC_KEY *key2)
215
0
{
216
0
    int ret;
217
0
    BN_CTX *ctx = NULL;
218
0
    const EC_GROUP *group1 = EC_KEY_get0_group(key1);
219
0
    const EC_GROUP *group2 = EC_KEY_get0_group(key2);
220
221
0
    ctx = BN_CTX_new_ex(ossl_ec_key_get_libctx(key1));
222
0
    if (ctx == NULL)
223
0
        return 0;
224
225
0
    ret = group1 != NULL
226
0
          && group2 != NULL
227
0
          && EC_GROUP_cmp(group1, group2, ctx) == 0;
228
0
    if (!ret)
229
0
        ERR_raise(ERR_LIB_PROV, PROV_R_MISMATCHING_DOMAIN_PARAMETERS);
230
0
    BN_CTX_free(ctx);
231
0
    return ret;
232
0
}
233
234
static int eckem_init(void *vctx, int operation, void *vec, void *vauth,
235
                      const OSSL_PARAM params[])
236
0
{
237
0
    int rv;
238
0
    PROV_EC_CTX *ctx = (PROV_EC_CTX *)vctx;
239
0
    EC_KEY *ec = vec;
240
0
    EC_KEY *auth = vauth;
241
242
0
    if (!ossl_prov_is_running())
243
0
        return 0;
244
245
0
    if (!eckey_check(ec, operation == EVP_PKEY_OP_DECAPSULATE))
246
0
        return 0;
247
0
    rv = recipient_key_set(ctx, ec);
248
0
    if (rv <= 0)
249
0
        return rv;
250
251
0
    if (auth != NULL) {
252
0
        if (!ossl_ec_match_params(ec, auth)
253
0
            || !eckey_check(auth, operation == EVP_PKEY_OP_ENCAPSULATE)
254
0
            || !sender_authkey_set(ctx, auth))
255
0
        return 0;
256
0
    }
257
258
0
    ctx->op = operation;
259
0
    return eckem_set_ctx_params(vctx, params);
260
0
}
261
262
static int eckem_encapsulate_init(void *vctx, void *vec,
263
                                   const OSSL_PARAM params[])
264
0
{
265
0
    return eckem_init(vctx, EVP_PKEY_OP_ENCAPSULATE, vec, NULL, params);
266
0
}
267
268
static int eckem_decapsulate_init(void *vctx, void *vec,
269
                                   const OSSL_PARAM params[])
270
0
{
271
0
    return eckem_init(vctx, EVP_PKEY_OP_DECAPSULATE, vec, NULL, params);
272
0
}
273
274
static int eckem_auth_encapsulate_init(void *vctx, void *vecx, void *vauthpriv,
275
                                       const OSSL_PARAM params[])
276
0
{
277
0
    return eckem_init(vctx, EVP_PKEY_OP_ENCAPSULATE, vecx, vauthpriv, params);
278
0
}
279
280
static int eckem_auth_decapsulate_init(void *vctx, void *vecx, void *vauthpub,
281
                                       const OSSL_PARAM params[])
282
0
{
283
0
    return eckem_init(vctx, EVP_PKEY_OP_DECAPSULATE, vecx, vauthpub, params);
284
0
}
285
286
static int eckem_set_ctx_params(void *vctx, const OSSL_PARAM params[])
287
0
{
288
0
    PROV_EC_CTX *ctx = (PROV_EC_CTX *)vctx;
289
0
    const OSSL_PARAM *p;
290
0
    int mode;
291
292
0
    if (params == NULL)
293
0
        return 1;
294
295
0
    p = OSSL_PARAM_locate_const(params, OSSL_KEM_PARAM_IKME);
296
0
    if (p != NULL) {
297
0
        void *tmp = NULL;
298
0
        size_t tmplen = 0;
299
300
0
        if (p->data != NULL && p->data_size != 0) {
301
0
            if (!OSSL_PARAM_get_octet_string(p, &tmp, 0, &tmplen))
302
0
                return 0;
303
0
        }
304
0
        OPENSSL_clear_free(ctx->ikm, ctx->ikmlen);
305
        /* Set the ephemeral seed */
306
0
        ctx->ikm = tmp;
307
0
        ctx->ikmlen = tmplen;
308
0
    }
309
310
0
    p = OSSL_PARAM_locate_const(params, OSSL_KEM_PARAM_OPERATION);
311
0
    if (p != NULL) {
312
0
        if (p->data_type != OSSL_PARAM_UTF8_STRING)
313
0
            return 0;
314
0
        mode = ossl_eckem_modename2id(p->data);
315
0
        if (mode == KEM_MODE_UNDEFINED)
316
0
            return 0;
317
0
        ctx->mode = mode;
318
0
    }
319
0
    return 1;
320
0
}
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
328
static const OSSL_PARAM *eckem_settable_ctx_params(ossl_unused void *vctx,
329
                                                   ossl_unused void *provctx)
330
2
{
331
2
    return known_settable_eckem_ctx_params;
332
2
}
333
334
/*
335
 * See Section 4.1 DH-Based KEM (DHKEM) ExtractAndExpand
336
 */
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
0
{
344
0
    uint8_t suiteid[2];
345
0
    uint8_t prk[EVP_MAX_MD_SIZE];
346
0
    size_t prklen = okmlen;
347
0
    int ret;
348
349
0
    if (prklen > sizeof(prk))
350
0
        return 0;
351
352
0
    suiteid[0] = (kemid >> 8) & 0xff;
353
0
    suiteid[1] = kemid & 0xff;
354
355
0
    ret = ossl_hpke_labeled_extract(kctx, prk, prklen,
356
0
                                    NULL, 0, LABEL_KEM, suiteid, sizeof(suiteid),
357
0
                                    OSSL_DHKEM_LABEL_EAE_PRK, dhkm, dhkmlen)
358
0
          && ossl_hpke_labeled_expand(kctx, okm, okmlen, prk, prklen,
359
0
                                      LABEL_KEM, suiteid, sizeof(suiteid),
360
0
                                      OSSL_DHKEM_LABEL_SHARED_SECRET,
361
0
                                      kemctx, kemctxlen);
362
0
    OPENSSL_cleanse(prk, prklen);
363
0
    return ret;
364
0
}
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
 */
381
int ossl_ec_dhkem_derive_private(EC_KEY *ec, BIGNUM *priv,
382
                                 const unsigned char *ikm, size_t ikmlen)
383
0
{
384
0
    int ret = 0;
385
0
    EVP_KDF_CTX *kdfctx = NULL;
386
0
    uint8_t suiteid[2];
387
0
    unsigned char prk[OSSL_HPKE_MAX_SECRET];
388
0
    unsigned char privbuf[OSSL_HPKE_MAX_PRIVATE];
389
0
    const BIGNUM *order;
390
0
    unsigned char counter = 0;
391
0
    const char *curve = ec_curvename_get0(ec);
392
0
    const OSSL_HPKE_KEM_INFO *info;
393
394
0
    if (curve == NULL)
395
0
        return -2;
396
397
0
    info = ossl_HPKE_KEM_INFO_find_curve(curve);
398
0
    if (info == NULL)
399
0
        return -2;
400
401
0
    kdfctx = ossl_kdf_ctx_create("HKDF", info->mdname,
402
0
                                 ossl_ec_key_get_libctx(ec),
403
0
                                 ossl_ec_key_get0_propq(ec));
404
0
    if (kdfctx == NULL)
405
0
        return 0;
406
407
    /* ikmlen should have a length of at least Nsk */
408
0
    if (ikmlen < info->Nsk) {
409
0
        ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_INPUT_LENGTH,
410
0
                       "ikm length is :%zu, should be at least %zu",
411
0
                       ikmlen, info->Nsk);
412
0
        goto err;
413
0
    }
414
415
0
    suiteid[0] = info->kem_id / 256;
416
0
    suiteid[1] = info->kem_id % 256;
417
418
0
    if (!ossl_hpke_labeled_extract(kdfctx, prk, info->Nsecret,
419
0
                                   NULL, 0, LABEL_KEM, suiteid, sizeof(suiteid),
420
0
                                   OSSL_DHKEM_LABEL_DKP_PRK, ikm, ikmlen))
421
0
        goto err;
422
423
0
    order = EC_GROUP_get0_order(EC_KEY_get0_group(ec));
424
0
    do {
425
0
        if (!ossl_hpke_labeled_expand(kdfctx, privbuf, info->Nsk,
426
0
                                      prk, info->Nsecret,
427
0
                                      LABEL_KEM, suiteid, sizeof(suiteid),
428
0
                                      OSSL_DHKEM_LABEL_CANDIDATE,
429
0
                                      &counter, 1))
430
0
            goto err;
431
0
        privbuf[0] &= info->bitmask;
432
0
        if (BN_bin2bn(privbuf, info->Nsk, priv) == NULL)
433
0
            goto err;
434
0
        if (counter == 0xFF) {
435
0
            ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GENERATE_KEY);
436
0
            goto err;
437
0
        }
438
0
        counter++;
439
0
    } while (BN_is_zero(priv) || BN_cmp(priv, order) >= 0);
440
0
    ret = 1;
441
0
err:
442
0
    OPENSSL_cleanse(prk, sizeof(prk));
443
0
    OPENSSL_cleanse(privbuf, sizeof(privbuf));
444
0
    EVP_KDF_CTX_free(kdfctx);
445
0
    return ret;
446
0
}
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
 */
456
static EC_KEY *derivekey(PROV_EC_CTX *ctx,
457
                         const unsigned char *ikm, size_t ikmlen)
458
0
{
459
0
    int ret = 0;
460
0
    EC_KEY *key;
461
0
    unsigned char *seed = (unsigned char *)ikm;
462
0
    size_t seedlen = ikmlen;
463
0
    unsigned char tmpbuf[OSSL_HPKE_MAX_PRIVATE];
464
465
0
    key = EC_KEY_new_ex(ctx->libctx, ctx->propq);
466
0
    if (key == NULL)
467
0
        goto err;
468
0
    if (!EC_KEY_set_group(key, EC_KEY_get0_group(ctx->recipient_key)))
469
0
        goto err;
470
471
    /* Generate a random seed if there is no input ikm */
472
0
    if (seed == NULL || seedlen == 0) {
473
0
        seedlen = ctx->info->Nsk;
474
0
        if (seedlen > sizeof(tmpbuf))
475
0
            goto err;
476
0
        if (RAND_priv_bytes_ex(ctx->libctx, tmpbuf, seedlen, 0) <= 0)
477
0
            goto err;
478
0
        seed = tmpbuf;
479
0
    }
480
0
    ret = ossl_ec_generate_key_dhkem(key, seed, seedlen);
481
0
err:
482
0
    if (seed != ikm)
483
0
        OPENSSL_cleanse(seed, seedlen);
484
0
    if (ret <= 0) {
485
0
        EC_KEY_free(key);
486
0
        key = NULL;
487
0
    }
488
0
    return key;
489
0
}
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
 */
498
static int check_publickey(const EC_KEY *pub)
499
0
{
500
0
    int ret = 0;
501
0
    BN_CTX *bnctx = BN_CTX_new_ex(ossl_ec_key_get_libctx(pub));
502
503
0
    if (bnctx == NULL)
504
0
        return 0;
505
0
    ret = ossl_ec_key_public_check(pub, bnctx);
506
0
    BN_CTX_free(bnctx);
507
508
0
    return ret;
509
0
}
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
 */
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
0
{
524
0
    const EC_GROUP *group = EC_KEY_get0_group(sender);
525
0
    size_t secretlen = (EC_GROUP_get_degree(group) + 7) / 8;
526
527
0
    if (secretlen != secretsz || secretlen > maxout) {
528
0
        ERR_raise_data(ERR_LIB_PROV,  PROV_R_BAD_LENGTH, "secretsz invalid");
529
0
        return 0;
530
0
    }
531
532
0
    if (!check_publickey(peer))
533
0
        return 0;
534
0
    return ECDH_compute_key(out, secretlen, EC_KEY_get0_public_key(peer),
535
0
                            sender, NULL) > 0;
536
0
}
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
 */
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
0
{
566
0
    int ret = 0;
567
0
    EVP_KDF_CTX *kdfctx = NULL;
568
0
    unsigned char sender_authpub[OSSL_HPKE_MAX_PUBLIC];
569
0
    unsigned char dhkm[OSSL_HPKE_MAX_PRIVATE * 2];
570
0
    unsigned char kemctx[OSSL_HPKE_MAX_PUBLIC * 3];
571
0
    size_t sender_authpublen;
572
0
    size_t kemctxlen = 0, dhkmlen = 0;
573
0
    const OSSL_HPKE_KEM_INFO *info = ctx->info;
574
0
    size_t encodedpublen = info->Npk;
575
0
    size_t encodedprivlen = info->Nsk;
576
0
    int auth = ctx->sender_authkey != NULL;
577
578
0
    if (!generate_ecdhkm(privkey1, peerkey1, dhkm, sizeof(dhkm), encodedprivlen))
579
0
        goto err;
580
0
    dhkmlen = encodedprivlen;
581
0
    kemctxlen = 2 * encodedpublen;
582
583
    /* Concat the optional second ECDH (used for Auth) */
584
0
    if (auth) {
585
        /* Get the public key of the auth sender in encoded form */
586
0
        if (!ecpubkey_todata(ctx->sender_authkey, sender_authpub,
587
0
                             &sender_authpublen, sizeof(sender_authpub)))
588
0
            goto err;
589
0
        if (sender_authpublen != encodedpublen) {
590
0
            ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_KEY,
591
0
                           "Invalid sender auth public key");
592
0
            goto err;
593
0
        }
594
0
        if (!generate_ecdhkm(privkey2, peerkey2,
595
0
                             dhkm + dhkmlen, sizeof(dhkm) - dhkmlen,
596
0
                             encodedprivlen))
597
0
            goto err;
598
0
        dhkmlen += encodedprivlen;
599
0
        kemctxlen += encodedpublen;
600
0
    }
601
0
    if (kemctxlen > sizeof(kemctx))
602
0
        goto err;
603
604
    /* kemctx is the concat of both sides encoded public key */
605
0
    memcpy(kemctx, sender_pub, info->Npk);
606
0
    memcpy(kemctx + info->Npk, recipient_pub, info->Npk);
607
0
    if (auth)
608
0
        memcpy(kemctx + 2 * encodedpublen, sender_authpub, encodedpublen);
609
0
    kdfctx = ossl_kdf_ctx_create(ctx->kdfname, info->mdname,
610
0
                                 ctx->libctx, ctx->propq);
611
0
    if (kdfctx == NULL)
612
0
        goto err;
613
0
    if (!dhkem_extract_and_expand(kdfctx, secret, info->Nsecret,
614
0
                                  info->kem_id, dhkm, dhkmlen,
615
0
                                  kemctx, kemctxlen))
616
0
        goto err;
617
0
    ret = 1;
618
0
err:
619
0
    OPENSSL_cleanse(dhkm, dhkmlen);
620
0
    EVP_KDF_CTX_free(kdfctx);
621
0
    return ret;
622
0
}
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
 */
642
static int dhkem_encap(PROV_EC_CTX *ctx,
643
                       unsigned char *enc, size_t *enclen,
644
                       unsigned char *secret, size_t *secretlen)
645
0
{
646
0
    int ret = 0;
647
0
    EC_KEY *sender_ephemkey = NULL;
648
0
    unsigned char sender_pub[OSSL_HPKE_MAX_PUBLIC];
649
0
    unsigned char recipient_pub[OSSL_HPKE_MAX_PUBLIC];
650
0
    size_t sender_publen, recipient_publen;
651
0
    const OSSL_HPKE_KEM_INFO *info = ctx->info;
652
653
0
    if (enc == NULL) {
654
0
        if (enclen == NULL && secretlen == NULL)
655
0
            return 0;
656
0
        if (enclen != NULL)
657
0
            *enclen = info->Nenc;
658
0
        if (secretlen != NULL)
659
0
            *secretlen = info->Nsecret;
660
0
       return 1;
661
0
    }
662
663
0
    if (*secretlen < info->Nsecret) {
664
0
        ERR_raise_data(ERR_LIB_PROV, PROV_R_BAD_LENGTH, "*secretlen too small");
665
0
        return 0;
666
0
    }
667
0
    if (*enclen < info->Nenc) {
668
0
        ERR_raise_data(ERR_LIB_PROV, PROV_R_BAD_LENGTH, "*enclen too small");
669
0
        return 0;
670
0
    }
671
672
    /* Create an ephemeral key */
673
0
    sender_ephemkey = derivekey(ctx, ctx->ikm, ctx->ikmlen);
674
0
    if (sender_ephemkey == NULL)
675
0
        goto err;
676
0
    if (!ecpubkey_todata(sender_ephemkey, sender_pub, &sender_publen,
677
0
                         sizeof(sender_pub))
678
0
            || !ecpubkey_todata(ctx->recipient_key, recipient_pub,
679
0
                                &recipient_publen, sizeof(recipient_pub)))
680
0
        goto err;
681
682
0
    if (sender_publen != info->Npk
683
0
            || recipient_publen != sender_publen) {
684
0
        ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_KEY, "Invalid public key");
685
0
        goto err;
686
0
    }
687
688
0
    if (!derive_secret(ctx, secret,
689
0
                       sender_ephemkey, ctx->recipient_key,
690
0
                       ctx->sender_authkey, ctx->recipient_key,
691
0
                       sender_pub, recipient_pub))
692
0
        goto err;
693
694
    /* Return the senders ephemeral public key in encoded form */
695
0
    memcpy(enc, sender_pub, sender_publen);
696
0
    *enclen = sender_publen;
697
0
    *secretlen = info->Nsecret;
698
0
    ret = 1;
699
0
err:
700
0
    EC_KEY_free(sender_ephemkey);
701
0
    return ret;
702
0
}
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
 */
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
0
{
724
0
    int ret = 0;
725
0
    EC_KEY *sender_ephempubkey = NULL;
726
0
    const OSSL_HPKE_KEM_INFO *info = ctx->info;
727
0
    unsigned char recipient_pub[OSSL_HPKE_MAX_PUBLIC];
728
0
    size_t recipient_publen;
729
0
    size_t encodedpublen = info->Npk;
730
731
0
    if (secret == NULL) {
732
0
        *secretlen = info->Nsecret;
733
0
        return 1;
734
0
    }
735
736
0
    if (*secretlen < info->Nsecret) {
737
0
        ERR_raise_data(ERR_LIB_PROV, PROV_R_BAD_LENGTH, "*secretlen too small");
738
0
        return 0;
739
0
    }
740
0
    if (enclen != encodedpublen) {
741
0
        ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_KEY, "Invalid enc public key");
742
0
        return 0;
743
0
    }
744
745
0
    sender_ephempubkey = eckey_frompub(ctx->recipient_key, enc, enclen);
746
0
    if (sender_ephempubkey == NULL)
747
0
        goto err;
748
0
    if (!ecpubkey_todata(ctx->recipient_key, recipient_pub, &recipient_publen,
749
0
                         sizeof(recipient_pub)))
750
0
        goto err;
751
0
    if (recipient_publen != encodedpublen) {
752
0
        ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_KEY, "Invalid recipient public key");
753
0
        goto err;
754
0
    }
755
756
0
    if (!derive_secret(ctx, secret,
757
0
                       ctx->recipient_key, sender_ephempubkey,
758
0
                       ctx->recipient_key, ctx->sender_authkey,
759
0
                       enc, recipient_pub))
760
0
        goto err;
761
0
    *secretlen = info->Nsecret;
762
0
    ret = 1;
763
0
err:
764
0
    EC_KEY_free(sender_ephempubkey);
765
0
    return ret;
766
0
}
767
768
static int eckem_encapsulate(void *vctx, unsigned char *out, size_t *outlen,
769
                             unsigned char *secret, size_t *secretlen)
770
0
{
771
0
    PROV_EC_CTX *ctx = (PROV_EC_CTX *)vctx;
772
773
0
    switch (ctx->mode) {
774
0
        case KEM_MODE_DHKEM:
775
0
            return dhkem_encap(ctx, out, outlen, secret, secretlen);
776
0
        default:
777
0
            ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_MODE);
778
0
            return -2;
779
0
    }
780
0
}
781
782
static int eckem_decapsulate(void *vctx, unsigned char *out, size_t *outlen,
783
                             const unsigned char *in, size_t inlen)
784
0
{
785
0
    PROV_EC_CTX *ctx = (PROV_EC_CTX *)vctx;
786
787
0
    switch (ctx->mode) {
788
0
        case KEM_MODE_DHKEM:
789
0
            return dhkem_decap(ctx, out, outlen, in, inlen);
790
0
        default:
791
0
            ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_MODE);
792
0
            return -2;
793
0
    }
794
0
}
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
};