xref: /openbmc/linux/crypto/ecdh.c (revision 33837be3)
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 
366755fd26STudor-Dan Ambarus 	if (!params.key || !params.key_size)
376755fd26STudor-Dan Ambarus 		return ecc_gen_privkey(ctx->curve_id, ctx->ndigits,
386755fd26STudor-Dan Ambarus 				       ctx->private_key);
396755fd26STudor-Dan Ambarus 
403c4b2390SSalvatore Benedetto 	memcpy(ctx->private_key, params.key, params.key_size);
413c4b2390SSalvatore Benedetto 
4217858b14SArd Biesheuvel 	if (ecc_is_key_valid(ctx->curve_id, ctx->ndigits,
4317858b14SArd Biesheuvel 			     ctx->private_key, params.key_size) < 0) {
4417858b14SArd Biesheuvel 		memzero_explicit(ctx->private_key, params.key_size);
4517858b14SArd Biesheuvel 		return -EINVAL;
4617858b14SArd Biesheuvel 	}
473c4b2390SSalvatore Benedetto 	return 0;
483c4b2390SSalvatore Benedetto }
493c4b2390SSalvatore Benedetto 
ecdh_compute_value(struct kpp_request * req)503c4b2390SSalvatore Benedetto static int ecdh_compute_value(struct kpp_request *req)
513c4b2390SSalvatore Benedetto {
523c4b2390SSalvatore Benedetto 	struct crypto_kpp *tfm = crypto_kpp_reqtfm(req);
533c4b2390SSalvatore Benedetto 	struct ecdh_ctx *ctx = ecdh_get_ctx(tfm);
54952035baSTudor-Dan Ambarus 	u64 *public_key;
55952035baSTudor-Dan Ambarus 	u64 *shared_secret = NULL;
563c4b2390SSalvatore Benedetto 	void *buf;
57952035baSTudor-Dan Ambarus 	size_t copied, nbytes, public_key_sz;
58952035baSTudor-Dan Ambarus 	int ret = -ENOMEM;
593c4b2390SSalvatore Benedetto 
603c4b2390SSalvatore Benedetto 	nbytes = ctx->ndigits << ECC_DIGITS_TO_BYTES_SHIFT;
61952035baSTudor-Dan Ambarus 	/* Public part is a point thus it has both coordinates */
62952035baSTudor-Dan Ambarus 	public_key_sz = 2 * nbytes;
63952035baSTudor-Dan Ambarus 
64952035baSTudor-Dan Ambarus 	public_key = kmalloc(public_key_sz, GFP_KERNEL);
65952035baSTudor-Dan Ambarus 	if (!public_key)
66952035baSTudor-Dan Ambarus 		return -ENOMEM;
673c4b2390SSalvatore Benedetto 
683c4b2390SSalvatore Benedetto 	if (req->src) {
69952035baSTudor-Dan Ambarus 		shared_secret = kmalloc(nbytes, GFP_KERNEL);
70952035baSTudor-Dan Ambarus 		if (!shared_secret)
71952035baSTudor-Dan Ambarus 			goto free_pubkey;
72952035baSTudor-Dan Ambarus 
7395ec01baSJames Bottomley 		/* from here on it's invalid parameters */
74952035baSTudor-Dan Ambarus 		ret = -EINVAL;
7595ec01baSJames Bottomley 
7695ec01baSJames Bottomley 		/* must have exactly two points to be on the curve */
7795ec01baSJames Bottomley 		if (public_key_sz != req->src_len)
78952035baSTudor-Dan Ambarus 			goto free_all;
7995ec01baSJames Bottomley 
8095ec01baSJames Bottomley 		copied = sg_copy_to_buffer(req->src,
8195ec01baSJames Bottomley 					   sg_nents_for_len(req->src,
8295ec01baSJames Bottomley 							    public_key_sz),
8395ec01baSJames Bottomley 					   public_key, public_key_sz);
8495ec01baSJames Bottomley 		if (copied != public_key_sz)
8595ec01baSJames Bottomley 			goto free_all;
863c4b2390SSalvatore Benedetto 
878f44df15SStephen Rothwell 		ret = crypto_ecdh_shared_secret(ctx->curve_id, ctx->ndigits,
88952035baSTudor-Dan Ambarus 						ctx->private_key, public_key,
89952035baSTudor-Dan Ambarus 						shared_secret);
903c4b2390SSalvatore Benedetto 
91952035baSTudor-Dan Ambarus 		buf = shared_secret;
923c4b2390SSalvatore Benedetto 	} else {
937380c56dSTudor-Dan Ambarus 		ret = ecc_make_pub_key(ctx->curve_id, ctx->ndigits,
94952035baSTudor-Dan Ambarus 				       ctx->private_key, public_key);
95952035baSTudor-Dan Ambarus 		buf = public_key;
96952035baSTudor-Dan Ambarus 		nbytes = public_key_sz;
973c4b2390SSalvatore Benedetto 	}
983c4b2390SSalvatore Benedetto 
993c4b2390SSalvatore Benedetto 	if (ret < 0)
100952035baSTudor-Dan Ambarus 		goto free_all;
1013c4b2390SSalvatore Benedetto 
10295ec01baSJames Bottomley 	/* might want less than we've got */
10395ec01baSJames Bottomley 	nbytes = min_t(size_t, nbytes, req->dst_len);
10495ec01baSJames Bottomley 	copied = sg_copy_from_buffer(req->dst, sg_nents_for_len(req->dst,
10595ec01baSJames Bottomley 								nbytes),
10695ec01baSJames Bottomley 				     buf, nbytes);
1073c4b2390SSalvatore Benedetto 	if (copied != nbytes)
108952035baSTudor-Dan Ambarus 		ret = -EINVAL;
1093c4b2390SSalvatore Benedetto 
110952035baSTudor-Dan Ambarus 	/* fall through */
111952035baSTudor-Dan Ambarus free_all:
112453431a5SWaiman Long 	kfree_sensitive(shared_secret);
113952035baSTudor-Dan Ambarus free_pubkey:
114952035baSTudor-Dan Ambarus 	kfree(public_key);
1153c4b2390SSalvatore Benedetto 	return ret;
1163c4b2390SSalvatore Benedetto }
1173c4b2390SSalvatore Benedetto 
ecdh_max_size(struct crypto_kpp * tfm)118d0efb48bSTudor-Dan Ambarus static unsigned int ecdh_max_size(struct crypto_kpp *tfm)
1193c4b2390SSalvatore Benedetto {
1203c4b2390SSalvatore Benedetto 	struct ecdh_ctx *ctx = ecdh_get_ctx(tfm);
1213c4b2390SSalvatore Benedetto 
122d0efb48bSTudor-Dan Ambarus 	/* Public key is made of two coordinates, add one to the left shift */
123d0efb48bSTudor-Dan Ambarus 	return ctx->ndigits << (ECC_DIGITS_TO_BYTES_SHIFT + 1);
1243c4b2390SSalvatore Benedetto }
1253c4b2390SSalvatore Benedetto 
ecdh_nist_p192_init_tfm(struct crypto_kpp * tfm)1266763f5eaSMeng Yu static int ecdh_nist_p192_init_tfm(struct crypto_kpp *tfm)
1276763f5eaSMeng Yu {
1286763f5eaSMeng Yu 	struct ecdh_ctx *ctx = ecdh_get_ctx(tfm);
1296763f5eaSMeng Yu 
1306763f5eaSMeng Yu 	ctx->curve_id = ECC_CURVE_NIST_P192;
1316763f5eaSMeng Yu 	ctx->ndigits = ECC_CURVE_NIST_P192_DIGITS;
1326763f5eaSMeng Yu 
1336763f5eaSMeng Yu 	return 0;
1346763f5eaSMeng Yu }
1356763f5eaSMeng Yu 
1366763f5eaSMeng Yu static struct kpp_alg ecdh_nist_p192 = {
1373c4b2390SSalvatore Benedetto 	.set_secret = ecdh_set_secret,
1383c4b2390SSalvatore Benedetto 	.generate_public_key = ecdh_compute_value,
1393c4b2390SSalvatore Benedetto 	.compute_shared_secret = ecdh_compute_value,
1403c4b2390SSalvatore Benedetto 	.max_size = ecdh_max_size,
1416763f5eaSMeng Yu 	.init = ecdh_nist_p192_init_tfm,
1423c4b2390SSalvatore Benedetto 	.base = {
1436763f5eaSMeng Yu 		.cra_name = "ecdh-nist-p192",
144c5ae16f5SHui Tang 		.cra_driver_name = "ecdh-nist-p192-generic",
1453c4b2390SSalvatore Benedetto 		.cra_priority = 100,
1463c4b2390SSalvatore Benedetto 		.cra_module = THIS_MODULE,
1473c4b2390SSalvatore Benedetto 		.cra_ctxsize = sizeof(struct ecdh_ctx),
1483c4b2390SSalvatore Benedetto 	},
1493c4b2390SSalvatore Benedetto };
1503c4b2390SSalvatore Benedetto 
ecdh_nist_p256_init_tfm(struct crypto_kpp * tfm)1516763f5eaSMeng Yu static int ecdh_nist_p256_init_tfm(struct crypto_kpp *tfm)
1526763f5eaSMeng Yu {
1536763f5eaSMeng Yu 	struct ecdh_ctx *ctx = ecdh_get_ctx(tfm);
1546763f5eaSMeng Yu 
1556763f5eaSMeng Yu 	ctx->curve_id = ECC_CURVE_NIST_P256;
1566763f5eaSMeng Yu 	ctx->ndigits = ECC_CURVE_NIST_P256_DIGITS;
1576763f5eaSMeng Yu 
1586763f5eaSMeng Yu 	return 0;
1596763f5eaSMeng Yu }
1606763f5eaSMeng Yu 
1616763f5eaSMeng Yu static struct kpp_alg ecdh_nist_p256 = {
1626763f5eaSMeng Yu 	.set_secret = ecdh_set_secret,
1636763f5eaSMeng Yu 	.generate_public_key = ecdh_compute_value,
1646763f5eaSMeng Yu 	.compute_shared_secret = ecdh_compute_value,
1656763f5eaSMeng Yu 	.max_size = ecdh_max_size,
1666763f5eaSMeng Yu 	.init = ecdh_nist_p256_init_tfm,
1676763f5eaSMeng Yu 	.base = {
1686763f5eaSMeng Yu 		.cra_name = "ecdh-nist-p256",
169c5ae16f5SHui Tang 		.cra_driver_name = "ecdh-nist-p256-generic",
1706763f5eaSMeng Yu 		.cra_priority = 100,
1716763f5eaSMeng Yu 		.cra_module = THIS_MODULE,
1726763f5eaSMeng Yu 		.cra_ctxsize = sizeof(struct ecdh_ctx),
1736763f5eaSMeng Yu 	},
1746763f5eaSMeng Yu };
1756763f5eaSMeng Yu 
ecdh_nist_p384_init_tfm(struct crypto_kpp * tfm)17681541325SHui Tang static int ecdh_nist_p384_init_tfm(struct crypto_kpp *tfm)
17781541325SHui Tang {
17881541325SHui Tang 	struct ecdh_ctx *ctx = ecdh_get_ctx(tfm);
17981541325SHui Tang 
18081541325SHui Tang 	ctx->curve_id = ECC_CURVE_NIST_P384;
18181541325SHui Tang 	ctx->ndigits = ECC_CURVE_NIST_P384_DIGITS;
18281541325SHui Tang 
18381541325SHui Tang 	return 0;
18481541325SHui Tang }
18581541325SHui Tang 
18681541325SHui Tang static struct kpp_alg ecdh_nist_p384 = {
18781541325SHui Tang 	.set_secret = ecdh_set_secret,
18881541325SHui Tang 	.generate_public_key = ecdh_compute_value,
18981541325SHui Tang 	.compute_shared_secret = ecdh_compute_value,
19081541325SHui Tang 	.max_size = ecdh_max_size,
19181541325SHui Tang 	.init = ecdh_nist_p384_init_tfm,
19281541325SHui Tang 	.base = {
19381541325SHui Tang 		.cra_name = "ecdh-nist-p384",
19481541325SHui Tang 		.cra_driver_name = "ecdh-nist-p384-generic",
19581541325SHui Tang 		.cra_priority = 100,
19681541325SHui Tang 		.cra_module = THIS_MODULE,
19781541325SHui Tang 		.cra_ctxsize = sizeof(struct ecdh_ctx),
19881541325SHui Tang 	},
19981541325SHui Tang };
20081541325SHui Tang 
2016763f5eaSMeng Yu static bool ecdh_nist_p192_registered;
2026763f5eaSMeng Yu 
ecdh_init(void)203*33837be3SXiu Jianfeng static int __init ecdh_init(void)
2043c4b2390SSalvatore Benedetto {
2056763f5eaSMeng Yu 	int ret;
2066763f5eaSMeng Yu 
2076889fc21SHui Tang 	/* NIST p192 will fail to register in FIPS mode */
2086763f5eaSMeng Yu 	ret = crypto_register_kpp(&ecdh_nist_p192);
2096763f5eaSMeng Yu 	ecdh_nist_p192_registered = ret == 0;
2106763f5eaSMeng Yu 
2118fd28fa5SHui Tang 	ret = crypto_register_kpp(&ecdh_nist_p256);
2128fd28fa5SHui Tang 	if (ret)
2138fd28fa5SHui Tang 		goto nist_p256_error;
2148fd28fa5SHui Tang 
21581541325SHui Tang 	ret = crypto_register_kpp(&ecdh_nist_p384);
21681541325SHui Tang 	if (ret)
21781541325SHui Tang 		goto nist_p384_error;
21881541325SHui Tang 
2198fd28fa5SHui Tang 	return 0;
2208fd28fa5SHui Tang 
22181541325SHui Tang nist_p384_error:
22281541325SHui Tang 	crypto_unregister_kpp(&ecdh_nist_p256);
22381541325SHui Tang 
2248fd28fa5SHui Tang nist_p256_error:
2258fd28fa5SHui Tang 	if (ecdh_nist_p192_registered)
2268fd28fa5SHui Tang 		crypto_unregister_kpp(&ecdh_nist_p192);
2278fd28fa5SHui Tang 	return ret;
2283c4b2390SSalvatore Benedetto }
2293c4b2390SSalvatore Benedetto 
ecdh_exit(void)230*33837be3SXiu Jianfeng static void __exit ecdh_exit(void)
2313c4b2390SSalvatore Benedetto {
2326763f5eaSMeng Yu 	if (ecdh_nist_p192_registered)
2336763f5eaSMeng Yu 		crypto_unregister_kpp(&ecdh_nist_p192);
2346763f5eaSMeng Yu 	crypto_unregister_kpp(&ecdh_nist_p256);
23581541325SHui Tang 	crypto_unregister_kpp(&ecdh_nist_p384);
2363c4b2390SSalvatore Benedetto }
2373c4b2390SSalvatore Benedetto 
238c4741b23SEric Biggers subsys_initcall(ecdh_init);
2393c4b2390SSalvatore Benedetto module_exit(ecdh_exit);
2403c4b2390SSalvatore Benedetto MODULE_ALIAS_CRYPTO("ecdh");
2413c4b2390SSalvatore Benedetto MODULE_LICENSE("GPL");
2423c4b2390SSalvatore Benedetto MODULE_DESCRIPTION("ECDH generic algorithm");
243