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