12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
23c4b2390SSalvatore Benedetto /* ECDH key-agreement protocol
33c4b2390SSalvatore Benedetto *
43c4b2390SSalvatore Benedetto * Copyright (c) 2016, Intel Corporation
53c4b2390SSalvatore Benedetto * Authors: Salvator Benedetto <salvatore.benedetto@intel.com>
63c4b2390SSalvatore Benedetto */
73c4b2390SSalvatore Benedetto
83c4b2390SSalvatore Benedetto #include <linux/module.h>
9a745d3acSDaniele Alessandrelli #include <crypto/internal/ecc.h>
103c4b2390SSalvatore Benedetto #include <crypto/internal/kpp.h>
113c4b2390SSalvatore Benedetto #include <crypto/kpp.h>
123c4b2390SSalvatore Benedetto #include <crypto/ecdh.h>
133c4b2390SSalvatore Benedetto #include <linux/scatterlist.h>
143c4b2390SSalvatore Benedetto
153c4b2390SSalvatore Benedetto struct ecdh_ctx {
163c4b2390SSalvatore Benedetto unsigned int curve_id;
173c4b2390SSalvatore Benedetto unsigned int ndigits;
183c4b2390SSalvatore Benedetto u64 private_key[ECC_MAX_DIGITS];
193c4b2390SSalvatore Benedetto };
203c4b2390SSalvatore Benedetto
ecdh_get_ctx(struct crypto_kpp * tfm)213c4b2390SSalvatore Benedetto static inline struct ecdh_ctx *ecdh_get_ctx(struct crypto_kpp *tfm)
223c4b2390SSalvatore Benedetto {
233c4b2390SSalvatore Benedetto return kpp_tfm_ctx(tfm);
243c4b2390SSalvatore Benedetto }
253c4b2390SSalvatore Benedetto
ecdh_set_secret(struct crypto_kpp * tfm,const void * buf,unsigned int len)265527dfb6SEric Biggers static int ecdh_set_secret(struct crypto_kpp *tfm, const void *buf,
275527dfb6SEric Biggers unsigned int len)
283c4b2390SSalvatore Benedetto {
293c4b2390SSalvatore Benedetto struct ecdh_ctx *ctx = ecdh_get_ctx(tfm);
303c4b2390SSalvatore Benedetto struct ecdh params;
313c4b2390SSalvatore Benedetto
320aa171e9SArd Biesheuvel if (crypto_ecdh_decode_key(buf, len, ¶ms) < 0 ||
336763f5eaSMeng Yu params.key_size > sizeof(u64) * ctx->ndigits)
343c4b2390SSalvatore Benedetto return -EINVAL;
353c4b2390SSalvatore Benedetto
36*80575b25SJoachim Vandersmissen memset(ctx->private_key, 0, sizeof(ctx->private_key));
37*80575b25SJoachim Vandersmissen
386755fd26STudor-Dan Ambarus if (!params.key || !params.key_size)
396755fd26STudor-Dan Ambarus return ecc_gen_privkey(ctx->curve_id, ctx->ndigits,
406755fd26STudor-Dan Ambarus ctx->private_key);
416755fd26STudor-Dan Ambarus
423c4b2390SSalvatore Benedetto memcpy(ctx->private_key, params.key, params.key_size);
433c4b2390SSalvatore Benedetto
4417858b14SArd Biesheuvel if (ecc_is_key_valid(ctx->curve_id, ctx->ndigits,
4517858b14SArd Biesheuvel ctx->private_key, params.key_size) < 0) {
4617858b14SArd Biesheuvel memzero_explicit(ctx->private_key, params.key_size);
4717858b14SArd Biesheuvel return -EINVAL;
4817858b14SArd Biesheuvel }
493c4b2390SSalvatore Benedetto return 0;
503c4b2390SSalvatore Benedetto }
513c4b2390SSalvatore Benedetto
ecdh_compute_value(struct kpp_request * req)523c4b2390SSalvatore Benedetto static int ecdh_compute_value(struct kpp_request *req)
533c4b2390SSalvatore Benedetto {
543c4b2390SSalvatore Benedetto struct crypto_kpp *tfm = crypto_kpp_reqtfm(req);
553c4b2390SSalvatore Benedetto struct ecdh_ctx *ctx = ecdh_get_ctx(tfm);
56952035baSTudor-Dan Ambarus u64 *public_key;
57952035baSTudor-Dan Ambarus u64 *shared_secret = NULL;
583c4b2390SSalvatore Benedetto void *buf;
59952035baSTudor-Dan Ambarus size_t copied, nbytes, public_key_sz;
60952035baSTudor-Dan Ambarus int ret = -ENOMEM;
613c4b2390SSalvatore Benedetto
623c4b2390SSalvatore Benedetto nbytes = ctx->ndigits << ECC_DIGITS_TO_BYTES_SHIFT;
63952035baSTudor-Dan Ambarus /* Public part is a point thus it has both coordinates */
64952035baSTudor-Dan Ambarus public_key_sz = 2 * nbytes;
65952035baSTudor-Dan Ambarus
66952035baSTudor-Dan Ambarus public_key = kmalloc(public_key_sz, GFP_KERNEL);
67952035baSTudor-Dan Ambarus if (!public_key)
68952035baSTudor-Dan Ambarus return -ENOMEM;
693c4b2390SSalvatore Benedetto
703c4b2390SSalvatore Benedetto if (req->src) {
71952035baSTudor-Dan Ambarus shared_secret = kmalloc(nbytes, GFP_KERNEL);
72952035baSTudor-Dan Ambarus if (!shared_secret)
73952035baSTudor-Dan Ambarus goto free_pubkey;
74952035baSTudor-Dan Ambarus
7595ec01baSJames Bottomley /* from here on it's invalid parameters */
76952035baSTudor-Dan Ambarus ret = -EINVAL;
7795ec01baSJames Bottomley
7895ec01baSJames Bottomley /* must have exactly two points to be on the curve */
7995ec01baSJames Bottomley if (public_key_sz != req->src_len)
80952035baSTudor-Dan Ambarus goto free_all;
8195ec01baSJames Bottomley
8295ec01baSJames Bottomley copied = sg_copy_to_buffer(req->src,
8395ec01baSJames Bottomley sg_nents_for_len(req->src,
8495ec01baSJames Bottomley public_key_sz),
8595ec01baSJames Bottomley public_key, public_key_sz);
8695ec01baSJames Bottomley if (copied != public_key_sz)
8795ec01baSJames Bottomley goto free_all;
883c4b2390SSalvatore Benedetto
898f44df15SStephen Rothwell ret = crypto_ecdh_shared_secret(ctx->curve_id, ctx->ndigits,
90952035baSTudor-Dan Ambarus ctx->private_key, public_key,
91952035baSTudor-Dan Ambarus shared_secret);
923c4b2390SSalvatore Benedetto
93952035baSTudor-Dan Ambarus buf = shared_secret;
943c4b2390SSalvatore Benedetto } else {
957380c56dSTudor-Dan Ambarus ret = ecc_make_pub_key(ctx->curve_id, ctx->ndigits,
96952035baSTudor-Dan Ambarus ctx->private_key, public_key);
97952035baSTudor-Dan Ambarus buf = public_key;
98952035baSTudor-Dan Ambarus nbytes = public_key_sz;
993c4b2390SSalvatore Benedetto }
1003c4b2390SSalvatore Benedetto
1013c4b2390SSalvatore Benedetto if (ret < 0)
102952035baSTudor-Dan Ambarus goto free_all;
1033c4b2390SSalvatore Benedetto
10495ec01baSJames Bottomley /* might want less than we've got */
10595ec01baSJames Bottomley nbytes = min_t(size_t, nbytes, req->dst_len);
10695ec01baSJames Bottomley copied = sg_copy_from_buffer(req->dst, sg_nents_for_len(req->dst,
10795ec01baSJames Bottomley nbytes),
10895ec01baSJames Bottomley buf, nbytes);
1093c4b2390SSalvatore Benedetto if (copied != nbytes)
110952035baSTudor-Dan Ambarus ret = -EINVAL;
1113c4b2390SSalvatore Benedetto
112952035baSTudor-Dan Ambarus /* fall through */
113952035baSTudor-Dan Ambarus free_all:
114453431a5SWaiman Long kfree_sensitive(shared_secret);
115952035baSTudor-Dan Ambarus free_pubkey:
116952035baSTudor-Dan Ambarus kfree(public_key);
1173c4b2390SSalvatore Benedetto return ret;
1183c4b2390SSalvatore Benedetto }
1193c4b2390SSalvatore Benedetto
ecdh_max_size(struct crypto_kpp * tfm)120d0efb48bSTudor-Dan Ambarus static unsigned int ecdh_max_size(struct crypto_kpp *tfm)
1213c4b2390SSalvatore Benedetto {
1223c4b2390SSalvatore Benedetto struct ecdh_ctx *ctx = ecdh_get_ctx(tfm);
1233c4b2390SSalvatore Benedetto
124d0efb48bSTudor-Dan Ambarus /* Public key is made of two coordinates, add one to the left shift */
125d0efb48bSTudor-Dan Ambarus return ctx->ndigits << (ECC_DIGITS_TO_BYTES_SHIFT + 1);
1263c4b2390SSalvatore Benedetto }
1273c4b2390SSalvatore Benedetto
ecdh_nist_p192_init_tfm(struct crypto_kpp * tfm)1286763f5eaSMeng Yu static int ecdh_nist_p192_init_tfm(struct crypto_kpp *tfm)
1296763f5eaSMeng Yu {
1306763f5eaSMeng Yu struct ecdh_ctx *ctx = ecdh_get_ctx(tfm);
1316763f5eaSMeng Yu
1326763f5eaSMeng Yu ctx->curve_id = ECC_CURVE_NIST_P192;
1336763f5eaSMeng Yu ctx->ndigits = ECC_CURVE_NIST_P192_DIGITS;
1346763f5eaSMeng Yu
1356763f5eaSMeng Yu return 0;
1366763f5eaSMeng Yu }
1376763f5eaSMeng Yu
1386763f5eaSMeng Yu static struct kpp_alg ecdh_nist_p192 = {
1393c4b2390SSalvatore Benedetto .set_secret = ecdh_set_secret,
1403c4b2390SSalvatore Benedetto .generate_public_key = ecdh_compute_value,
1413c4b2390SSalvatore Benedetto .compute_shared_secret = ecdh_compute_value,
1423c4b2390SSalvatore Benedetto .max_size = ecdh_max_size,
1436763f5eaSMeng Yu .init = ecdh_nist_p192_init_tfm,
1443c4b2390SSalvatore Benedetto .base = {
1456763f5eaSMeng Yu .cra_name = "ecdh-nist-p192",
146c5ae16f5SHui Tang .cra_driver_name = "ecdh-nist-p192-generic",
1473c4b2390SSalvatore Benedetto .cra_priority = 100,
1483c4b2390SSalvatore Benedetto .cra_module = THIS_MODULE,
1493c4b2390SSalvatore Benedetto .cra_ctxsize = sizeof(struct ecdh_ctx),
1503c4b2390SSalvatore Benedetto },
1513c4b2390SSalvatore Benedetto };
1523c4b2390SSalvatore Benedetto
ecdh_nist_p256_init_tfm(struct crypto_kpp * tfm)1536763f5eaSMeng Yu static int ecdh_nist_p256_init_tfm(struct crypto_kpp *tfm)
1546763f5eaSMeng Yu {
1556763f5eaSMeng Yu struct ecdh_ctx *ctx = ecdh_get_ctx(tfm);
1566763f5eaSMeng Yu
1576763f5eaSMeng Yu ctx->curve_id = ECC_CURVE_NIST_P256;
1586763f5eaSMeng Yu ctx->ndigits = ECC_CURVE_NIST_P256_DIGITS;
1596763f5eaSMeng Yu
1606763f5eaSMeng Yu return 0;
1616763f5eaSMeng Yu }
1626763f5eaSMeng Yu
1636763f5eaSMeng Yu static struct kpp_alg ecdh_nist_p256 = {
1646763f5eaSMeng Yu .set_secret = ecdh_set_secret,
1656763f5eaSMeng Yu .generate_public_key = ecdh_compute_value,
1666763f5eaSMeng Yu .compute_shared_secret = ecdh_compute_value,
1676763f5eaSMeng Yu .max_size = ecdh_max_size,
1686763f5eaSMeng Yu .init = ecdh_nist_p256_init_tfm,
1696763f5eaSMeng Yu .base = {
1706763f5eaSMeng Yu .cra_name = "ecdh-nist-p256",
171c5ae16f5SHui Tang .cra_driver_name = "ecdh-nist-p256-generic",
1726763f5eaSMeng Yu .cra_priority = 100,
1736763f5eaSMeng Yu .cra_module = THIS_MODULE,
1746763f5eaSMeng Yu .cra_ctxsize = sizeof(struct ecdh_ctx),
1756763f5eaSMeng Yu },
1766763f5eaSMeng Yu };
1776763f5eaSMeng Yu
ecdh_nist_p384_init_tfm(struct crypto_kpp * tfm)17881541325SHui Tang static int ecdh_nist_p384_init_tfm(struct crypto_kpp *tfm)
17981541325SHui Tang {
18081541325SHui Tang struct ecdh_ctx *ctx = ecdh_get_ctx(tfm);
18181541325SHui Tang
18281541325SHui Tang ctx->curve_id = ECC_CURVE_NIST_P384;
18381541325SHui Tang ctx->ndigits = ECC_CURVE_NIST_P384_DIGITS;
18481541325SHui Tang
18581541325SHui Tang return 0;
18681541325SHui Tang }
18781541325SHui Tang
18881541325SHui Tang static struct kpp_alg ecdh_nist_p384 = {
18981541325SHui Tang .set_secret = ecdh_set_secret,
19081541325SHui Tang .generate_public_key = ecdh_compute_value,
19181541325SHui Tang .compute_shared_secret = ecdh_compute_value,
19281541325SHui Tang .max_size = ecdh_max_size,
19381541325SHui Tang .init = ecdh_nist_p384_init_tfm,
19481541325SHui Tang .base = {
19581541325SHui Tang .cra_name = "ecdh-nist-p384",
19681541325SHui Tang .cra_driver_name = "ecdh-nist-p384-generic",
19781541325SHui Tang .cra_priority = 100,
19881541325SHui Tang .cra_module = THIS_MODULE,
19981541325SHui Tang .cra_ctxsize = sizeof(struct ecdh_ctx),
20081541325SHui Tang },
20181541325SHui Tang };
20281541325SHui Tang
2036763f5eaSMeng Yu static bool ecdh_nist_p192_registered;
2046763f5eaSMeng Yu
ecdh_init(void)20533837be3SXiu Jianfeng static int __init ecdh_init(void)
2063c4b2390SSalvatore Benedetto {
2076763f5eaSMeng Yu int ret;
2086763f5eaSMeng Yu
2096889fc21SHui Tang /* NIST p192 will fail to register in FIPS mode */
2106763f5eaSMeng Yu ret = crypto_register_kpp(&ecdh_nist_p192);
2116763f5eaSMeng Yu ecdh_nist_p192_registered = ret == 0;
2126763f5eaSMeng Yu
2138fd28fa5SHui Tang ret = crypto_register_kpp(&ecdh_nist_p256);
2148fd28fa5SHui Tang if (ret)
2158fd28fa5SHui Tang goto nist_p256_error;
2168fd28fa5SHui Tang
21781541325SHui Tang ret = crypto_register_kpp(&ecdh_nist_p384);
21881541325SHui Tang if (ret)
21981541325SHui Tang goto nist_p384_error;
22081541325SHui Tang
2218fd28fa5SHui Tang return 0;
2228fd28fa5SHui Tang
22381541325SHui Tang nist_p384_error:
22481541325SHui Tang crypto_unregister_kpp(&ecdh_nist_p256);
22581541325SHui Tang
2268fd28fa5SHui Tang nist_p256_error:
2278fd28fa5SHui Tang if (ecdh_nist_p192_registered)
2288fd28fa5SHui Tang crypto_unregister_kpp(&ecdh_nist_p192);
2298fd28fa5SHui Tang return ret;
2303c4b2390SSalvatore Benedetto }
2313c4b2390SSalvatore Benedetto
ecdh_exit(void)23233837be3SXiu Jianfeng static void __exit ecdh_exit(void)
2333c4b2390SSalvatore Benedetto {
2346763f5eaSMeng Yu if (ecdh_nist_p192_registered)
2356763f5eaSMeng Yu crypto_unregister_kpp(&ecdh_nist_p192);
2366763f5eaSMeng Yu crypto_unregister_kpp(&ecdh_nist_p256);
23781541325SHui Tang crypto_unregister_kpp(&ecdh_nist_p384);
2383c4b2390SSalvatore Benedetto }
2393c4b2390SSalvatore Benedetto
240c4741b23SEric Biggers subsys_initcall(ecdh_init);
2413c4b2390SSalvatore Benedetto module_exit(ecdh_exit);
2423c4b2390SSalvatore Benedetto MODULE_ALIAS_CRYPTO("ecdh");
2433c4b2390SSalvatore Benedetto MODULE_LICENSE("GPL");
2443c4b2390SSalvatore Benedetto MODULE_DESCRIPTION("ECDH generic algorithm");
245