Coverage Report

Created: 2025-08-25 06:30

/src/openssl/providers/implementations/kem/ec_kem.c
Line
Count
Source (jump to first uncovered line)
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
/*
12
 * The following implementation is part of RFC 9180 related to DHKEM using
13
 * EC keys (i.e. P-256, P-384 and P-521)
14
 * References to Sections in the comments below refer to RFC 9180.
15
 */
16
17
#include "internal/deprecated.h"
18
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/ec.h>
24
#include <openssl/params.h>
25
#include <openssl/err.h>
26
#include <openssl/proverr.h>
27
#include <openssl/kdf.h>
28
#include <openssl/rand.h>
29
#include "internal/cryptlib.h"
30
#include "prov/provider_ctx.h"
31
#include "prov/implementations.h"
32
#include "prov/securitycheck.h"
33
#include "prov/providercommon.h"
34
35
#include <openssl/hpke.h>
36
#include "internal/hpke_util.h"
37
#include "crypto/ec.h"
38
#include "prov/ecx.h"
39
#include "prov/eckem.h"
40
41
typedef struct {
42
    EC_KEY *recipient_key;
43
    EC_KEY *sender_authkey;
44
    OSSL_LIB_CTX *libctx;
45
    char *propq;
46
    unsigned int mode;
47
    unsigned int op;
48
    unsigned char *ikm;
49
    size_t ikmlen;
50
    const char *kdfname;
51
    const OSSL_HPKE_KEM_INFO *info;
52
} PROV_EC_CTX;
53
54
static OSSL_FUNC_kem_newctx_fn eckem_newctx;
55
static OSSL_FUNC_kem_encapsulate_init_fn eckem_encapsulate_init;
56
static OSSL_FUNC_kem_auth_encapsulate_init_fn eckem_auth_encapsulate_init;
57
static OSSL_FUNC_kem_encapsulate_fn eckem_encapsulate;
58
static OSSL_FUNC_kem_decapsulate_init_fn eckem_decapsulate_init;
59
static OSSL_FUNC_kem_auth_decapsulate_init_fn eckem_auth_decapsulate_init;
60
static OSSL_FUNC_kem_decapsulate_fn eckem_decapsulate;
61
static OSSL_FUNC_kem_freectx_fn eckem_freectx;
62
static OSSL_FUNC_kem_set_ctx_params_fn eckem_set_ctx_params;
63
static OSSL_FUNC_kem_settable_ctx_params_fn eckem_settable_ctx_params;
64
65
/* ASCII: "KEM", in hex for EBCDIC compatibility */
66
static const char LABEL_KEM[] = "\x4b\x45\x4d";
67
68
static int eckey_check(const EC_KEY *ec, int requires_privatekey)
69
0
{
70
0
    int rv = 0;
71
0
    BN_CTX *bnctx = NULL;
72
0
    BIGNUM *rem = NULL;
73
0
    const BIGNUM *priv = EC_KEY_get0_private_key(ec);
74
0
    const EC_POINT *pub = EC_KEY_get0_public_key(ec);
75
76
    /* Keys always require a public component */
77
0
    if (pub == NULL) {
78
0
        ERR_raise(ERR_LIB_PROV, PROV_R_NOT_A_PUBLIC_KEY);
79
0
        return 0;
80
0
    }
81
0
    if (priv == NULL) {
82
0
        return (requires_privatekey == 0);
83
0
    } else {
84
        /* If there is a private key, check that is non zero (mod order) */
85
0
        const EC_GROUP *group = EC_KEY_get0_group(ec);
86
0
        const BIGNUM *order = EC_GROUP_get0_order(group);
87
88
0
        bnctx = BN_CTX_new_ex(ossl_ec_key_get_libctx(ec));
89
0
        rem = BN_new();
90
91
0
        if (order != NULL && rem != NULL && bnctx != NULL) {
92
0
             rv = BN_mod(rem, priv, order, bnctx)
93
0
                  && !BN_is_zero(rem);
94
0
        }
95
0
    }
96
0
    BN_free(rem);
97
0
    BN_CTX_free(bnctx);
98
0
    return rv;
99
0
}
100
101
/* Returns NULL if the curve is not supported */
102
static const char *ec_curvename_get0(const EC_KEY *ec)
103
0
{
104
0
    const EC_GROUP *group = EC_KEY_get0_group(ec);
105
106
0
    return EC_curve_nid2nist(EC_GROUP_get_curve_name(group));
107
0
}
108
109
/*
110
 * Set the recipient key, and free any existing key.
111
 * ec can be NULL.
112
 * The ec key may have only a private or public component
113
 * (but it must have a group).
114
 */
115
static int recipient_key_set(PROV_EC_CTX *ctx, EC_KEY *ec)
116
0
{
117
0
    EC_KEY_free(ctx->recipient_key);
118
0
    ctx->recipient_key = NULL;
119
120
0
    if (ec != NULL) {
121
0
        const char *curve = ec_curvename_get0(ec);
122
123
0
        if (curve == NULL)
124
0
            return -2;
125
0
        ctx->info = ossl_HPKE_KEM_INFO_find_curve(curve);
126
0
        if (ctx->info == NULL)
127
0
            return -2;
128
0
        if (!EC_KEY_up_ref(ec))
129
0
            return 0;
130
0
        ctx->recipient_key = ec;
131
0
        ctx->kdfname = "HKDF";
132
0
    }
133
0
    return 1;
134
0
}
135
136
/*
137
 * Set the senders auth key, and free any existing auth key.
138
 * ec can be NULL.
139
 */
140
static int sender_authkey_set(PROV_EC_CTX *ctx, EC_KEY *ec)
141
0
{
142
0
    EC_KEY_free(ctx->sender_authkey);
143
0
    ctx->sender_authkey = NULL;
144
145
0
    if (ec != NULL) {
146
0
        if (!EC_KEY_up_ref(ec))
147
0
            return 0;
148
0
        ctx->sender_authkey = ec;
149
0
    }
150
0
    return 1;
151
0
}
152
153
/*
154
 * Serializes a encoded public key buffer into a EC public key.
155
 * Params:
156
 *     in Contains the group.
157
 *     pubbuf The encoded public key buffer
158
 * Returns: The created public EC key, or NULL if there is an error.
159
 */
160
static EC_KEY *eckey_frompub(EC_KEY *in,
161
                             const unsigned char *pubbuf, size_t pubbuflen)
162
0
{
163
0
    EC_KEY *key;
164
165
0
    key = EC_KEY_new_ex(ossl_ec_key_get_libctx(in), ossl_ec_key_get0_propq(in));
166
0
    if (key == NULL)
167
0
        goto err;
168
0
    if (!EC_KEY_set_group(key, EC_KEY_get0_group(in)))
169
0
        goto err;
170
0
    if (!EC_KEY_oct2key(key, pubbuf, pubbuflen, NULL))
171
0
        goto err;
172
0
    return key;
173
0
err:
174
0
    EC_KEY_free(key);
175
0
    return NULL;
176
0
}
177
178
/*
179
 * Deserialises a EC public key into a encoded byte array.
180
 * Returns: 1 if successful or 0 otherwise.
181
 */
182
static int ecpubkey_todata(const EC_KEY *ec, unsigned char *out, size_t *outlen,
183
                           size_t maxoutlen)
184
0
{
185
0
    const EC_POINT *pub;
186
0
    const EC_GROUP *group;
187
188
0
    group = EC_KEY_get0_group(ec);
189
0
    pub = EC_KEY_get0_public_key(ec);
190
0
    *outlen = EC_POINT_point2oct(group, pub, POINT_CONVERSION_UNCOMPRESSED,
191
0
                                 out, maxoutlen, NULL);
192
0
    return *outlen != 0;
193
0
}
194
195
static void *eckem_newctx(void *provctx)
196
0
{
197
0
    PROV_EC_CTX *ctx =  OPENSSL_zalloc(sizeof(PROV_EC_CTX));
198
199
0
    if (ctx == NULL)
200
0
        return NULL;
201
0
    ctx->libctx = PROV_LIBCTX_OF(provctx);
202
0
    ctx->mode = KEM_MODE_DHKEM;
203
204
0
    return ctx;
205
0
}
206
207
static void eckem_freectx(void *vectx)
208
0
{
209
0
    PROV_EC_CTX *ctx = (PROV_EC_CTX *)vectx;
210
211
0
    OPENSSL_clear_free(ctx->ikm, ctx->ikmlen);
212
0
    recipient_key_set(ctx, NULL);
213
0
    sender_authkey_set(ctx, NULL);
214
0
    OPENSSL_free(ctx);
215
0
}
216
217
static int ossl_ec_match_params(const EC_KEY *key1, const EC_KEY *key2)
218
0
{
219
0
    int ret;
220
0
    BN_CTX *ctx = NULL;
221
0
    const EC_GROUP *group1 = EC_KEY_get0_group(key1);
222
0
    const EC_GROUP *group2 = EC_KEY_get0_group(key2);
223
224
0
    ctx = BN_CTX_new_ex(ossl_ec_key_get_libctx(key1));
225
0
    if (ctx == NULL)
226
0
        return 0;
227
228
0
    ret = group1 != NULL
229
0
          && group2 != NULL
230
0
          && EC_GROUP_cmp(group1, group2, ctx) == 0;
231
0
    if (!ret)
232
0
        ERR_raise(ERR_LIB_PROV, PROV_R_MISMATCHING_DOMAIN_PARAMETERS);
233
0
    BN_CTX_free(ctx);
234
0
    return ret;
235
0
}
236
237
static int eckem_init(void *vctx, int operation, void *vec, void *vauth,
238
                      const OSSL_PARAM params[])
239
0
{
240
0
    int rv;
241
0
    PROV_EC_CTX *ctx = (PROV_EC_CTX *)vctx;
242
0
    EC_KEY *ec = vec;
243
0
    EC_KEY *auth = vauth;
244
245
0
    if (!ossl_prov_is_running())
246
0
        return 0;
247
248
0
    if (!eckey_check(ec, operation == EVP_PKEY_OP_DECAPSULATE))
249
0
        return 0;
250
0
    rv = recipient_key_set(ctx, ec);
251
0
    if (rv <= 0)
252
0
        return rv;
253
254
0
    if (auth != NULL) {
255
0
        if (!ossl_ec_match_params(ec, auth)
256
0
            || !eckey_check(auth, operation == EVP_PKEY_OP_ENCAPSULATE)
257
0
            || !sender_authkey_set(ctx, auth))
258
0
        return 0;
259
0
    }
260
261
0
    ctx->op = operation;
262
0
    return eckem_set_ctx_params(vctx, params);
263
0
}
264
265
static int eckem_encapsulate_init(void *vctx, void *vec,
266
                                   const OSSL_PARAM params[])
267
0
{
268
0
    return eckem_init(vctx, EVP_PKEY_OP_ENCAPSULATE, vec, NULL, params);
269
0
}
270
271
static int eckem_decapsulate_init(void *vctx, void *vec,
272
                                   const OSSL_PARAM params[])
273
0
{
274
0
    return eckem_init(vctx, EVP_PKEY_OP_DECAPSULATE, vec, NULL, params);
275
0
}
276
277
static int eckem_auth_encapsulate_init(void *vctx, void *vecx, void *vauthpriv,
278
                                       const OSSL_PARAM params[])
279
0
{
280
0
    return eckem_init(vctx, EVP_PKEY_OP_ENCAPSULATE, vecx, vauthpriv, params);
281
0
}
282
283
static int eckem_auth_decapsulate_init(void *vctx, void *vecx, void *vauthpub,
284
                                       const OSSL_PARAM params[])
285
0
{
286
0
    return eckem_init(vctx, EVP_PKEY_OP_DECAPSULATE, vecx, vauthpub, params);
287
0
}
288
289
290
/* Machine generated by util/perl/OpenSSL/paramnames.pm */
291
#ifndef eckem_set_ctx_params_list
292
static const OSSL_PARAM eckem_set_ctx_params_list[] = {
293
    OSSL_PARAM_utf8_string(OSSL_KEM_PARAM_OPERATION, NULL, 0),
294
    OSSL_PARAM_octet_string(OSSL_KEM_PARAM_IKME, NULL, 0),
295
    OSSL_PARAM_END
296
};
297
#endif
298
299
#ifndef eckem_set_ctx_params_st
300
struct eckem_set_ctx_params_st {
301
    OSSL_PARAM *ikme;
302
    OSSL_PARAM *op;
303
};
304
#endif
305
306
#ifndef eckem_set_ctx_params_decoder
307
static int eckem_set_ctx_params_decoder
308
    (const OSSL_PARAM *p, struct eckem_set_ctx_params_st *r)
309
0
{
310
0
    const char *s;
311
312
0
    memset(r, 0, sizeof(*r));
313
0
    if (p != NULL)
314
0
        for (; (s = p->key) != NULL; p++)
315
0
            switch(s[0]) {
316
0
            default:
317
0
                break;
318
0
            case 'i':
319
0
                if (ossl_likely(strcmp("kme", s + 1) == 0)) {
320
                    /* KEM_PARAM_IKME */
321
0
                    if (ossl_unlikely(r->ikme != NULL)) {
322
0
                        ERR_raise_data(ERR_LIB_PROV, PROV_R_REPEATED_PARAMETER,
323
0
                                       "param %s is repeated", s);
324
0
                        return 0;
325
0
                    }
326
0
                    r->ikme = (OSSL_PARAM *)p;
327
0
                }
328
0
                break;
329
0
            case 'o':
330
0
                if (ossl_likely(strcmp("peration", s + 1) == 0)) {
331
                    /* KEM_PARAM_OPERATION */
332
0
                    if (ossl_unlikely(r->op != NULL)) {
333
0
                        ERR_raise_data(ERR_LIB_PROV, PROV_R_REPEATED_PARAMETER,
334
0
                                       "param %s is repeated", s);
335
0
                        return 0;
336
0
                    }
337
0
                    r->op = (OSSL_PARAM *)p;
338
0
                }
339
0
            }
340
0
    return 1;
341
0
}
342
#endif
343
/* End of machine generated */
344
345
static int eckem_set_ctx_params(void *vctx, const OSSL_PARAM params[])
346
0
{
347
0
    PROV_EC_CTX *ctx = (PROV_EC_CTX *)vctx;
348
0
    struct eckem_set_ctx_params_st p;
349
0
    int mode;
350
351
0
    if (ctx == NULL || !eckem_set_ctx_params_decoder(params, &p))
352
0
        return 0;
353
354
0
    if (p.ikme != NULL) {
355
0
        void *tmp = NULL;
356
0
        size_t tmplen = 0;
357
358
0
        if (p.ikme->data != NULL && p.ikme->data_size != 0) {
359
0
            if (!OSSL_PARAM_get_octet_string(p.ikme, &tmp, 0, &tmplen))
360
0
                return 0;
361
0
        }
362
0
        OPENSSL_clear_free(ctx->ikm, ctx->ikmlen);
363
        /* Set the ephemeral seed */
364
0
        ctx->ikm = tmp;
365
0
        ctx->ikmlen = tmplen;
366
0
    }
367
368
0
    if (p.op != NULL) {
369
0
        if (p.op->data_type != OSSL_PARAM_UTF8_STRING)
370
0
            return 0;
371
0
        mode = ossl_eckem_modename2id(p.op->data);
372
0
        if (mode == KEM_MODE_UNDEFINED)
373
0
            return 0;
374
0
        ctx->mode = mode;
375
0
    }
376
0
    return 1;
377
0
}
378
379
static const OSSL_PARAM *eckem_settable_ctx_params(ossl_unused void *vctx,
380
                                                   ossl_unused void *provctx)
381
0
{
382
0
    return eckem_set_ctx_params_list;
383
0
}
384
385
/*
386
 * See Section 4.1 DH-Based KEM (DHKEM) ExtractAndExpand
387
 */
388
static int dhkem_extract_and_expand(EVP_KDF_CTX *kctx,
389
                                    unsigned char *okm, size_t okmlen,
390
                                    uint16_t kemid,
391
                                    const unsigned char *dhkm, size_t dhkmlen,
392
                                    const unsigned char *kemctx,
393
                                    size_t kemctxlen)
394
0
{
395
0
    uint8_t suiteid[2];
396
0
    uint8_t prk[EVP_MAX_MD_SIZE];
397
0
    size_t prklen = okmlen;
398
0
    int ret;
399
400
0
    if (prklen > sizeof(prk))
401
0
        return 0;
402
403
0
    suiteid[0] = (kemid >> 8) & 0xff;
404
0
    suiteid[1] = kemid & 0xff;
405
406
0
    ret = ossl_hpke_labeled_extract(kctx, prk, prklen,
407
0
                                    NULL, 0, LABEL_KEM, suiteid, sizeof(suiteid),
408
0
                                    OSSL_DHKEM_LABEL_EAE_PRK, dhkm, dhkmlen)
409
0
          && ossl_hpke_labeled_expand(kctx, okm, okmlen, prk, prklen,
410
0
                                      LABEL_KEM, suiteid, sizeof(suiteid),
411
0
                                      OSSL_DHKEM_LABEL_SHARED_SECRET,
412
0
                                      kemctx, kemctxlen);
413
0
    OPENSSL_cleanse(prk, prklen);
414
0
    return ret;
415
0
}
416
417
/*
418
 * See Section 7.1.3 DeriveKeyPair.
419
 *
420
 * This function is used by ec keygen.
421
 * (For this reason it does not use any of the state stored in PROV_EC_CTX).
422
 *
423
 * Params:
424
 *     ec An initialized ec key.
425
 *     priv The buffer to store the generated private key into (it is assumed
426
 *          this is of length alg->encodedprivlen).
427
 *     ikm buffer containing the input key material (seed). This must be set.
428
 *     ikmlen size of the ikm buffer in bytes
429
 * Returns:
430
 *     1 if successful or 0 otherwise.
431
 */
432
int ossl_ec_dhkem_derive_private(EC_KEY *ec, BIGNUM *priv,
433
                                 const unsigned char *ikm, size_t ikmlen)
434
0
{
435
0
    int ret = 0;
436
0
    EVP_KDF_CTX *kdfctx = NULL;
437
0
    uint8_t suiteid[2];
438
0
    unsigned char prk[OSSL_HPKE_MAX_SECRET];
439
0
    unsigned char privbuf[OSSL_HPKE_MAX_PRIVATE];
440
0
    const BIGNUM *order;
441
0
    unsigned char counter = 0;
442
0
    const char *curve = ec_curvename_get0(ec);
443
0
    const OSSL_HPKE_KEM_INFO *info;
444
445
0
    if (curve == NULL)
446
0
        return -2;
447
448
0
    info = ossl_HPKE_KEM_INFO_find_curve(curve);
449
0
    if (info == NULL)
450
0
        return -2;
451
452
0
    kdfctx = ossl_kdf_ctx_create("HKDF", info->mdname,
453
0
                                 ossl_ec_key_get_libctx(ec),
454
0
                                 ossl_ec_key_get0_propq(ec));
455
0
    if (kdfctx == NULL)
456
0
        return 0;
457
458
    /* ikmlen should have a length of at least Nsk */
459
0
    if (ikmlen < info->Nsk) {
460
0
        ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_INPUT_LENGTH,
461
0
                       "ikm length is :%zu, should be at least %zu",
462
0
                       ikmlen, info->Nsk);
463
0
        goto err;
464
0
    }
465
466
0
    suiteid[0] = info->kem_id / 256;
467
0
    suiteid[1] = info->kem_id % 256;
468
469
0
    if (!ossl_hpke_labeled_extract(kdfctx, prk, info->Nsecret,
470
0
                                   NULL, 0, LABEL_KEM, suiteid, sizeof(suiteid),
471
0
                                   OSSL_DHKEM_LABEL_DKP_PRK, ikm, ikmlen))
472
0
        goto err;
473
474
0
    order = EC_GROUP_get0_order(EC_KEY_get0_group(ec));
475
0
    do {
476
0
        if (!ossl_hpke_labeled_expand(kdfctx, privbuf, info->Nsk,
477
0
                                      prk, info->Nsecret,
478
0
                                      LABEL_KEM, suiteid, sizeof(suiteid),
479
0
                                      OSSL_DHKEM_LABEL_CANDIDATE,
480
0
                                      &counter, 1))
481
0
            goto err;
482
0
        privbuf[0] &= info->bitmask;
483
0
        if (BN_bin2bn(privbuf, (int)info->Nsk, priv) == NULL)
484
0
            goto err;
485
0
        if (counter == 0xFF) {
486
0
            ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GENERATE_KEY);
487
0
            goto err;
488
0
        }
489
0
        counter++;
490
0
    } while (BN_is_zero(priv) || BN_cmp(priv, order) >= 0);
491
0
    ret = 1;
492
0
err:
493
0
    OPENSSL_cleanse(prk, sizeof(prk));
494
0
    OPENSSL_cleanse(privbuf, sizeof(privbuf));
495
0
    EVP_KDF_CTX_free(kdfctx);
496
0
    return ret;
497
0
}
498
499
/*
500
 * Do a keygen operation without having to use EVP_PKEY.
501
 * Params:
502
 *     ctx Context object
503
 *     ikm The seed material - if this is NULL, then a random seed is used.
504
 * Returns:
505
 *     The generated EC key, or NULL on failure.
506
 */
507
static EC_KEY *derivekey(PROV_EC_CTX *ctx,
508
                         const unsigned char *ikm, size_t ikmlen)
509
0
{
510
0
    int ret = 0;
511
0
    EC_KEY *key;
512
0
    unsigned char *seed = (unsigned char *)ikm;
513
0
    size_t seedlen = ikmlen;
514
0
    unsigned char tmpbuf[OSSL_HPKE_MAX_PRIVATE];
515
516
0
    key = EC_KEY_new_ex(ctx->libctx, ctx->propq);
517
0
    if (key == NULL)
518
0
        goto err;
519
0
    if (!EC_KEY_set_group(key, EC_KEY_get0_group(ctx->recipient_key)))
520
0
        goto err;
521
522
    /* Generate a random seed if there is no input ikm */
523
0
    if (seed == NULL || seedlen == 0) {
524
0
        seedlen = ctx->info->Nsk;
525
0
        if (seedlen > sizeof(tmpbuf))
526
0
            goto err;
527
0
        if (RAND_priv_bytes_ex(ctx->libctx, tmpbuf, seedlen, 0) <= 0)
528
0
            goto err;
529
0
        seed = tmpbuf;
530
0
    }
531
0
    ret = ossl_ec_generate_key_dhkem(key, seed, seedlen);
532
0
err:
533
0
    if (seed != ikm)
534
0
        OPENSSL_cleanse(seed, seedlen);
535
0
    if (ret <= 0) {
536
0
        EC_KEY_free(key);
537
0
        key = NULL;
538
0
    }
539
0
    return key;
540
0
}
541
542
/*
543
 * Before doing a key exchange the public key of the peer needs to be checked
544
 * Note that the group check is not done here as we have already checked
545
 * that it only uses one of the approved curve names when the key was set.
546
 *
547
 * Returns 1 if the public key is valid, or 0 if it fails.
548
 */
549
static int check_publickey(const EC_KEY *pub)
550
0
{
551
0
    int ret = 0;
552
0
    BN_CTX *bnctx = BN_CTX_new_ex(ossl_ec_key_get_libctx(pub));
553
554
0
    if (bnctx == NULL)
555
0
        return 0;
556
0
    ret = ossl_ec_key_public_check(pub, bnctx);
557
0
    BN_CTX_free(bnctx);
558
559
0
    return ret;
560
0
}
561
562
/*
563
 * Do an ecdh key exchange.
564
 * dhkm = DH(sender, peer)
565
 *
566
 * NOTE: Instead of using EVP_PKEY_derive() API's, we use EC_KEY operations
567
 *       to avoid messy conversions back to EVP_PKEY.
568
 *
569
 * Returns the size of the secret if successful, or 0 otherwise,
570
 */
571
static int generate_ecdhkm(const EC_KEY *sender, const EC_KEY *peer,
572
                           unsigned char *out, size_t maxout,
573
                           unsigned int secretsz)
574
0
{
575
0
    const EC_GROUP *group = EC_KEY_get0_group(sender);
576
0
    size_t secretlen = (EC_GROUP_get_degree(group) + 7) / 8;
577
578
0
    if (secretlen != secretsz || secretlen > maxout) {
579
0
        ERR_raise_data(ERR_LIB_PROV,  PROV_R_BAD_LENGTH, "secretsz invalid");
580
0
        return 0;
581
0
    }
582
583
0
    if (!check_publickey(peer))
584
0
        return 0;
585
0
    return ECDH_compute_key(out, secretlen, EC_KEY_get0_public_key(peer),
586
0
                            sender, NULL) > 0;
587
0
}
588
589
/*
590
 * Derive a secret using ECDH (code is shared by the encap and decap)
591
 *
592
 * dhkm = Concat(ecdh(privkey1, peerkey1), ecdh(privkey2, peerkey2)
593
 * kemctx = Concat(sender_pub, recipient_pub, ctx->sender_authkey)
594
 * secret = dhkem_extract_and_expand(kemid, dhkm, kemctx);
595
 *
596
 * Params:
597
 *     ctx Object that contains algorithm state and constants.
598
 *     secret The returned secret (with a length ctx->alg->secretlen bytes).
599
 *     privkey1 A private key used for ECDH key derivation.
600
 *     peerkey1 A public key used for ECDH key derivation with privkey1
601
 *     privkey2 A optional private key used for a second ECDH key derivation.
602
 *              It can be NULL.
603
 *     peerkey2 A optional public key used for a second ECDH key derivation
604
 *              with privkey2,. It can be NULL.
605
 *     sender_pub The senders public key in encoded form.
606
 *     recipient_pub The recipients public key in encoded form.
607
 * Notes:
608
 *     The second ecdh() is only used for the HPKE auth modes when both privkey2
609
 *     and peerkey2 are non NULL (i.e. ctx->sender_authkey is not NULL).
610
 */
611
static int derive_secret(PROV_EC_CTX *ctx, unsigned char *secret,
612
                         const EC_KEY *privkey1, const EC_KEY *peerkey1,
613
                         const EC_KEY *privkey2, const EC_KEY *peerkey2,
614
                         const unsigned char *sender_pub,
615
                         const unsigned char *recipient_pub)
616
0
{
617
0
    int ret = 0;
618
0
    EVP_KDF_CTX *kdfctx = NULL;
619
0
    unsigned char sender_authpub[OSSL_HPKE_MAX_PUBLIC];
620
0
    unsigned char dhkm[OSSL_HPKE_MAX_PRIVATE * 2];
621
0
    unsigned char kemctx[OSSL_HPKE_MAX_PUBLIC * 3];
622
0
    size_t sender_authpublen;
623
0
    size_t kemctxlen = 0, dhkmlen = 0;
624
0
    const OSSL_HPKE_KEM_INFO *info = ctx->info;
625
0
    size_t encodedpublen = info->Npk;
626
0
    size_t encodedprivlen = info->Nsk;
627
0
    int auth = ctx->sender_authkey != NULL;
628
629
0
    if (!generate_ecdhkm(privkey1, peerkey1, dhkm, sizeof(dhkm),
630
0
                         (unsigned int)encodedprivlen))
631
0
        goto err;
632
0
    dhkmlen = encodedprivlen;
633
0
    kemctxlen = 2 * encodedpublen;
634
635
    /* Concat the optional second ECDH (used for Auth) */
636
0
    if (auth) {
637
        /* Get the public key of the auth sender in encoded form */
638
0
        if (!ecpubkey_todata(ctx->sender_authkey, sender_authpub,
639
0
                             &sender_authpublen, sizeof(sender_authpub)))
640
0
            goto err;
641
0
        if (sender_authpublen != encodedpublen) {
642
0
            ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_KEY,
643
0
                           "Invalid sender auth public key");
644
0
            goto err;
645
0
        }
646
0
        if (!generate_ecdhkm(privkey2, peerkey2,
647
0
                             dhkm + dhkmlen, sizeof(dhkm) - dhkmlen,
648
0
                             (unsigned int)encodedprivlen))
649
0
            goto err;
650
0
        dhkmlen += encodedprivlen;
651
0
        kemctxlen += encodedpublen;
652
0
    }
653
0
    if (kemctxlen > sizeof(kemctx))
654
0
        goto err;
655
656
    /* kemctx is the concat of both sides encoded public key */
657
0
    memcpy(kemctx, sender_pub, info->Npk);
658
0
    memcpy(kemctx + info->Npk, recipient_pub, info->Npk);
659
0
    if (auth)
660
0
        memcpy(kemctx + 2 * encodedpublen, sender_authpub, encodedpublen);
661
0
    kdfctx = ossl_kdf_ctx_create(ctx->kdfname, info->mdname,
662
0
                                 ctx->libctx, ctx->propq);
663
0
    if (kdfctx == NULL)
664
0
        goto err;
665
0
    if (!dhkem_extract_and_expand(kdfctx, secret, info->Nsecret,
666
0
                                  info->kem_id, dhkm, dhkmlen,
667
0
                                  kemctx, kemctxlen))
668
0
        goto err;
669
0
    ret = 1;
670
0
err:
671
0
    OPENSSL_cleanse(dhkm, dhkmlen);
672
0
    EVP_KDF_CTX_free(kdfctx);
673
0
    return ret;
674
0
}
675
676
/*
677
 * Do a DHKEM encapsulate operation.
678
 *
679
 * See Section 4.1 Encap() and AuthEncap()
680
 *
681
 * Params:
682
 *     ctx A context object holding the recipients public key and the
683
 *         optional senders auth private key.
684
 *     enc A buffer to return the senders ephemeral public key.
685
 *         Setting this to NULL allows the enclen and secretlen to return
686
 *         values, without calculating the secret.
687
 *     enclen Passes in the max size of the enc buffer and returns the
688
 *            encoded public key length.
689
 *     secret A buffer to return the calculated shared secret.
690
 *     secretlen Passes in the max size of the secret buffer and returns the
691
 *               secret length.
692
 * Returns: 1 on success or 0 otherwise.
693
 */
694
static int dhkem_encap(PROV_EC_CTX *ctx,
695
                       unsigned char *enc, size_t *enclen,
696
                       unsigned char *secret, size_t *secretlen)
697
0
{
698
0
    int ret = 0;
699
0
    EC_KEY *sender_ephemkey = NULL;
700
0
    unsigned char sender_pub[OSSL_HPKE_MAX_PUBLIC];
701
0
    unsigned char recipient_pub[OSSL_HPKE_MAX_PUBLIC];
702
0
    size_t sender_publen, recipient_publen;
703
0
    const OSSL_HPKE_KEM_INFO *info = ctx->info;
704
705
0
    if (enc == NULL) {
706
0
        if (enclen == NULL && secretlen == NULL)
707
0
            return 0;
708
0
        if (enclen != NULL)
709
0
            *enclen = info->Nenc;
710
0
        if (secretlen != NULL)
711
0
            *secretlen = info->Nsecret;
712
0
       return 1;
713
0
    }
714
715
0
    if (*secretlen < info->Nsecret) {
716
0
        ERR_raise_data(ERR_LIB_PROV, PROV_R_BAD_LENGTH, "*secretlen too small");
717
0
        return 0;
718
0
    }
719
0
    if (*enclen < info->Nenc) {
720
0
        ERR_raise_data(ERR_LIB_PROV, PROV_R_BAD_LENGTH, "*enclen too small");
721
0
        return 0;
722
0
    }
723
724
    /* Create an ephemeral key */
725
0
    sender_ephemkey = derivekey(ctx, ctx->ikm, ctx->ikmlen);
726
0
    if (sender_ephemkey == NULL)
727
0
        goto err;
728
0
    if (!ecpubkey_todata(sender_ephemkey, sender_pub, &sender_publen,
729
0
                         sizeof(sender_pub))
730
0
            || !ecpubkey_todata(ctx->recipient_key, recipient_pub,
731
0
                                &recipient_publen, sizeof(recipient_pub)))
732
0
        goto err;
733
734
0
    if (sender_publen != info->Npk
735
0
            || recipient_publen != sender_publen) {
736
0
        ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_KEY, "Invalid public key");
737
0
        goto err;
738
0
    }
739
740
0
    if (!derive_secret(ctx, secret,
741
0
                       sender_ephemkey, ctx->recipient_key,
742
0
                       ctx->sender_authkey, ctx->recipient_key,
743
0
                       sender_pub, recipient_pub))
744
0
        goto err;
745
746
    /* Return the senders ephemeral public key in encoded form */
747
0
    memcpy(enc, sender_pub, sender_publen);
748
0
    *enclen = sender_publen;
749
0
    *secretlen = info->Nsecret;
750
0
    ret = 1;
751
0
err:
752
0
    EC_KEY_free(sender_ephemkey);
753
0
    return ret;
754
0
}
755
756
/*
757
 * Do a DHKEM decapsulate operation.
758
 * See Section 4.1 Decap() and Auth Decap()
759
 *
760
 * Params:
761
 *     ctx A context object holding the recipients private key and the
762
 *         optional senders auth public key.
763
 *     secret A buffer to return the calculated shared secret. Setting this to
764
 *            NULL can be used to return the secretlen.
765
 *     secretlen Passes in the max size of the secret buffer and returns the
766
 *               secret length.
767
 *     enc A buffer containing the senders ephemeral public key that was returned
768
 *         from dhkem_encap().
769
 *     enclen The length in bytes of enc.
770
 * Returns: 1 If the shared secret is returned or 0 on error.
771
 */
772
static int dhkem_decap(PROV_EC_CTX *ctx,
773
                       unsigned char *secret, size_t *secretlen,
774
                       const unsigned char *enc, size_t enclen)
775
0
{
776
0
    int ret = 0;
777
0
    EC_KEY *sender_ephempubkey = NULL;
778
0
    const OSSL_HPKE_KEM_INFO *info = ctx->info;
779
0
    unsigned char recipient_pub[OSSL_HPKE_MAX_PUBLIC];
780
0
    size_t recipient_publen;
781
0
    size_t encodedpublen = info->Npk;
782
783
0
    if (secret == NULL) {
784
0
        *secretlen = info->Nsecret;
785
0
        return 1;
786
0
    }
787
788
0
    if (*secretlen < info->Nsecret) {
789
0
        ERR_raise_data(ERR_LIB_PROV, PROV_R_BAD_LENGTH, "*secretlen too small");
790
0
        return 0;
791
0
    }
792
0
    if (enclen != encodedpublen) {
793
0
        ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_KEY, "Invalid enc public key");
794
0
        return 0;
795
0
    }
796
797
0
    sender_ephempubkey = eckey_frompub(ctx->recipient_key, enc, enclen);
798
0
    if (sender_ephempubkey == NULL)
799
0
        goto err;
800
0
    if (!ecpubkey_todata(ctx->recipient_key, recipient_pub, &recipient_publen,
801
0
                         sizeof(recipient_pub)))
802
0
        goto err;
803
0
    if (recipient_publen != encodedpublen) {
804
0
        ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_KEY, "Invalid recipient public key");
805
0
        goto err;
806
0
    }
807
808
0
    if (!derive_secret(ctx, secret,
809
0
                       ctx->recipient_key, sender_ephempubkey,
810
0
                       ctx->recipient_key, ctx->sender_authkey,
811
0
                       enc, recipient_pub))
812
0
        goto err;
813
0
    *secretlen = info->Nsecret;
814
0
    ret = 1;
815
0
err:
816
0
    EC_KEY_free(sender_ephempubkey);
817
0
    return ret;
818
0
}
819
820
static int eckem_encapsulate(void *vctx, unsigned char *out, size_t *outlen,
821
                             unsigned char *secret, size_t *secretlen)
822
0
{
823
0
    PROV_EC_CTX *ctx = (PROV_EC_CTX *)vctx;
824
825
0
    switch (ctx->mode) {
826
0
        case KEM_MODE_DHKEM:
827
0
            return dhkem_encap(ctx, out, outlen, secret, secretlen);
828
0
        default:
829
0
            ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_MODE);
830
0
            return -2;
831
0
    }
832
0
}
833
834
static int eckem_decapsulate(void *vctx, unsigned char *out, size_t *outlen,
835
                             const unsigned char *in, size_t inlen)
836
0
{
837
0
    PROV_EC_CTX *ctx = (PROV_EC_CTX *)vctx;
838
839
0
    switch (ctx->mode) {
840
0
        case KEM_MODE_DHKEM:
841
0
            return dhkem_decap(ctx, out, outlen, in, inlen);
842
0
        default:
843
0
            ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_MODE);
844
0
            return -2;
845
0
    }
846
0
}
847
848
const OSSL_DISPATCH ossl_ec_asym_kem_functions[] = {
849
    { OSSL_FUNC_KEM_NEWCTX, (void (*)(void))eckem_newctx },
850
    { OSSL_FUNC_KEM_ENCAPSULATE_INIT,
851
      (void (*)(void))eckem_encapsulate_init },
852
    { OSSL_FUNC_KEM_ENCAPSULATE, (void (*)(void))eckem_encapsulate },
853
    { OSSL_FUNC_KEM_DECAPSULATE_INIT,
854
      (void (*)(void))eckem_decapsulate_init },
855
    { OSSL_FUNC_KEM_DECAPSULATE, (void (*)(void))eckem_decapsulate },
856
    { OSSL_FUNC_KEM_FREECTX, (void (*)(void))eckem_freectx },
857
    { OSSL_FUNC_KEM_SET_CTX_PARAMS,
858
      (void (*)(void))eckem_set_ctx_params },
859
    { OSSL_FUNC_KEM_SETTABLE_CTX_PARAMS,
860
      (void (*)(void))eckem_settable_ctx_params },
861
    { OSSL_FUNC_KEM_AUTH_ENCAPSULATE_INIT,
862
      (void (*)(void))eckem_auth_encapsulate_init },
863
    { OSSL_FUNC_KEM_AUTH_DECAPSULATE_INIT,
864
      (void (*)(void))eckem_auth_decapsulate_init },
865
    OSSL_DISPATCH_END
866
};