1d8f1308aSJason A. Donenfeld // SPDX-License-Identifier: GPL-2.0 OR MIT
2d8f1308aSJason A. Donenfeld /*
3d8f1308aSJason A. Donenfeld * Copyright (C) 2015-2019 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
4d8f1308aSJason A. Donenfeld *
5d8f1308aSJason A. Donenfeld * Based on public domain code from Daniel J. Bernstein and Peter Schwabe. This
6d8f1308aSJason A. Donenfeld * began from SUPERCOP's curve25519/neon2/scalarmult.s, but has subsequently been
7d8f1308aSJason A. Donenfeld * manually reworked for use in kernel space.
8d8f1308aSJason A. Donenfeld */
9d8f1308aSJason A. Donenfeld
10d8f1308aSJason A. Donenfeld #include <asm/hwcap.h>
11d8f1308aSJason A. Donenfeld #include <asm/neon.h>
12d8f1308aSJason A. Donenfeld #include <asm/simd.h>
13d8f1308aSJason A. Donenfeld #include <crypto/internal/kpp.h>
14d8f1308aSJason A. Donenfeld #include <crypto/internal/simd.h>
15d8f1308aSJason A. Donenfeld #include <linux/types.h>
16d8f1308aSJason A. Donenfeld #include <linux/module.h>
17d8f1308aSJason A. Donenfeld #include <linux/init.h>
18d8f1308aSJason A. Donenfeld #include <linux/jump_label.h>
196779d0e6SFabio Estevam #include <linux/scatterlist.h>
20d8f1308aSJason A. Donenfeld #include <crypto/curve25519.h>
21d8f1308aSJason A. Donenfeld
22d8f1308aSJason A. Donenfeld asmlinkage void curve25519_neon(u8 mypublic[CURVE25519_KEY_SIZE],
23d8f1308aSJason A. Donenfeld const u8 secret[CURVE25519_KEY_SIZE],
24d8f1308aSJason A. Donenfeld const u8 basepoint[CURVE25519_KEY_SIZE]);
25d8f1308aSJason A. Donenfeld
26d8f1308aSJason A. Donenfeld static __ro_after_init DEFINE_STATIC_KEY_FALSE(have_neon);
27d8f1308aSJason A. Donenfeld
curve25519_arch(u8 out[CURVE25519_KEY_SIZE],const u8 scalar[CURVE25519_KEY_SIZE],const u8 point[CURVE25519_KEY_SIZE])28d8f1308aSJason A. Donenfeld void curve25519_arch(u8 out[CURVE25519_KEY_SIZE],
29d8f1308aSJason A. Donenfeld const u8 scalar[CURVE25519_KEY_SIZE],
30d8f1308aSJason A. Donenfeld const u8 point[CURVE25519_KEY_SIZE])
31d8f1308aSJason A. Donenfeld {
32d8f1308aSJason A. Donenfeld if (static_branch_likely(&have_neon) && crypto_simd_usable()) {
33d8f1308aSJason A. Donenfeld kernel_neon_begin();
34d8f1308aSJason A. Donenfeld curve25519_neon(out, scalar, point);
35d8f1308aSJason A. Donenfeld kernel_neon_end();
36d8f1308aSJason A. Donenfeld } else {
37d8f1308aSJason A. Donenfeld curve25519_generic(out, scalar, point);
38d8f1308aSJason A. Donenfeld }
39d8f1308aSJason A. Donenfeld }
40d8f1308aSJason A. Donenfeld EXPORT_SYMBOL(curve25519_arch);
41d8f1308aSJason A. Donenfeld
curve25519_base_arch(u8 pub[CURVE25519_KEY_SIZE],const u8 secret[CURVE25519_KEY_SIZE])4284faa307SJason A. Donenfeld void curve25519_base_arch(u8 pub[CURVE25519_KEY_SIZE],
4384faa307SJason A. Donenfeld const u8 secret[CURVE25519_KEY_SIZE])
4484faa307SJason A. Donenfeld {
4584faa307SJason A. Donenfeld return curve25519_arch(pub, secret, curve25519_base_point);
4684faa307SJason A. Donenfeld }
4784faa307SJason A. Donenfeld EXPORT_SYMBOL(curve25519_base_arch);
4884faa307SJason A. Donenfeld
curve25519_set_secret(struct crypto_kpp * tfm,const void * buf,unsigned int len)49d8f1308aSJason A. Donenfeld static int curve25519_set_secret(struct crypto_kpp *tfm, const void *buf,
50d8f1308aSJason A. Donenfeld unsigned int len)
51d8f1308aSJason A. Donenfeld {
52d8f1308aSJason A. Donenfeld u8 *secret = kpp_tfm_ctx(tfm);
53d8f1308aSJason A. Donenfeld
54d8f1308aSJason A. Donenfeld if (!len)
55d8f1308aSJason A. Donenfeld curve25519_generate_secret(secret);
56d8f1308aSJason A. Donenfeld else if (len == CURVE25519_KEY_SIZE &&
57d8f1308aSJason A. Donenfeld crypto_memneq(buf, curve25519_null_point, CURVE25519_KEY_SIZE))
58d8f1308aSJason A. Donenfeld memcpy(secret, buf, CURVE25519_KEY_SIZE);
59d8f1308aSJason A. Donenfeld else
60d8f1308aSJason A. Donenfeld return -EINVAL;
61d8f1308aSJason A. Donenfeld return 0;
62d8f1308aSJason A. Donenfeld }
63d8f1308aSJason A. Donenfeld
curve25519_compute_value(struct kpp_request * req)64d8f1308aSJason A. Donenfeld static int curve25519_compute_value(struct kpp_request *req)
65d8f1308aSJason A. Donenfeld {
66d8f1308aSJason A. Donenfeld struct crypto_kpp *tfm = crypto_kpp_reqtfm(req);
67d8f1308aSJason A. Donenfeld const u8 *secret = kpp_tfm_ctx(tfm);
68d8f1308aSJason A. Donenfeld u8 public_key[CURVE25519_KEY_SIZE];
69d8f1308aSJason A. Donenfeld u8 buf[CURVE25519_KEY_SIZE];
70d8f1308aSJason A. Donenfeld int copied, nbytes;
71d8f1308aSJason A. Donenfeld u8 const *bp;
72d8f1308aSJason A. Donenfeld
73d8f1308aSJason A. Donenfeld if (req->src) {
74d8f1308aSJason A. Donenfeld copied = sg_copy_to_buffer(req->src,
75d8f1308aSJason A. Donenfeld sg_nents_for_len(req->src,
76d8f1308aSJason A. Donenfeld CURVE25519_KEY_SIZE),
77d8f1308aSJason A. Donenfeld public_key, CURVE25519_KEY_SIZE);
78d8f1308aSJason A. Donenfeld if (copied != CURVE25519_KEY_SIZE)
79d8f1308aSJason A. Donenfeld return -EINVAL;
80d8f1308aSJason A. Donenfeld bp = public_key;
81d8f1308aSJason A. Donenfeld } else {
82d8f1308aSJason A. Donenfeld bp = curve25519_base_point;
83d8f1308aSJason A. Donenfeld }
84d8f1308aSJason A. Donenfeld
85d8f1308aSJason A. Donenfeld curve25519_arch(buf, secret, bp);
86d8f1308aSJason A. Donenfeld
87d8f1308aSJason A. Donenfeld /* might want less than we've got */
88d8f1308aSJason A. Donenfeld nbytes = min_t(size_t, CURVE25519_KEY_SIZE, req->dst_len);
89d8f1308aSJason A. Donenfeld copied = sg_copy_from_buffer(req->dst, sg_nents_for_len(req->dst,
90d8f1308aSJason A. Donenfeld nbytes),
91d8f1308aSJason A. Donenfeld buf, nbytes);
92d8f1308aSJason A. Donenfeld if (copied != nbytes)
93d8f1308aSJason A. Donenfeld return -EINVAL;
94d8f1308aSJason A. Donenfeld return 0;
95d8f1308aSJason A. Donenfeld }
96d8f1308aSJason A. Donenfeld
curve25519_max_size(struct crypto_kpp * tfm)97d8f1308aSJason A. Donenfeld static unsigned int curve25519_max_size(struct crypto_kpp *tfm)
98d8f1308aSJason A. Donenfeld {
99d8f1308aSJason A. Donenfeld return CURVE25519_KEY_SIZE;
100d8f1308aSJason A. Donenfeld }
101d8f1308aSJason A. Donenfeld
102d8f1308aSJason A. Donenfeld static struct kpp_alg curve25519_alg = {
103d8f1308aSJason A. Donenfeld .base.cra_name = "curve25519",
104d8f1308aSJason A. Donenfeld .base.cra_driver_name = "curve25519-neon",
105d8f1308aSJason A. Donenfeld .base.cra_priority = 200,
106d8f1308aSJason A. Donenfeld .base.cra_module = THIS_MODULE,
107d8f1308aSJason A. Donenfeld .base.cra_ctxsize = CURVE25519_KEY_SIZE,
108d8f1308aSJason A. Donenfeld
109d8f1308aSJason A. Donenfeld .set_secret = curve25519_set_secret,
110d8f1308aSJason A. Donenfeld .generate_public_key = curve25519_compute_value,
111d8f1308aSJason A. Donenfeld .compute_shared_secret = curve25519_compute_value,
112d8f1308aSJason A. Donenfeld .max_size = curve25519_max_size,
113d8f1308aSJason A. Donenfeld };
114d8f1308aSJason A. Donenfeld
arm_curve25519_init(void)115*cb5f09e8SRandy Dunlap static int __init arm_curve25519_init(void)
116d8f1308aSJason A. Donenfeld {
117d8f1308aSJason A. Donenfeld if (elf_hwcap & HWCAP_NEON) {
118d8f1308aSJason A. Donenfeld static_branch_enable(&have_neon);
1198394bfecSJason A. Donenfeld return IS_REACHABLE(CONFIG_CRYPTO_KPP) ?
1208394bfecSJason A. Donenfeld crypto_register_kpp(&curve25519_alg) : 0;
121d8f1308aSJason A. Donenfeld }
122d8f1308aSJason A. Donenfeld return 0;
123d8f1308aSJason A. Donenfeld }
124d8f1308aSJason A. Donenfeld
arm_curve25519_exit(void)125*cb5f09e8SRandy Dunlap static void __exit arm_curve25519_exit(void)
126d8f1308aSJason A. Donenfeld {
1278394bfecSJason A. Donenfeld if (IS_REACHABLE(CONFIG_CRYPTO_KPP) && elf_hwcap & HWCAP_NEON)
128d8f1308aSJason A. Donenfeld crypto_unregister_kpp(&curve25519_alg);
129d8f1308aSJason A. Donenfeld }
130d8f1308aSJason A. Donenfeld
131*cb5f09e8SRandy Dunlap module_init(arm_curve25519_init);
132*cb5f09e8SRandy Dunlap module_exit(arm_curve25519_exit);
133d8f1308aSJason A. Donenfeld
134d8f1308aSJason A. Donenfeld MODULE_ALIAS_CRYPTO("curve25519");
135d8f1308aSJason A. Donenfeld MODULE_ALIAS_CRYPTO("curve25519-neon");
136d8f1308aSJason A. Donenfeld MODULE_LICENSE("GPL v2");
137