162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/* ECDH key-agreement protocol
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright (c) 2016, Intel Corporation
562306a36Sopenharmony_ci * Authors: Salvator Benedetto <salvatore.benedetto@intel.com>
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/module.h>
962306a36Sopenharmony_ci#include <crypto/internal/ecc.h>
1062306a36Sopenharmony_ci#include <crypto/internal/kpp.h>
1162306a36Sopenharmony_ci#include <crypto/kpp.h>
1262306a36Sopenharmony_ci#include <crypto/ecdh.h>
1362306a36Sopenharmony_ci#include <linux/scatterlist.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_cistruct ecdh_ctx {
1662306a36Sopenharmony_ci	unsigned int curve_id;
1762306a36Sopenharmony_ci	unsigned int ndigits;
1862306a36Sopenharmony_ci	u64 private_key[ECC_MAX_DIGITS];
1962306a36Sopenharmony_ci};
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_cistatic inline struct ecdh_ctx *ecdh_get_ctx(struct crypto_kpp *tfm)
2262306a36Sopenharmony_ci{
2362306a36Sopenharmony_ci	return kpp_tfm_ctx(tfm);
2462306a36Sopenharmony_ci}
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_cistatic int ecdh_set_secret(struct crypto_kpp *tfm, const void *buf,
2762306a36Sopenharmony_ci			   unsigned int len)
2862306a36Sopenharmony_ci{
2962306a36Sopenharmony_ci	struct ecdh_ctx *ctx = ecdh_get_ctx(tfm);
3062306a36Sopenharmony_ci	struct ecdh params;
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci	if (crypto_ecdh_decode_key(buf, len, &params) < 0 ||
3362306a36Sopenharmony_ci	    params.key_size > sizeof(u64) * ctx->ndigits)
3462306a36Sopenharmony_ci		return -EINVAL;
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci	if (!params.key || !params.key_size)
3762306a36Sopenharmony_ci		return ecc_gen_privkey(ctx->curve_id, ctx->ndigits,
3862306a36Sopenharmony_ci				       ctx->private_key);
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci	memcpy(ctx->private_key, params.key, params.key_size);
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	if (ecc_is_key_valid(ctx->curve_id, ctx->ndigits,
4362306a36Sopenharmony_ci			     ctx->private_key, params.key_size) < 0) {
4462306a36Sopenharmony_ci		memzero_explicit(ctx->private_key, params.key_size);
4562306a36Sopenharmony_ci		return -EINVAL;
4662306a36Sopenharmony_ci	}
4762306a36Sopenharmony_ci	return 0;
4862306a36Sopenharmony_ci}
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_cistatic int ecdh_compute_value(struct kpp_request *req)
5162306a36Sopenharmony_ci{
5262306a36Sopenharmony_ci	struct crypto_kpp *tfm = crypto_kpp_reqtfm(req);
5362306a36Sopenharmony_ci	struct ecdh_ctx *ctx = ecdh_get_ctx(tfm);
5462306a36Sopenharmony_ci	u64 *public_key;
5562306a36Sopenharmony_ci	u64 *shared_secret = NULL;
5662306a36Sopenharmony_ci	void *buf;
5762306a36Sopenharmony_ci	size_t copied, nbytes, public_key_sz;
5862306a36Sopenharmony_ci	int ret = -ENOMEM;
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	nbytes = ctx->ndigits << ECC_DIGITS_TO_BYTES_SHIFT;
6162306a36Sopenharmony_ci	/* Public part is a point thus it has both coordinates */
6262306a36Sopenharmony_ci	public_key_sz = 2 * nbytes;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	public_key = kmalloc(public_key_sz, GFP_KERNEL);
6562306a36Sopenharmony_ci	if (!public_key)
6662306a36Sopenharmony_ci		return -ENOMEM;
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	if (req->src) {
6962306a36Sopenharmony_ci		shared_secret = kmalloc(nbytes, GFP_KERNEL);
7062306a36Sopenharmony_ci		if (!shared_secret)
7162306a36Sopenharmony_ci			goto free_pubkey;
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci		/* from here on it's invalid parameters */
7462306a36Sopenharmony_ci		ret = -EINVAL;
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci		/* must have exactly two points to be on the curve */
7762306a36Sopenharmony_ci		if (public_key_sz != req->src_len)
7862306a36Sopenharmony_ci			goto free_all;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci		copied = sg_copy_to_buffer(req->src,
8162306a36Sopenharmony_ci					   sg_nents_for_len(req->src,
8262306a36Sopenharmony_ci							    public_key_sz),
8362306a36Sopenharmony_ci					   public_key, public_key_sz);
8462306a36Sopenharmony_ci		if (copied != public_key_sz)
8562306a36Sopenharmony_ci			goto free_all;
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci		ret = crypto_ecdh_shared_secret(ctx->curve_id, ctx->ndigits,
8862306a36Sopenharmony_ci						ctx->private_key, public_key,
8962306a36Sopenharmony_ci						shared_secret);
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci		buf = shared_secret;
9262306a36Sopenharmony_ci	} else {
9362306a36Sopenharmony_ci		ret = ecc_make_pub_key(ctx->curve_id, ctx->ndigits,
9462306a36Sopenharmony_ci				       ctx->private_key, public_key);
9562306a36Sopenharmony_ci		buf = public_key;
9662306a36Sopenharmony_ci		nbytes = public_key_sz;
9762306a36Sopenharmony_ci	}
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	if (ret < 0)
10062306a36Sopenharmony_ci		goto free_all;
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	/* might want less than we've got */
10362306a36Sopenharmony_ci	nbytes = min_t(size_t, nbytes, req->dst_len);
10462306a36Sopenharmony_ci	copied = sg_copy_from_buffer(req->dst, sg_nents_for_len(req->dst,
10562306a36Sopenharmony_ci								nbytes),
10662306a36Sopenharmony_ci				     buf, nbytes);
10762306a36Sopenharmony_ci	if (copied != nbytes)
10862306a36Sopenharmony_ci		ret = -EINVAL;
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	/* fall through */
11162306a36Sopenharmony_cifree_all:
11262306a36Sopenharmony_ci	kfree_sensitive(shared_secret);
11362306a36Sopenharmony_cifree_pubkey:
11462306a36Sopenharmony_ci	kfree(public_key);
11562306a36Sopenharmony_ci	return ret;
11662306a36Sopenharmony_ci}
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_cistatic unsigned int ecdh_max_size(struct crypto_kpp *tfm)
11962306a36Sopenharmony_ci{
12062306a36Sopenharmony_ci	struct ecdh_ctx *ctx = ecdh_get_ctx(tfm);
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	/* Public key is made of two coordinates, add one to the left shift */
12362306a36Sopenharmony_ci	return ctx->ndigits << (ECC_DIGITS_TO_BYTES_SHIFT + 1);
12462306a36Sopenharmony_ci}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_cistatic int ecdh_nist_p192_init_tfm(struct crypto_kpp *tfm)
12762306a36Sopenharmony_ci{
12862306a36Sopenharmony_ci	struct ecdh_ctx *ctx = ecdh_get_ctx(tfm);
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	ctx->curve_id = ECC_CURVE_NIST_P192;
13162306a36Sopenharmony_ci	ctx->ndigits = ECC_CURVE_NIST_P192_DIGITS;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	return 0;
13462306a36Sopenharmony_ci}
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_cistatic struct kpp_alg ecdh_nist_p192 = {
13762306a36Sopenharmony_ci	.set_secret = ecdh_set_secret,
13862306a36Sopenharmony_ci	.generate_public_key = ecdh_compute_value,
13962306a36Sopenharmony_ci	.compute_shared_secret = ecdh_compute_value,
14062306a36Sopenharmony_ci	.max_size = ecdh_max_size,
14162306a36Sopenharmony_ci	.init = ecdh_nist_p192_init_tfm,
14262306a36Sopenharmony_ci	.base = {
14362306a36Sopenharmony_ci		.cra_name = "ecdh-nist-p192",
14462306a36Sopenharmony_ci		.cra_driver_name = "ecdh-nist-p192-generic",
14562306a36Sopenharmony_ci		.cra_priority = 100,
14662306a36Sopenharmony_ci		.cra_module = THIS_MODULE,
14762306a36Sopenharmony_ci		.cra_ctxsize = sizeof(struct ecdh_ctx),
14862306a36Sopenharmony_ci	},
14962306a36Sopenharmony_ci};
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_cistatic int ecdh_nist_p256_init_tfm(struct crypto_kpp *tfm)
15262306a36Sopenharmony_ci{
15362306a36Sopenharmony_ci	struct ecdh_ctx *ctx = ecdh_get_ctx(tfm);
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	ctx->curve_id = ECC_CURVE_NIST_P256;
15662306a36Sopenharmony_ci	ctx->ndigits = ECC_CURVE_NIST_P256_DIGITS;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	return 0;
15962306a36Sopenharmony_ci}
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_cistatic struct kpp_alg ecdh_nist_p256 = {
16262306a36Sopenharmony_ci	.set_secret = ecdh_set_secret,
16362306a36Sopenharmony_ci	.generate_public_key = ecdh_compute_value,
16462306a36Sopenharmony_ci	.compute_shared_secret = ecdh_compute_value,
16562306a36Sopenharmony_ci	.max_size = ecdh_max_size,
16662306a36Sopenharmony_ci	.init = ecdh_nist_p256_init_tfm,
16762306a36Sopenharmony_ci	.base = {
16862306a36Sopenharmony_ci		.cra_name = "ecdh-nist-p256",
16962306a36Sopenharmony_ci		.cra_driver_name = "ecdh-nist-p256-generic",
17062306a36Sopenharmony_ci		.cra_priority = 100,
17162306a36Sopenharmony_ci		.cra_module = THIS_MODULE,
17262306a36Sopenharmony_ci		.cra_ctxsize = sizeof(struct ecdh_ctx),
17362306a36Sopenharmony_ci	},
17462306a36Sopenharmony_ci};
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_cistatic int ecdh_nist_p384_init_tfm(struct crypto_kpp *tfm)
17762306a36Sopenharmony_ci{
17862306a36Sopenharmony_ci	struct ecdh_ctx *ctx = ecdh_get_ctx(tfm);
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	ctx->curve_id = ECC_CURVE_NIST_P384;
18162306a36Sopenharmony_ci	ctx->ndigits = ECC_CURVE_NIST_P384_DIGITS;
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	return 0;
18462306a36Sopenharmony_ci}
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_cistatic struct kpp_alg ecdh_nist_p384 = {
18762306a36Sopenharmony_ci	.set_secret = ecdh_set_secret,
18862306a36Sopenharmony_ci	.generate_public_key = ecdh_compute_value,
18962306a36Sopenharmony_ci	.compute_shared_secret = ecdh_compute_value,
19062306a36Sopenharmony_ci	.max_size = ecdh_max_size,
19162306a36Sopenharmony_ci	.init = ecdh_nist_p384_init_tfm,
19262306a36Sopenharmony_ci	.base = {
19362306a36Sopenharmony_ci		.cra_name = "ecdh-nist-p384",
19462306a36Sopenharmony_ci		.cra_driver_name = "ecdh-nist-p384-generic",
19562306a36Sopenharmony_ci		.cra_priority = 100,
19662306a36Sopenharmony_ci		.cra_module = THIS_MODULE,
19762306a36Sopenharmony_ci		.cra_ctxsize = sizeof(struct ecdh_ctx),
19862306a36Sopenharmony_ci	},
19962306a36Sopenharmony_ci};
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_cistatic bool ecdh_nist_p192_registered;
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_cistatic int __init ecdh_init(void)
20462306a36Sopenharmony_ci{
20562306a36Sopenharmony_ci	int ret;
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	/* NIST p192 will fail to register in FIPS mode */
20862306a36Sopenharmony_ci	ret = crypto_register_kpp(&ecdh_nist_p192);
20962306a36Sopenharmony_ci	ecdh_nist_p192_registered = ret == 0;
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	ret = crypto_register_kpp(&ecdh_nist_p256);
21262306a36Sopenharmony_ci	if (ret)
21362306a36Sopenharmony_ci		goto nist_p256_error;
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	ret = crypto_register_kpp(&ecdh_nist_p384);
21662306a36Sopenharmony_ci	if (ret)
21762306a36Sopenharmony_ci		goto nist_p384_error;
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	return 0;
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_cinist_p384_error:
22262306a36Sopenharmony_ci	crypto_unregister_kpp(&ecdh_nist_p256);
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_cinist_p256_error:
22562306a36Sopenharmony_ci	if (ecdh_nist_p192_registered)
22662306a36Sopenharmony_ci		crypto_unregister_kpp(&ecdh_nist_p192);
22762306a36Sopenharmony_ci	return ret;
22862306a36Sopenharmony_ci}
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_cistatic void __exit ecdh_exit(void)
23162306a36Sopenharmony_ci{
23262306a36Sopenharmony_ci	if (ecdh_nist_p192_registered)
23362306a36Sopenharmony_ci		crypto_unregister_kpp(&ecdh_nist_p192);
23462306a36Sopenharmony_ci	crypto_unregister_kpp(&ecdh_nist_p256);
23562306a36Sopenharmony_ci	crypto_unregister_kpp(&ecdh_nist_p384);
23662306a36Sopenharmony_ci}
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_cisubsys_initcall(ecdh_init);
23962306a36Sopenharmony_cimodule_exit(ecdh_exit);
24062306a36Sopenharmony_ciMODULE_ALIAS_CRYPTO("ecdh");
24162306a36Sopenharmony_ciMODULE_LICENSE("GPL");
24262306a36Sopenharmony_ciMODULE_DESCRIPTION("ECDH generic algorithm");
243