Coverage Report

Created: 2025-12-31 06:58

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