xref: /openbmc/linux/crypto/ecdh.c (revision f91ca89e924eb287915522664a31afc71a49c05b)
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, &params) < 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