Coverage Report

Created: 2025-06-13 06:55

/src/openssl/providers/implementations/exchange/ecdh_exch.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright 2020-2024 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
 * ECDH low level APIs are deprecated for public use, but still ok for
12
 * internal use.
13
 */
14
#include "internal/deprecated.h"
15
16
#include <string.h>
17
#include <openssl/crypto.h>
18
#include <openssl/evp.h>
19
#include <openssl/core_dispatch.h>
20
#include <openssl/core_names.h>
21
#include <openssl/ec.h>
22
#include <openssl/params.h>
23
#include <openssl/err.h>
24
#include <openssl/proverr.h>
25
#include "prov/provider_ctx.h"
26
#include "prov/providercommon.h"
27
#include "prov/implementations.h"
28
#include "prov/securitycheck.h"
29
#include "crypto/ec.h" /* ossl_ecdh_kdf_X9_63() */
30
31
static OSSL_FUNC_keyexch_newctx_fn ecdh_newctx;
32
static OSSL_FUNC_keyexch_init_fn ecdh_init;
33
static OSSL_FUNC_keyexch_set_peer_fn ecdh_set_peer;
34
static OSSL_FUNC_keyexch_derive_fn ecdh_derive;
35
static OSSL_FUNC_keyexch_freectx_fn ecdh_freectx;
36
static OSSL_FUNC_keyexch_dupctx_fn ecdh_dupctx;
37
static OSSL_FUNC_keyexch_set_ctx_params_fn ecdh_set_ctx_params;
38
static OSSL_FUNC_keyexch_settable_ctx_params_fn ecdh_settable_ctx_params;
39
static OSSL_FUNC_keyexch_get_ctx_params_fn ecdh_get_ctx_params;
40
static OSSL_FUNC_keyexch_gettable_ctx_params_fn ecdh_gettable_ctx_params;
41
42
enum kdf_type {
43
    PROV_ECDH_KDF_NONE = 0,
44
    PROV_ECDH_KDF_X9_63
45
};
46
47
/*
48
 * What's passed as an actual key is defined by the KEYMGMT interface.
49
 * We happen to know that our KEYMGMT simply passes EC_KEY structures, so
50
 * we use that here too.
51
 */
52
53
typedef struct {
54
    OSSL_LIB_CTX *libctx;
55
56
    EC_KEY *k;
57
    EC_KEY *peerk;
58
59
    /*
60
     * ECDH cofactor mode:
61
     *
62
     *  . 0  disabled
63
     *  . 1  enabled
64
     *  . -1 use cofactor mode set for k
65
     */
66
    int cofactor_mode;
67
68
    /************
69
     * ECDH KDF *
70
     ************/
71
    /* KDF (if any) to use for ECDH */
72
    enum kdf_type kdf_type;
73
    /* Message digest to use for key derivation */
74
    EVP_MD *kdf_md;
75
    /* User key material */
76
    unsigned char *kdf_ukm;
77
    size_t kdf_ukmlen;
78
    /* KDF output length */
79
    size_t kdf_outlen;
80
    OSSL_FIPS_IND_DECLARE
81
} PROV_ECDH_CTX;
82
83
static
84
void *ecdh_newctx(void *provctx)
85
0
{
86
0
    PROV_ECDH_CTX *pectx;
87
88
0
    if (!ossl_prov_is_running())
89
0
        return NULL;
90
91
0
    pectx = OPENSSL_zalloc(sizeof(*pectx));
92
0
    if (pectx == NULL)
93
0
        return NULL;
94
95
0
    pectx->libctx = PROV_LIBCTX_OF(provctx);
96
0
    pectx->cofactor_mode = -1;
97
0
    pectx->kdf_type = PROV_ECDH_KDF_NONE;
98
0
    OSSL_FIPS_IND_INIT(pectx)
99
100
0
    return (void *)pectx;
101
0
}
102
103
static
104
int ecdh_init(void *vpecdhctx, void *vecdh, const OSSL_PARAM params[])
105
0
{
106
0
    PROV_ECDH_CTX *pecdhctx = (PROV_ECDH_CTX *)vpecdhctx;
107
108
0
    if (!ossl_prov_is_running()
109
0
            || pecdhctx == NULL
110
0
            || vecdh == NULL
111
0
            || (EC_KEY_get0_group(vecdh) == NULL)
112
0
            || !EC_KEY_up_ref(vecdh))
113
0
        return 0;
114
0
    EC_KEY_free(pecdhctx->k);
115
0
    pecdhctx->k = vecdh;
116
0
    pecdhctx->cofactor_mode = -1;
117
0
    pecdhctx->kdf_type = PROV_ECDH_KDF_NONE;
118
119
0
    OSSL_FIPS_IND_SET_APPROVED(pecdhctx)
120
0
    if (!ecdh_set_ctx_params(pecdhctx, params))
121
0
        return 0;
122
#ifdef FIPS_MODULE
123
    if (!ossl_fips_ind_ec_key_check(OSSL_FIPS_IND_GET(pecdhctx),
124
                                    OSSL_FIPS_IND_SETTABLE0, pecdhctx->libctx,
125
                                    EC_KEY_get0_group(vecdh), "ECDH Init", 1))
126
        return 0;
127
#endif
128
0
    return 1;
129
0
}
130
131
static
132
int ecdh_match_params(const EC_KEY *priv, const EC_KEY *peer)
133
0
{
134
0
    int ret;
135
0
    BN_CTX *ctx = NULL;
136
0
    const EC_GROUP *group_priv = EC_KEY_get0_group(priv);
137
0
    const EC_GROUP *group_peer = EC_KEY_get0_group(peer);
138
139
0
    ctx = BN_CTX_new_ex(ossl_ec_key_get_libctx(priv));
140
0
    if (ctx == NULL) {
141
0
        ERR_raise(ERR_LIB_PROV, ERR_R_BN_LIB);
142
0
        return 0;
143
0
    }
144
0
    ret = group_priv != NULL
145
0
          && group_peer != NULL
146
0
          && EC_GROUP_cmp(group_priv, group_peer, ctx) == 0;
147
0
    if (!ret)
148
0
        ERR_raise(ERR_LIB_PROV, PROV_R_MISMATCHING_DOMAIN_PARAMETERS);
149
0
    BN_CTX_free(ctx);
150
0
    return ret;
151
0
}
152
153
static
154
int ecdh_set_peer(void *vpecdhctx, void *vecdh)
155
0
{
156
0
    PROV_ECDH_CTX *pecdhctx = (PROV_ECDH_CTX *)vpecdhctx;
157
158
0
    if (!ossl_prov_is_running()
159
0
            || pecdhctx == NULL
160
0
            || vecdh == NULL
161
0
            || !ecdh_match_params(pecdhctx->k, vecdh))
162
0
        return 0;
163
#ifdef FIPS_MODULE
164
    if (!ossl_fips_ind_ec_key_check(OSSL_FIPS_IND_GET(pecdhctx),
165
                                    OSSL_FIPS_IND_SETTABLE0, pecdhctx->libctx,
166
                                    EC_KEY_get0_group(vecdh), "ECDH Set Peer",
167
                                    1))
168
        return 0;
169
#endif
170
0
    if (!EC_KEY_up_ref(vecdh))
171
0
        return 0;
172
173
0
    EC_KEY_free(pecdhctx->peerk);
174
0
    pecdhctx->peerk = vecdh;
175
0
    return 1;
176
0
}
177
178
static
179
void ecdh_freectx(void *vpecdhctx)
180
0
{
181
0
    PROV_ECDH_CTX *pecdhctx = (PROV_ECDH_CTX *)vpecdhctx;
182
183
0
    EC_KEY_free(pecdhctx->k);
184
0
    EC_KEY_free(pecdhctx->peerk);
185
186
0
    EVP_MD_free(pecdhctx->kdf_md);
187
0
    OPENSSL_clear_free(pecdhctx->kdf_ukm, pecdhctx->kdf_ukmlen);
188
189
0
    OPENSSL_free(pecdhctx);
190
0
}
191
192
static
193
void *ecdh_dupctx(void *vpecdhctx)
194
0
{
195
0
    PROV_ECDH_CTX *srcctx = (PROV_ECDH_CTX *)vpecdhctx;
196
0
    PROV_ECDH_CTX *dstctx;
197
198
0
    if (!ossl_prov_is_running())
199
0
        return NULL;
200
201
0
    dstctx = OPENSSL_zalloc(sizeof(*srcctx));
202
0
    if (dstctx == NULL)
203
0
        return NULL;
204
205
0
    *dstctx = *srcctx;
206
207
    /* clear all pointers */
208
209
0
    dstctx->k= NULL;
210
0
    dstctx->peerk = NULL;
211
0
    dstctx->kdf_md = NULL;
212
0
    dstctx->kdf_ukm = NULL;
213
214
    /* up-ref all ref-counted objects referenced in dstctx */
215
216
0
    if (srcctx->k != NULL && !EC_KEY_up_ref(srcctx->k))
217
0
        goto err;
218
0
    else
219
0
        dstctx->k = srcctx->k;
220
221
0
    if (srcctx->peerk != NULL && !EC_KEY_up_ref(srcctx->peerk))
222
0
        goto err;
223
0
    else
224
0
        dstctx->peerk = srcctx->peerk;
225
226
0
    if (srcctx->kdf_md != NULL && !EVP_MD_up_ref(srcctx->kdf_md))
227
0
        goto err;
228
0
    else
229
0
        dstctx->kdf_md = srcctx->kdf_md;
230
231
    /* Duplicate UKM data if present */
232
0
    if (srcctx->kdf_ukm != NULL && srcctx->kdf_ukmlen > 0) {
233
0
        dstctx->kdf_ukm = OPENSSL_memdup(srcctx->kdf_ukm,
234
0
                                         srcctx->kdf_ukmlen);
235
0
        if (dstctx->kdf_ukm == NULL)
236
0
            goto err;
237
0
    }
238
239
0
    return dstctx;
240
241
0
 err:
242
0
    ecdh_freectx(dstctx);
243
0
    return NULL;
244
0
}
245
246
static
247
int ecdh_set_ctx_params(void *vpecdhctx, const OSSL_PARAM params[])
248
0
{
249
0
    char name[80] = { '\0' }; /* should be big enough */
250
0
    char *str = NULL;
251
0
    PROV_ECDH_CTX *pectx = (PROV_ECDH_CTX *)vpecdhctx;
252
0
    const OSSL_PARAM *p;
253
254
0
    if (pectx == NULL)
255
0
        return 0;
256
0
    if (ossl_param_is_empty(params))
257
0
        return 1;
258
259
0
    if (!OSSL_FIPS_IND_SET_CTX_PARAM(pectx, OSSL_FIPS_IND_SETTABLE0, params,
260
0
                                     OSSL_EXCHANGE_PARAM_FIPS_KEY_CHECK))
261
0
        return 0;
262
0
    if (!OSSL_FIPS_IND_SET_CTX_PARAM(pectx, OSSL_FIPS_IND_SETTABLE1, params,
263
0
                                     OSSL_EXCHANGE_PARAM_FIPS_DIGEST_CHECK))
264
0
        return 0;
265
0
    if (!OSSL_FIPS_IND_SET_CTX_PARAM(pectx, OSSL_FIPS_IND_SETTABLE2, params,
266
0
                                     OSSL_EXCHANGE_PARAM_FIPS_ECDH_COFACTOR_CHECK))
267
0
        return 0;
268
269
0
    p = OSSL_PARAM_locate_const(params, OSSL_EXCHANGE_PARAM_EC_ECDH_COFACTOR_MODE);
270
0
    if (p != NULL) {
271
0
        int mode;
272
273
0
        if (!OSSL_PARAM_get_int(p, &mode))
274
0
            return 0;
275
276
0
        if (mode < -1 || mode > 1)
277
0
            return 0;
278
279
0
        pectx->cofactor_mode = mode;
280
0
    }
281
282
0
    p = OSSL_PARAM_locate_const(params, OSSL_EXCHANGE_PARAM_KDF_TYPE);
283
0
    if (p != NULL) {
284
0
        str = name;
285
0
        if (!OSSL_PARAM_get_utf8_string(p, &str, sizeof(name)))
286
0
            return 0;
287
288
0
        if (name[0] == '\0')
289
0
            pectx->kdf_type = PROV_ECDH_KDF_NONE;
290
0
        else if (strcmp(name, OSSL_KDF_NAME_X963KDF) == 0)
291
0
            pectx->kdf_type = PROV_ECDH_KDF_X9_63;
292
0
        else
293
0
            return 0;
294
0
    }
295
296
0
    p = OSSL_PARAM_locate_const(params, OSSL_EXCHANGE_PARAM_KDF_DIGEST);
297
0
    if (p != NULL) {
298
0
        char mdprops[80] = { '\0' }; /* should be big enough */
299
300
0
        str = name;
301
0
        if (!OSSL_PARAM_get_utf8_string(p, &str, sizeof(name)))
302
0
            return 0;
303
304
0
        str = mdprops;
305
0
        p = OSSL_PARAM_locate_const(params,
306
0
                                    OSSL_EXCHANGE_PARAM_KDF_DIGEST_PROPS);
307
308
0
        if (p != NULL) {
309
0
            if (!OSSL_PARAM_get_utf8_string(p, &str, sizeof(mdprops)))
310
0
                return 0;
311
0
        }
312
313
0
        EVP_MD_free(pectx->kdf_md);
314
0
        pectx->kdf_md = EVP_MD_fetch(pectx->libctx, name, mdprops);
315
0
        if (pectx->kdf_md == NULL)
316
0
            return 0;
317
        /* XOF digests are not allowed */
318
0
        if (EVP_MD_xof(pectx->kdf_md)) {
319
0
            ERR_raise(ERR_LIB_PROV, PROV_R_XOF_DIGESTS_NOT_ALLOWED);
320
0
            return 0;
321
0
        }
322
#ifdef FIPS_MODULE
323
        if (!ossl_fips_ind_digest_exch_check(OSSL_FIPS_IND_GET(pectx),
324
                                             OSSL_FIPS_IND_SETTABLE1, pectx->libctx,
325
                                             pectx->kdf_md, "ECDH Set Ctx")) {
326
            EVP_MD_free(pectx->kdf_md);
327
            pectx->kdf_md = NULL;
328
            return 0;
329
        }
330
#endif
331
0
    }
332
333
0
    p = OSSL_PARAM_locate_const(params, OSSL_EXCHANGE_PARAM_KDF_OUTLEN);
334
0
    if (p != NULL) {
335
0
        size_t outlen;
336
337
0
        if (!OSSL_PARAM_get_size_t(p, &outlen))
338
0
            return 0;
339
0
        pectx->kdf_outlen = outlen;
340
0
    }
341
342
0
    p = OSSL_PARAM_locate_const(params, OSSL_EXCHANGE_PARAM_KDF_UKM);
343
0
    if (p != NULL) {
344
0
        void *tmp_ukm = NULL;
345
0
        size_t tmp_ukmlen;
346
347
0
        if (!OSSL_PARAM_get_octet_string(p, &tmp_ukm, 0, &tmp_ukmlen))
348
0
            return 0;
349
0
        OPENSSL_free(pectx->kdf_ukm);
350
0
        pectx->kdf_ukm = tmp_ukm;
351
0
        pectx->kdf_ukmlen = tmp_ukmlen;
352
0
    }
353
354
0
    return 1;
355
0
}
356
357
static const OSSL_PARAM known_settable_ctx_params[] = {
358
    OSSL_PARAM_int(OSSL_EXCHANGE_PARAM_EC_ECDH_COFACTOR_MODE, NULL),
359
    OSSL_PARAM_utf8_string(OSSL_EXCHANGE_PARAM_KDF_TYPE, NULL, 0),
360
    OSSL_PARAM_utf8_string(OSSL_EXCHANGE_PARAM_KDF_DIGEST, NULL, 0),
361
    OSSL_PARAM_utf8_string(OSSL_EXCHANGE_PARAM_KDF_DIGEST_PROPS, NULL, 0),
362
    OSSL_PARAM_size_t(OSSL_EXCHANGE_PARAM_KDF_OUTLEN, NULL),
363
    OSSL_PARAM_octet_string(OSSL_EXCHANGE_PARAM_KDF_UKM, NULL, 0),
364
    OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_EXCHANGE_PARAM_FIPS_KEY_CHECK)
365
    OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_EXCHANGE_PARAM_FIPS_DIGEST_CHECK)
366
    OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_EXCHANGE_PARAM_FIPS_ECDH_COFACTOR_CHECK)
367
    OSSL_PARAM_END
368
};
369
370
static
371
const OSSL_PARAM *ecdh_settable_ctx_params(ossl_unused void *vpecdhctx,
372
                                           ossl_unused void *provctx)
373
0
{
374
0
    return known_settable_ctx_params;
375
0
}
376
377
static
378
int ecdh_get_ctx_params(void *vpecdhctx, OSSL_PARAM params[])
379
0
{
380
0
    PROV_ECDH_CTX *pectx = (PROV_ECDH_CTX *)vpecdhctx;
381
0
    OSSL_PARAM *p;
382
383
0
    if (pectx == NULL)
384
0
        return 0;
385
386
0
    p = OSSL_PARAM_locate(params, OSSL_EXCHANGE_PARAM_EC_ECDH_COFACTOR_MODE);
387
0
    if (p != NULL) {
388
0
        int mode = pectx->cofactor_mode;
389
390
0
        if (mode == -1) {
391
            /* check what is the default for pecdhctx->k */
392
0
            mode = EC_KEY_get_flags(pectx->k) & EC_FLAG_COFACTOR_ECDH ? 1 : 0;
393
0
        }
394
395
0
        if (!OSSL_PARAM_set_int(p, mode))
396
0
            return 0;
397
0
    }
398
399
0
    p = OSSL_PARAM_locate(params, OSSL_EXCHANGE_PARAM_KDF_TYPE);
400
0
    if (p != NULL) {
401
0
        const char *kdf_type = NULL;
402
403
0
        switch (pectx->kdf_type) {
404
0
            case PROV_ECDH_KDF_NONE:
405
0
                kdf_type = "";
406
0
                break;
407
0
            case PROV_ECDH_KDF_X9_63:
408
0
                kdf_type = OSSL_KDF_NAME_X963KDF;
409
0
                break;
410
0
            default:
411
0
                return 0;
412
0
        }
413
414
0
        if (!OSSL_PARAM_set_utf8_string(p, kdf_type))
415
0
            return 0;
416
0
    }
417
418
0
    p = OSSL_PARAM_locate(params, OSSL_EXCHANGE_PARAM_KDF_DIGEST);
419
0
    if (p != NULL
420
0
            && !OSSL_PARAM_set_utf8_string(p, pectx->kdf_md == NULL
421
0
                                           ? ""
422
0
                                           : EVP_MD_get0_name(pectx->kdf_md))) {
423
0
        return 0;
424
0
    }
425
426
0
    p = OSSL_PARAM_locate(params, OSSL_EXCHANGE_PARAM_KDF_OUTLEN);
427
0
    if (p != NULL && !OSSL_PARAM_set_size_t(p, pectx->kdf_outlen))
428
0
        return 0;
429
430
0
    p = OSSL_PARAM_locate(params, OSSL_EXCHANGE_PARAM_KDF_UKM);
431
0
    if (p != NULL &&
432
0
        !OSSL_PARAM_set_octet_ptr(p, pectx->kdf_ukm, pectx->kdf_ukmlen))
433
0
        return 0;
434
0
    if (!OSSL_FIPS_IND_GET_CTX_PARAM(pectx, params))
435
0
        return 0;
436
0
    return 1;
437
0
}
438
439
static const OSSL_PARAM known_gettable_ctx_params[] = {
440
    OSSL_PARAM_int(OSSL_EXCHANGE_PARAM_EC_ECDH_COFACTOR_MODE, NULL),
441
    OSSL_PARAM_utf8_string(OSSL_EXCHANGE_PARAM_KDF_TYPE, NULL, 0),
442
    OSSL_PARAM_utf8_string(OSSL_EXCHANGE_PARAM_KDF_DIGEST, NULL, 0),
443
    OSSL_PARAM_size_t(OSSL_EXCHANGE_PARAM_KDF_OUTLEN, NULL),
444
    OSSL_PARAM_DEFN(OSSL_EXCHANGE_PARAM_KDF_UKM, OSSL_PARAM_OCTET_PTR,
445
                    NULL, 0),
446
    OSSL_FIPS_IND_GETTABLE_CTX_PARAM()
447
    OSSL_PARAM_END
448
};
449
450
static
451
const OSSL_PARAM *ecdh_gettable_ctx_params(ossl_unused void *vpecdhctx,
452
                                           ossl_unused void *provctx)
453
0
{
454
0
    return known_gettable_ctx_params;
455
0
}
456
457
static ossl_inline
458
size_t ecdh_size(const EC_KEY *k)
459
0
{
460
0
    size_t degree = 0;
461
0
    const EC_GROUP *group;
462
463
0
    if (k == NULL
464
0
            || (group = EC_KEY_get0_group(k)) == NULL)
465
0
        return 0;
466
467
0
    degree = EC_GROUP_get_degree(group);
468
469
0
    return (degree + 7) / 8;
470
0
}
471
472
static ossl_inline
473
int ecdh_plain_derive(void *vpecdhctx, unsigned char *secret,
474
                      size_t *psecretlen, size_t outlen)
475
0
{
476
0
    PROV_ECDH_CTX *pecdhctx = (PROV_ECDH_CTX *)vpecdhctx;
477
0
    int retlen, ret = 0;
478
0
    size_t ecdhsize, size;
479
0
    const EC_POINT *ppubkey = NULL;
480
0
    EC_KEY *privk = NULL;
481
0
    const EC_GROUP *group;
482
0
    const BIGNUM *cofactor;
483
0
    int key_cofactor_mode;
484
0
    int has_cofactor;
485
#ifdef FIPS_MODULE
486
    int cofactor_approved = 0;
487
#endif
488
489
0
    if (pecdhctx->k == NULL || pecdhctx->peerk == NULL) {
490
0
        ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY);
491
0
        return 0;
492
0
    }
493
494
0
    ecdhsize = ecdh_size(pecdhctx->k);
495
0
    if (secret == NULL) {
496
0
        *psecretlen = ecdhsize;
497
0
        return 1;
498
0
    }
499
500
0
    if ((group = EC_KEY_get0_group(pecdhctx->k)) == NULL
501
0
            || (cofactor = EC_GROUP_get0_cofactor(group)) == NULL)
502
0
        return 0;
503
504
0
    has_cofactor = !BN_is_one(cofactor);
505
506
    /*
507
     * NB: unlike PKCS#3 DH, if outlen is less than maximum size this is not
508
     * an error, the result is truncated.
509
     */
510
0
    size = outlen < ecdhsize ? outlen : ecdhsize;
511
512
    /*
513
     * The ctx->cofactor_mode flag has precedence over the
514
     * cofactor_mode flag set on ctx->k.
515
     *
516
     * - if ctx->cofactor_mode == -1, use ctx->k directly
517
     * - if ctx->cofactor_mode == key_cofactor_mode, use ctx->k directly
518
     * - if ctx->cofactor_mode != key_cofactor_mode:
519
     *     - if ctx->k->cofactor == 1, the cofactor_mode flag is irrelevant, use
520
     *          ctx->k directly
521
     *     - if ctx->k->cofactor != 1, use a duplicate of ctx->k with the flag
522
     *          set to ctx->cofactor_mode
523
     */
524
0
    key_cofactor_mode =
525
0
        (EC_KEY_get_flags(pecdhctx->k) & EC_FLAG_COFACTOR_ECDH) ? 1 : 0;
526
0
    if (pecdhctx->cofactor_mode != -1
527
0
            && pecdhctx->cofactor_mode != key_cofactor_mode
528
0
            && has_cofactor) {
529
0
        if ((privk = EC_KEY_dup(pecdhctx->k)) == NULL)
530
0
            return 0;
531
532
0
        if (pecdhctx->cofactor_mode == 1) {
533
0
            EC_KEY_set_flags(privk, EC_FLAG_COFACTOR_ECDH);
534
#ifdef FIPS_MODULE
535
            cofactor_approved = 1;
536
#endif
537
0
        } else {
538
0
            EC_KEY_clear_flags(privk, EC_FLAG_COFACTOR_ECDH);
539
0
        }
540
0
    } else {
541
0
        privk = pecdhctx->k;
542
#ifdef FIPS_MODULE
543
        cofactor_approved = key_cofactor_mode;
544
#endif
545
0
    }
546
547
#ifdef FIPS_MODULE
548
    /*
549
     * SP800-56A r3 Section 5.7.1.2 requires ECC Cofactor DH to be used.
550
     * This applies to the 'B' and 'K' curves that have cofactors that are not 1.
551
     */
552
    if (has_cofactor && !cofactor_approved) {
553
        if (!OSSL_FIPS_IND_ON_UNAPPROVED(pecdhctx, OSSL_FIPS_IND_SETTABLE2,
554
                                         pecdhctx->libctx, "ECDH", "Cofactor",
555
                                         ossl_fips_config_ecdh_cofactor_check)) {
556
            ERR_raise(ERR_LIB_PROV, PROV_R_COFACTOR_REQUIRED);
557
            goto end;
558
        }
559
    }
560
#endif
561
562
0
    ppubkey = EC_KEY_get0_public_key(pecdhctx->peerk);
563
564
0
    retlen = ECDH_compute_key(secret, size, ppubkey, privk, NULL);
565
566
0
    if (retlen <= 0)
567
0
        goto end;
568
569
0
    *psecretlen = retlen;
570
0
    ret = 1;
571
572
0
 end:
573
0
    if (privk != pecdhctx->k)
574
0
        EC_KEY_free(privk);
575
0
    return ret;
576
0
}
577
578
static ossl_inline
579
int ecdh_X9_63_kdf_derive(void *vpecdhctx, unsigned char *secret,
580
                          size_t *psecretlen, size_t outlen)
581
0
{
582
0
    PROV_ECDH_CTX *pecdhctx = (PROV_ECDH_CTX *)vpecdhctx;
583
0
    unsigned char *stmp = NULL;
584
0
    size_t stmplen;
585
0
    int ret = 0;
586
587
0
    if (secret == NULL) {
588
0
        *psecretlen = pecdhctx->kdf_outlen;
589
0
        return 1;
590
0
    }
591
592
0
    if (pecdhctx->kdf_outlen > outlen) {
593
0
        ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL);
594
0
        return 0;
595
0
    }
596
0
    if (!ecdh_plain_derive(vpecdhctx, NULL, &stmplen, 0))
597
0
        return 0;
598
0
    if ((stmp = OPENSSL_secure_malloc(stmplen)) == NULL)
599
0
        return 0;
600
0
    if (!ecdh_plain_derive(vpecdhctx, stmp, &stmplen, stmplen))
601
0
        goto err;
602
603
    /* Do KDF stuff */
604
0
    if (!ossl_ecdh_kdf_X9_63(secret, pecdhctx->kdf_outlen,
605
0
                             stmp, stmplen,
606
0
                             pecdhctx->kdf_ukm,
607
0
                             pecdhctx->kdf_ukmlen,
608
0
                             pecdhctx->kdf_md,
609
0
                             pecdhctx->libctx, NULL))
610
0
        goto err;
611
0
    *psecretlen = pecdhctx->kdf_outlen;
612
0
    ret = 1;
613
614
0
 err:
615
0
    OPENSSL_secure_clear_free(stmp, stmplen);
616
0
    return ret;
617
0
}
618
619
static
620
int ecdh_derive(void *vpecdhctx, unsigned char *secret,
621
                size_t *psecretlen, size_t outlen)
622
0
{
623
0
    PROV_ECDH_CTX *pecdhctx = (PROV_ECDH_CTX *)vpecdhctx;
624
625
0
    switch (pecdhctx->kdf_type) {
626
0
        case PROV_ECDH_KDF_NONE:
627
0
            return ecdh_plain_derive(vpecdhctx, secret, psecretlen, outlen);
628
0
        case PROV_ECDH_KDF_X9_63:
629
0
            return ecdh_X9_63_kdf_derive(vpecdhctx, secret, psecretlen, outlen);
630
0
        default:
631
0
            break;
632
0
    }
633
0
    return 0;
634
0
}
635
636
const OSSL_DISPATCH ossl_ecdh_keyexch_functions[] = {
637
    { OSSL_FUNC_KEYEXCH_NEWCTX, (void (*)(void))ecdh_newctx },
638
    { OSSL_FUNC_KEYEXCH_INIT, (void (*)(void))ecdh_init },
639
    { OSSL_FUNC_KEYEXCH_DERIVE, (void (*)(void))ecdh_derive },
640
    { OSSL_FUNC_KEYEXCH_SET_PEER, (void (*)(void))ecdh_set_peer },
641
    { OSSL_FUNC_KEYEXCH_FREECTX, (void (*)(void))ecdh_freectx },
642
    { OSSL_FUNC_KEYEXCH_DUPCTX, (void (*)(void))ecdh_dupctx },
643
    { OSSL_FUNC_KEYEXCH_SET_CTX_PARAMS, (void (*)(void))ecdh_set_ctx_params },
644
    { OSSL_FUNC_KEYEXCH_SETTABLE_CTX_PARAMS,
645
      (void (*)(void))ecdh_settable_ctx_params },
646
    { OSSL_FUNC_KEYEXCH_GET_CTX_PARAMS, (void (*)(void))ecdh_get_ctx_params },
647
    { OSSL_FUNC_KEYEXCH_GETTABLE_CTX_PARAMS,
648
      (void (*)(void))ecdh_gettable_ctx_params },
649
    OSSL_DISPATCH_END
650
};