1 // SPDX-License-Identifier: GPL-2.0
3 * OpenSSL/Cryptogams accelerated Poly1305 transform for ARM
5 * Copyright (C) 2019 Linaro Ltd. <ard.biesheuvel@linaro.org>
11 #include <asm/unaligned.h>
12 #include <crypto/algapi.h>
13 #include <crypto/internal/hash.h>
14 #include <crypto/internal/poly1305.h>
15 #include <crypto/internal/simd.h>
16 #include <linux/cpufeature.h>
17 #include <linux/crypto.h>
18 #include <linux/jump_label.h>
19 #include <linux/module.h>
21 void poly1305_init_arm(void *state
, const u8
*key
);
22 void poly1305_blocks_arm(void *state
, const u8
*src
, u32 len
, u32 hibit
);
23 void poly1305_blocks_neon(void *state
, const u8
*src
, u32 len
, u32 hibit
);
24 void poly1305_emit_arm(void *state
, u8
*digest
, const u32
*nonce
);
26 void __weak
poly1305_blocks_neon(void *state
, const u8
*src
, u32 len
, u32 hibit
)
30 static __ro_after_init
DEFINE_STATIC_KEY_FALSE(have_neon
);
32 void poly1305_init_arch(struct poly1305_desc_ctx
*dctx
, const u8
*key
)
34 poly1305_init_arm(&dctx
->h
, key
);
35 dctx
->s
[0] = get_unaligned_le32(key
+ 16);
36 dctx
->s
[1] = get_unaligned_le32(key
+ 20);
37 dctx
->s
[2] = get_unaligned_le32(key
+ 24);
38 dctx
->s
[3] = get_unaligned_le32(key
+ 28);
41 EXPORT_SYMBOL(poly1305_init_arch
);
43 static int arm_poly1305_init(struct shash_desc
*desc
)
45 struct poly1305_desc_ctx
*dctx
= shash_desc_ctx(desc
);
54 static void arm_poly1305_blocks(struct poly1305_desc_ctx
*dctx
, const u8
*src
,
55 u32 len
, u32 hibit
, bool do_neon
)
57 if (unlikely(!dctx
->sset
)) {
59 poly1305_init_arm(&dctx
->h
, src
);
60 src
+= POLY1305_BLOCK_SIZE
;
61 len
-= POLY1305_BLOCK_SIZE
;
64 if (len
>= POLY1305_BLOCK_SIZE
) {
65 dctx
->s
[0] = get_unaligned_le32(src
+ 0);
66 dctx
->s
[1] = get_unaligned_le32(src
+ 4);
67 dctx
->s
[2] = get_unaligned_le32(src
+ 8);
68 dctx
->s
[3] = get_unaligned_le32(src
+ 12);
69 src
+= POLY1305_BLOCK_SIZE
;
70 len
-= POLY1305_BLOCK_SIZE
;
73 if (len
< POLY1305_BLOCK_SIZE
)
77 len
&= ~(POLY1305_BLOCK_SIZE
- 1);
79 if (static_branch_likely(&have_neon
) && likely(do_neon
))
80 poly1305_blocks_neon(&dctx
->h
, src
, len
, hibit
);
82 poly1305_blocks_arm(&dctx
->h
, src
, len
, hibit
);
85 static void arm_poly1305_do_update(struct poly1305_desc_ctx
*dctx
,
86 const u8
*src
, u32 len
, bool do_neon
)
88 if (unlikely(dctx
->buflen
)) {
89 u32 bytes
= min(len
, POLY1305_BLOCK_SIZE
- dctx
->buflen
);
91 memcpy(dctx
->buf
+ dctx
->buflen
, src
, bytes
);
94 dctx
->buflen
+= bytes
;
96 if (dctx
->buflen
== POLY1305_BLOCK_SIZE
) {
97 arm_poly1305_blocks(dctx
, dctx
->buf
,
98 POLY1305_BLOCK_SIZE
, 1, false);
103 if (likely(len
>= POLY1305_BLOCK_SIZE
)) {
104 arm_poly1305_blocks(dctx
, src
, len
, 1, do_neon
);
105 src
+= round_down(len
, POLY1305_BLOCK_SIZE
);
106 len
%= POLY1305_BLOCK_SIZE
;
111 memcpy(dctx
->buf
, src
, len
);
115 static int arm_poly1305_update(struct shash_desc
*desc
,
116 const u8
*src
, unsigned int srclen
)
118 struct poly1305_desc_ctx
*dctx
= shash_desc_ctx(desc
);
120 arm_poly1305_do_update(dctx
, src
, srclen
, false);
124 static int __maybe_unused
arm_poly1305_update_neon(struct shash_desc
*desc
,
128 struct poly1305_desc_ctx
*dctx
= shash_desc_ctx(desc
);
129 bool do_neon
= crypto_simd_usable() && srclen
> 128;
131 if (static_branch_likely(&have_neon
) && do_neon
)
133 arm_poly1305_do_update(dctx
, src
, srclen
, do_neon
);
134 if (static_branch_likely(&have_neon
) && do_neon
)
139 void poly1305_update_arch(struct poly1305_desc_ctx
*dctx
, const u8
*src
,
142 bool do_neon
= IS_ENABLED(CONFIG_KERNEL_MODE_NEON
) &&
143 crypto_simd_usable();
145 if (unlikely(dctx
->buflen
)) {
146 u32 bytes
= min(nbytes
, POLY1305_BLOCK_SIZE
- dctx
->buflen
);
148 memcpy(dctx
->buf
+ dctx
->buflen
, src
, bytes
);
151 dctx
->buflen
+= bytes
;
153 if (dctx
->buflen
== POLY1305_BLOCK_SIZE
) {
154 poly1305_blocks_arm(&dctx
->h
, dctx
->buf
,
155 POLY1305_BLOCK_SIZE
, 1);
160 if (likely(nbytes
>= POLY1305_BLOCK_SIZE
)) {
161 unsigned int len
= round_down(nbytes
, POLY1305_BLOCK_SIZE
);
163 if (static_branch_likely(&have_neon
) && do_neon
) {
165 unsigned int todo
= min_t(unsigned int, len
, SZ_4K
);
168 poly1305_blocks_neon(&dctx
->h
, src
, todo
, 1);
175 poly1305_blocks_arm(&dctx
->h
, src
, len
, 1);
178 nbytes
%= POLY1305_BLOCK_SIZE
;
181 if (unlikely(nbytes
)) {
182 dctx
->buflen
= nbytes
;
183 memcpy(dctx
->buf
, src
, nbytes
);
186 EXPORT_SYMBOL(poly1305_update_arch
);
188 void poly1305_final_arch(struct poly1305_desc_ctx
*dctx
, u8
*dst
)
190 if (unlikely(dctx
->buflen
)) {
191 dctx
->buf
[dctx
->buflen
++] = 1;
192 memset(dctx
->buf
+ dctx
->buflen
, 0,
193 POLY1305_BLOCK_SIZE
- dctx
->buflen
);
194 poly1305_blocks_arm(&dctx
->h
, dctx
->buf
, POLY1305_BLOCK_SIZE
, 0);
197 poly1305_emit_arm(&dctx
->h
, dst
, dctx
->s
);
198 *dctx
= (struct poly1305_desc_ctx
){};
200 EXPORT_SYMBOL(poly1305_final_arch
);
202 static int arm_poly1305_final(struct shash_desc
*desc
, u8
*dst
)
204 struct poly1305_desc_ctx
*dctx
= shash_desc_ctx(desc
);
206 if (unlikely(!dctx
->sset
))
209 poly1305_final_arch(dctx
, dst
);
213 static struct shash_alg arm_poly1305_algs
[] = {{
214 .init
= arm_poly1305_init
,
215 .update
= arm_poly1305_update
,
216 .final
= arm_poly1305_final
,
217 .digestsize
= POLY1305_DIGEST_SIZE
,
218 .descsize
= sizeof(struct poly1305_desc_ctx
),
220 .base
.cra_name
= "poly1305",
221 .base
.cra_driver_name
= "poly1305-arm",
222 .base
.cra_priority
= 150,
223 .base
.cra_blocksize
= POLY1305_BLOCK_SIZE
,
224 .base
.cra_module
= THIS_MODULE
,
225 #ifdef CONFIG_KERNEL_MODE_NEON
227 .init
= arm_poly1305_init
,
228 .update
= arm_poly1305_update_neon
,
229 .final
= arm_poly1305_final
,
230 .digestsize
= POLY1305_DIGEST_SIZE
,
231 .descsize
= sizeof(struct poly1305_desc_ctx
),
233 .base
.cra_name
= "poly1305",
234 .base
.cra_driver_name
= "poly1305-neon",
235 .base
.cra_priority
= 200,
236 .base
.cra_blocksize
= POLY1305_BLOCK_SIZE
,
237 .base
.cra_module
= THIS_MODULE
,
241 static int __init
arm_poly1305_mod_init(void)
243 if (IS_ENABLED(CONFIG_KERNEL_MODE_NEON
) &&
244 (elf_hwcap
& HWCAP_NEON
))
245 static_branch_enable(&have_neon
);
246 else if (IS_REACHABLE(CONFIG_CRYPTO_HASH
))
247 /* register only the first entry */
248 return crypto_register_shash(&arm_poly1305_algs
[0]);
250 return IS_REACHABLE(CONFIG_CRYPTO_HASH
) ?
251 crypto_register_shashes(arm_poly1305_algs
,
252 ARRAY_SIZE(arm_poly1305_algs
)) : 0;
255 static void __exit
arm_poly1305_mod_exit(void)
257 if (!IS_REACHABLE(CONFIG_CRYPTO_HASH
))
259 if (!static_branch_likely(&have_neon
)) {
260 crypto_unregister_shash(&arm_poly1305_algs
[0]);
263 crypto_unregister_shashes(arm_poly1305_algs
,
264 ARRAY_SIZE(arm_poly1305_algs
));
267 module_init(arm_poly1305_mod_init
);
268 module_exit(arm_poly1305_mod_exit
);
270 MODULE_LICENSE("GPL v2");
271 MODULE_ALIAS_CRYPTO("poly1305");
272 MODULE_ALIAS_CRYPTO("poly1305-arm");
273 MODULE_ALIAS_CRYPTO("poly1305-neon");