1/*
2 * Copyright 2020-2023 The OpenSSL Project Authors. All Rights Reserved.
3 *
4 * Licensed under the Apache License 2.0 (the "License").  You may not use
5 * this file except in compliance with the License.  You can obtain a copy
6 * in the file LICENSE in the source distribution or at
7 * https://www.openssl.org/source/license.html
8 */
9
10#include "internal/deprecated.h"
11
12#include <openssl/core_names.h>
13#include <openssl/err.h>
14#include <openssl/ec.h>
15#include "crypto/evp.h"
16#include "crypto/ec.h"
17
18/*
19 * This file is meant to contain functions to provide EVP_PKEY support for EC
20 * keys.
21 */
22
23static ossl_inline
24int evp_pkey_ctx_getset_ecdh_param_checks(const EVP_PKEY_CTX *ctx)
25{
26    if (ctx == NULL || !EVP_PKEY_CTX_IS_DERIVE_OP(ctx)) {
27        ERR_raise(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED);
28        /* Uses the same return values as EVP_PKEY_CTX_ctrl */
29        return -2;
30    }
31
32    /* If key type not EC return error */
33    if (evp_pkey_ctx_is_legacy(ctx)
34        && ctx->pmeth != NULL && ctx->pmeth->pkey_id != EVP_PKEY_EC)
35        return -1;
36
37    return 1;
38}
39
40int EVP_PKEY_CTX_set_ecdh_cofactor_mode(EVP_PKEY_CTX *ctx, int cofactor_mode)
41{
42    int ret;
43    OSSL_PARAM params[2], *p = params;
44
45    ret = evp_pkey_ctx_getset_ecdh_param_checks(ctx);
46    if (ret != 1)
47        return ret;
48
49    /*
50     * Valid input values are:
51     *  * 0 for disable
52     *  * 1 for enable
53     *  * -1 for reset to default for associated priv key
54     */
55    if (cofactor_mode < -1 || cofactor_mode > 1) {
56        /* Uses the same return value of pkey_ec_ctrl() */
57        return -2;
58    }
59
60    *p++ = OSSL_PARAM_construct_int(OSSL_EXCHANGE_PARAM_EC_ECDH_COFACTOR_MODE,
61                                    &cofactor_mode);
62    *p++ = OSSL_PARAM_construct_end();
63
64    ret = evp_pkey_ctx_set_params_strict(ctx, params);
65    if (ret == -2)
66        ERR_raise(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED);
67    return ret;
68}
69
70int EVP_PKEY_CTX_get_ecdh_cofactor_mode(EVP_PKEY_CTX *ctx)
71{
72    int ret, mode;
73    OSSL_PARAM params[2], *p = params;
74
75    ret = evp_pkey_ctx_getset_ecdh_param_checks(ctx);
76    if (ret != 1)
77        return ret;
78
79    *p++ = OSSL_PARAM_construct_int(OSSL_EXCHANGE_PARAM_EC_ECDH_COFACTOR_MODE,
80                                    &mode);
81    *p++ = OSSL_PARAM_construct_end();
82
83    ret = evp_pkey_ctx_get_params_strict(ctx, params);
84
85    switch (ret) {
86    case -2:
87        ERR_raise(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED);
88        break;
89    case 1:
90        ret = mode;
91        if (mode < 0 || mode > 1) {
92            /*
93             * The provider should return either 0 or 1, any other value is a
94             * provider error.
95             */
96            ret = -1;
97        }
98        break;
99    default:
100        ret = -1;
101        break;
102    }
103
104    return ret;
105}
106
107/*
108 * This one is currently implemented as an EVP_PKEY_CTX_ctrl() wrapper,
109 * simply because that's easier.
110 */
111int EVP_PKEY_CTX_set_ecdh_kdf_type(EVP_PKEY_CTX *ctx, int kdf)
112{
113    return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_EC, EVP_PKEY_OP_DERIVE,
114                             EVP_PKEY_CTRL_EC_KDF_TYPE, kdf, NULL);
115}
116
117/*
118 * This one is currently implemented as an EVP_PKEY_CTX_ctrl() wrapper,
119 * simply because that's easier.
120 */
121int EVP_PKEY_CTX_get_ecdh_kdf_type(EVP_PKEY_CTX *ctx)
122{
123    return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_EC, EVP_PKEY_OP_DERIVE,
124                             EVP_PKEY_CTRL_EC_KDF_TYPE, -2, NULL);
125}
126
127/*
128 * This one is currently implemented as an EVP_PKEY_CTX_ctrl() wrapper,
129 * simply because that's easier.
130 */
131int EVP_PKEY_CTX_set_ecdh_kdf_md(EVP_PKEY_CTX *ctx, const EVP_MD *md)
132{
133    return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_EC, EVP_PKEY_OP_DERIVE,
134                             EVP_PKEY_CTRL_EC_KDF_MD, 0, (void *)(md));
135}
136
137/*
138 * This one is currently implemented as an EVP_PKEY_CTX_ctrl() wrapper,
139 * simply because that's easier.
140 */
141int EVP_PKEY_CTX_get_ecdh_kdf_md(EVP_PKEY_CTX *ctx, const EVP_MD **pmd)
142{
143    return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_EC, EVP_PKEY_OP_DERIVE,
144                             EVP_PKEY_CTRL_GET_EC_KDF_MD, 0, (void *)(pmd));
145}
146
147int EVP_PKEY_CTX_set_ecdh_kdf_outlen(EVP_PKEY_CTX *ctx, int outlen)
148{
149    int ret;
150    size_t len = outlen;
151    OSSL_PARAM params[2], *p = params;
152
153    ret = evp_pkey_ctx_getset_ecdh_param_checks(ctx);
154    if (ret != 1)
155        return ret;
156
157    if (outlen <= 0) {
158        /*
159         * This would ideally be -1 or 0, but we have to retain compatibility
160         * with legacy behaviour of EVP_PKEY_CTX_ctrl() which returned -2 if
161         * in <= 0
162         */
163        return -2;
164    }
165
166    *p++ = OSSL_PARAM_construct_size_t(OSSL_EXCHANGE_PARAM_KDF_OUTLEN,
167                                       &len);
168    *p++ = OSSL_PARAM_construct_end();
169
170    ret = evp_pkey_ctx_set_params_strict(ctx, params);
171    if (ret == -2)
172        ERR_raise(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED);
173    return ret;
174}
175
176int EVP_PKEY_CTX_get_ecdh_kdf_outlen(EVP_PKEY_CTX *ctx, int *plen)
177{
178    size_t len = UINT_MAX;
179    int ret;
180    OSSL_PARAM params[2], *p = params;
181
182    ret = evp_pkey_ctx_getset_ecdh_param_checks(ctx);
183    if (ret != 1)
184        return ret;
185
186    *p++ = OSSL_PARAM_construct_size_t(OSSL_EXCHANGE_PARAM_KDF_OUTLEN,
187                                       &len);
188    *p++ = OSSL_PARAM_construct_end();
189
190    ret = evp_pkey_ctx_get_params_strict(ctx, params);
191
192    switch (ret) {
193    case -2:
194        ERR_raise(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED);
195        break;
196    case 1:
197        if (len <= INT_MAX)
198            *plen = (int)len;
199        else
200            ret = -1;
201        break;
202    default:
203        ret = -1;
204        break;
205    }
206
207    return ret;
208}
209
210int EVP_PKEY_CTX_set0_ecdh_kdf_ukm(EVP_PKEY_CTX *ctx, unsigned char *ukm, int len)
211{
212    int ret;
213    OSSL_PARAM params[2], *p = params;
214
215    ret = evp_pkey_ctx_getset_ecdh_param_checks(ctx);
216    if (ret != 1)
217        return ret;
218
219    *p++ = OSSL_PARAM_construct_octet_string(OSSL_EXCHANGE_PARAM_KDF_UKM,
220                                            /*
221                                             * Cast away the const. This is read
222                                             * only so should be safe
223                                             */
224                                            (void *)ukm,
225                                            (size_t)len);
226    *p++ = OSSL_PARAM_construct_end();
227
228    ret = evp_pkey_ctx_set_params_strict(ctx, params);
229
230    switch (ret) {
231    case -2:
232        ERR_raise(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED);
233        break;
234    case 1:
235        OPENSSL_free(ukm);
236        break;
237    }
238
239    return ret;
240}
241
242#ifndef OPENSSL_NO_DEPRECATED_3_0
243int EVP_PKEY_CTX_get0_ecdh_kdf_ukm(EVP_PKEY_CTX *ctx, unsigned char **pukm)
244{
245    size_t ukmlen;
246    int ret;
247    OSSL_PARAM params[2], *p = params;
248
249    ret = evp_pkey_ctx_getset_ecdh_param_checks(ctx);
250    if (ret != 1)
251        return ret;
252
253    *p++ = OSSL_PARAM_construct_octet_ptr(OSSL_EXCHANGE_PARAM_KDF_UKM,
254                                          (void **)pukm, 0);
255    *p++ = OSSL_PARAM_construct_end();
256
257    ret = evp_pkey_ctx_get_params_strict(ctx, params);
258
259    switch (ret) {
260    case -2:
261        ERR_raise(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED);
262        break;
263    case 1:
264        ret = -1;
265        ukmlen = params[0].return_size;
266        if (ukmlen <= INT_MAX)
267            ret = (int)ukmlen;
268        break;
269    default:
270        ret = -1;
271        break;
272    }
273
274    return ret;
275}
276#endif
277
278#ifndef FIPS_MODULE
279/*
280 * This one is currently implemented as an EVP_PKEY_CTX_ctrl() wrapper,
281 * simply because that's easier.
282 * ASN1_OBJECT (which would be converted to text internally)?
283 */
284int EVP_PKEY_CTX_set_ec_paramgen_curve_nid(EVP_PKEY_CTX *ctx, int nid)
285{
286    int keytype = nid == EVP_PKEY_SM2 ? EVP_PKEY_SM2 : EVP_PKEY_EC;
287
288    return EVP_PKEY_CTX_ctrl(ctx, keytype, EVP_PKEY_OP_TYPE_GEN,
289                             EVP_PKEY_CTRL_EC_PARAMGEN_CURVE_NID,
290                             nid, NULL);
291}
292
293/*
294 * This one is currently implemented as an EVP_PKEY_CTX_ctrl() wrapper,
295 * simply because that's easier.
296 */
297int EVP_PKEY_CTX_set_ec_param_enc(EVP_PKEY_CTX *ctx, int param_enc)
298{
299    return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_EC, EVP_PKEY_OP_TYPE_GEN,
300                             EVP_PKEY_CTRL_EC_PARAM_ENC, param_enc, NULL);
301}
302#endif
303