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