xref: /linux/arch/arm/crypto/chacha-neon-core.S (revision b7019ac550eb3916f34d79db583e9b7ea2524afa)
1/*
2 * ChaCha/XChaCha NEON helper functions
3 *
4 * Copyright (C) 2016 Linaro, Ltd. <ard.biesheuvel@linaro.org>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 *
10 * Based on:
11 * ChaCha20 256-bit cipher algorithm, RFC7539, x64 SSE3 functions
12 *
13 * Copyright (C) 2015 Martin Willi
14 *
15 * This program is free software; you can redistribute it and/or modify
16 * it under the terms of the GNU General Public License as published by
17 * the Free Software Foundation; either version 2 of the License, or
18 * (at your option) any later version.
19 */
20
21 /*
22  * NEON doesn't have a rotate instruction.  The alternatives are, more or less:
23  *
24  * (a)  vshl.u32 + vsri.u32		(needs temporary register)
25  * (b)  vshl.u32 + vshr.u32 + vorr	(needs temporary register)
26  * (c)  vrev32.16			(16-bit rotations only)
27  * (d)  vtbl.8 + vtbl.8		(multiple of 8 bits rotations only,
28  *					 needs index vector)
29  *
30  * ChaCha has 16, 12, 8, and 7-bit rotations.  For the 12 and 7-bit rotations,
31  * the only choices are (a) and (b).  We use (a) since it takes two-thirds the
32  * cycles of (b) on both Cortex-A7 and Cortex-A53.
33  *
34  * For the 16-bit rotation, we use vrev32.16 since it's consistently fastest
35  * and doesn't need a temporary register.
36  *
37  * For the 8-bit rotation, we use vtbl.8 + vtbl.8.  On Cortex-A7, this sequence
38  * is twice as fast as (a), even when doing (a) on multiple registers
39  * simultaneously to eliminate the stall between vshl and vsri.  Also, it
40  * parallelizes better when temporary registers are scarce.
41  *
42  * A disadvantage is that on Cortex-A53, the vtbl sequence is the same speed as
43  * (a), so the need to load the rotation table actually makes the vtbl method
44  * slightly slower overall on that CPU (~1.3% slower ChaCha20).  Still, it
45  * seems to be a good compromise to get a more significant speed boost on some
46  * CPUs, e.g. ~4.8% faster ChaCha20 on Cortex-A7.
47  */
48
49#include <linux/linkage.h>
50
51	.text
52	.fpu		neon
53	.align		5
54
55/*
56 * chacha_permute - permute one block
57 *
58 * Permute one 64-byte block where the state matrix is stored in the four NEON
59 * registers q0-q3.  It performs matrix operations on four words in parallel,
60 * but requires shuffling to rearrange the words after each round.
61 *
62 * The round count is given in r3.
63 *
64 * Clobbers: r3, ip, q4-q5
65 */
66chacha_permute:
67
68	adr		ip, .Lrol8_table
69	vld1.8		{d10}, [ip, :64]
70
71.Ldoubleround:
72	// x0 += x1, x3 = rotl32(x3 ^ x0, 16)
73	vadd.i32	q0, q0, q1
74	veor		q3, q3, q0
75	vrev32.16	q3, q3
76
77	// x2 += x3, x1 = rotl32(x1 ^ x2, 12)
78	vadd.i32	q2, q2, q3
79	veor		q4, q1, q2
80	vshl.u32	q1, q4, #12
81	vsri.u32	q1, q4, #20
82
83	// x0 += x1, x3 = rotl32(x3 ^ x0, 8)
84	vadd.i32	q0, q0, q1
85	veor		q3, q3, q0
86	vtbl.8		d6, {d6}, d10
87	vtbl.8		d7, {d7}, d10
88
89	// x2 += x3, x1 = rotl32(x1 ^ x2, 7)
90	vadd.i32	q2, q2, q3
91	veor		q4, q1, q2
92	vshl.u32	q1, q4, #7
93	vsri.u32	q1, q4, #25
94
95	// x1 = shuffle32(x1, MASK(0, 3, 2, 1))
96	vext.8		q1, q1, q1, #4
97	// x2 = shuffle32(x2, MASK(1, 0, 3, 2))
98	vext.8		q2, q2, q2, #8
99	// x3 = shuffle32(x3, MASK(2, 1, 0, 3))
100	vext.8		q3, q3, q3, #12
101
102	// x0 += x1, x3 = rotl32(x3 ^ x0, 16)
103	vadd.i32	q0, q0, q1
104	veor		q3, q3, q0
105	vrev32.16	q3, q3
106
107	// x2 += x3, x1 = rotl32(x1 ^ x2, 12)
108	vadd.i32	q2, q2, q3
109	veor		q4, q1, q2
110	vshl.u32	q1, q4, #12
111	vsri.u32	q1, q4, #20
112
113	// x0 += x1, x3 = rotl32(x3 ^ x0, 8)
114	vadd.i32	q0, q0, q1
115	veor		q3, q3, q0
116	vtbl.8		d6, {d6}, d10
117	vtbl.8		d7, {d7}, d10
118
119	// x2 += x3, x1 = rotl32(x1 ^ x2, 7)
120	vadd.i32	q2, q2, q3
121	veor		q4, q1, q2
122	vshl.u32	q1, q4, #7
123	vsri.u32	q1, q4, #25
124
125	// x1 = shuffle32(x1, MASK(2, 1, 0, 3))
126	vext.8		q1, q1, q1, #12
127	// x2 = shuffle32(x2, MASK(1, 0, 3, 2))
128	vext.8		q2, q2, q2, #8
129	// x3 = shuffle32(x3, MASK(0, 3, 2, 1))
130	vext.8		q3, q3, q3, #4
131
132	subs		r3, r3, #2
133	bne		.Ldoubleround
134
135	bx		lr
136ENDPROC(chacha_permute)
137
138ENTRY(chacha_block_xor_neon)
139	// r0: Input state matrix, s
140	// r1: 1 data block output, o
141	// r2: 1 data block input, i
142	// r3: nrounds
143	push		{lr}
144
145	// x0..3 = s0..3
146	add		ip, r0, #0x20
147	vld1.32		{q0-q1}, [r0]
148	vld1.32		{q2-q3}, [ip]
149
150	vmov		q8, q0
151	vmov		q9, q1
152	vmov		q10, q2
153	vmov		q11, q3
154
155	bl		chacha_permute
156
157	add		ip, r2, #0x20
158	vld1.8		{q4-q5}, [r2]
159	vld1.8		{q6-q7}, [ip]
160
161	// o0 = i0 ^ (x0 + s0)
162	vadd.i32	q0, q0, q8
163	veor		q0, q0, q4
164
165	// o1 = i1 ^ (x1 + s1)
166	vadd.i32	q1, q1, q9
167	veor		q1, q1, q5
168
169	// o2 = i2 ^ (x2 + s2)
170	vadd.i32	q2, q2, q10
171	veor		q2, q2, q6
172
173	// o3 = i3 ^ (x3 + s3)
174	vadd.i32	q3, q3, q11
175	veor		q3, q3, q7
176
177	add		ip, r1, #0x20
178	vst1.8		{q0-q1}, [r1]
179	vst1.8		{q2-q3}, [ip]
180
181	pop		{pc}
182ENDPROC(chacha_block_xor_neon)
183
184ENTRY(hchacha_block_neon)
185	// r0: Input state matrix, s
186	// r1: output (8 32-bit words)
187	// r2: nrounds
188	push		{lr}
189
190	vld1.32		{q0-q1}, [r0]!
191	vld1.32		{q2-q3}, [r0]
192
193	mov		r3, r2
194	bl		chacha_permute
195
196	vst1.32		{q0}, [r1]!
197	vst1.32		{q3}, [r1]
198
199	pop		{pc}
200ENDPROC(hchacha_block_neon)
201
202	.align		4
203.Lctrinc:	.word	0, 1, 2, 3
204.Lrol8_table:	.byte	3, 0, 1, 2, 7, 4, 5, 6
205
206	.align		5
207ENTRY(chacha_4block_xor_neon)
208	push		{r4-r5}
209	mov		r4, sp			// preserve the stack pointer
210	sub		ip, sp, #0x20		// allocate a 32 byte buffer
211	bic		ip, ip, #0x1f		// aligned to 32 bytes
212	mov		sp, ip
213
214	// r0: Input state matrix, s
215	// r1: 4 data blocks output, o
216	// r2: 4 data blocks input, i
217	// r3: nrounds
218
219	//
220	// This function encrypts four consecutive ChaCha blocks by loading
221	// the state matrix in NEON registers four times. The algorithm performs
222	// each operation on the corresponding word of each state matrix, hence
223	// requires no word shuffling. The words are re-interleaved before the
224	// final addition of the original state and the XORing step.
225	//
226
227	// x0..15[0-3] = s0..15[0-3]
228	add		ip, r0, #0x20
229	vld1.32		{q0-q1}, [r0]
230	vld1.32		{q2-q3}, [ip]
231
232	adr		r5, .Lctrinc
233	vdup.32		q15, d7[1]
234	vdup.32		q14, d7[0]
235	vld1.32		{q4}, [r5, :128]
236	vdup.32		q13, d6[1]
237	vdup.32		q12, d6[0]
238	vdup.32		q11, d5[1]
239	vdup.32		q10, d5[0]
240	vadd.u32	q12, q12, q4		// x12 += counter values 0-3
241	vdup.32		q9, d4[1]
242	vdup.32		q8, d4[0]
243	vdup.32		q7, d3[1]
244	vdup.32		q6, d3[0]
245	vdup.32		q5, d2[1]
246	vdup.32		q4, d2[0]
247	vdup.32		q3, d1[1]
248	vdup.32		q2, d1[0]
249	vdup.32		q1, d0[1]
250	vdup.32		q0, d0[0]
251
252	adr		ip, .Lrol8_table
253	b		1f
254
255.Ldoubleround4:
256	vld1.32		{q8-q9}, [sp, :256]
2571:
258	// x0 += x4, x12 = rotl32(x12 ^ x0, 16)
259	// x1 += x5, x13 = rotl32(x13 ^ x1, 16)
260	// x2 += x6, x14 = rotl32(x14 ^ x2, 16)
261	// x3 += x7, x15 = rotl32(x15 ^ x3, 16)
262	vadd.i32	q0, q0, q4
263	vadd.i32	q1, q1, q5
264	vadd.i32	q2, q2, q6
265	vadd.i32	q3, q3, q7
266
267	veor		q12, q12, q0
268	veor		q13, q13, q1
269	veor		q14, q14, q2
270	veor		q15, q15, q3
271
272	vrev32.16	q12, q12
273	vrev32.16	q13, q13
274	vrev32.16	q14, q14
275	vrev32.16	q15, q15
276
277	// x8 += x12, x4 = rotl32(x4 ^ x8, 12)
278	// x9 += x13, x5 = rotl32(x5 ^ x9, 12)
279	// x10 += x14, x6 = rotl32(x6 ^ x10, 12)
280	// x11 += x15, x7 = rotl32(x7 ^ x11, 12)
281	vadd.i32	q8, q8, q12
282	vadd.i32	q9, q9, q13
283	vadd.i32	q10, q10, q14
284	vadd.i32	q11, q11, q15
285
286	vst1.32		{q8-q9}, [sp, :256]
287
288	veor		q8, q4, q8
289	veor		q9, q5, q9
290	vshl.u32	q4, q8, #12
291	vshl.u32	q5, q9, #12
292	vsri.u32	q4, q8, #20
293	vsri.u32	q5, q9, #20
294
295	veor		q8, q6, q10
296	veor		q9, q7, q11
297	vshl.u32	q6, q8, #12
298	vshl.u32	q7, q9, #12
299	vsri.u32	q6, q8, #20
300	vsri.u32	q7, q9, #20
301
302	// x0 += x4, x12 = rotl32(x12 ^ x0, 8)
303	// x1 += x5, x13 = rotl32(x13 ^ x1, 8)
304	// x2 += x6, x14 = rotl32(x14 ^ x2, 8)
305	// x3 += x7, x15 = rotl32(x15 ^ x3, 8)
306	vld1.8		{d16}, [ip, :64]
307	vadd.i32	q0, q0, q4
308	vadd.i32	q1, q1, q5
309	vadd.i32	q2, q2, q6
310	vadd.i32	q3, q3, q7
311
312	veor		q12, q12, q0
313	veor		q13, q13, q1
314	veor		q14, q14, q2
315	veor		q15, q15, q3
316
317	vtbl.8		d24, {d24}, d16
318	vtbl.8		d25, {d25}, d16
319	vtbl.8		d26, {d26}, d16
320	vtbl.8		d27, {d27}, d16
321	vtbl.8		d28, {d28}, d16
322	vtbl.8		d29, {d29}, d16
323	vtbl.8		d30, {d30}, d16
324	vtbl.8		d31, {d31}, d16
325
326	vld1.32		{q8-q9}, [sp, :256]
327
328	// x8 += x12, x4 = rotl32(x4 ^ x8, 7)
329	// x9 += x13, x5 = rotl32(x5 ^ x9, 7)
330	// x10 += x14, x6 = rotl32(x6 ^ x10, 7)
331	// x11 += x15, x7 = rotl32(x7 ^ x11, 7)
332	vadd.i32	q8, q8, q12
333	vadd.i32	q9, q9, q13
334	vadd.i32	q10, q10, q14
335	vadd.i32	q11, q11, q15
336
337	vst1.32		{q8-q9}, [sp, :256]
338
339	veor		q8, q4, q8
340	veor		q9, q5, q9
341	vshl.u32	q4, q8, #7
342	vshl.u32	q5, q9, #7
343	vsri.u32	q4, q8, #25
344	vsri.u32	q5, q9, #25
345
346	veor		q8, q6, q10
347	veor		q9, q7, q11
348	vshl.u32	q6, q8, #7
349	vshl.u32	q7, q9, #7
350	vsri.u32	q6, q8, #25
351	vsri.u32	q7, q9, #25
352
353	vld1.32		{q8-q9}, [sp, :256]
354
355	// x0 += x5, x15 = rotl32(x15 ^ x0, 16)
356	// x1 += x6, x12 = rotl32(x12 ^ x1, 16)
357	// x2 += x7, x13 = rotl32(x13 ^ x2, 16)
358	// x3 += x4, x14 = rotl32(x14 ^ x3, 16)
359	vadd.i32	q0, q0, q5
360	vadd.i32	q1, q1, q6
361	vadd.i32	q2, q2, q7
362	vadd.i32	q3, q3, q4
363
364	veor		q15, q15, q0
365	veor		q12, q12, q1
366	veor		q13, q13, q2
367	veor		q14, q14, q3
368
369	vrev32.16	q15, q15
370	vrev32.16	q12, q12
371	vrev32.16	q13, q13
372	vrev32.16	q14, q14
373
374	// x10 += x15, x5 = rotl32(x5 ^ x10, 12)
375	// x11 += x12, x6 = rotl32(x6 ^ x11, 12)
376	// x8 += x13, x7 = rotl32(x7 ^ x8, 12)
377	// x9 += x14, x4 = rotl32(x4 ^ x9, 12)
378	vadd.i32	q10, q10, q15
379	vadd.i32	q11, q11, q12
380	vadd.i32	q8, q8, q13
381	vadd.i32	q9, q9, q14
382
383	vst1.32		{q8-q9}, [sp, :256]
384
385	veor		q8, q7, q8
386	veor		q9, q4, q9
387	vshl.u32	q7, q8, #12
388	vshl.u32	q4, q9, #12
389	vsri.u32	q7, q8, #20
390	vsri.u32	q4, q9, #20
391
392	veor		q8, q5, q10
393	veor		q9, q6, q11
394	vshl.u32	q5, q8, #12
395	vshl.u32	q6, q9, #12
396	vsri.u32	q5, q8, #20
397	vsri.u32	q6, q9, #20
398
399	// x0 += x5, x15 = rotl32(x15 ^ x0, 8)
400	// x1 += x6, x12 = rotl32(x12 ^ x1, 8)
401	// x2 += x7, x13 = rotl32(x13 ^ x2, 8)
402	// x3 += x4, x14 = rotl32(x14 ^ x3, 8)
403	vld1.8		{d16}, [ip, :64]
404	vadd.i32	q0, q0, q5
405	vadd.i32	q1, q1, q6
406	vadd.i32	q2, q2, q7
407	vadd.i32	q3, q3, q4
408
409	veor		q15, q15, q0
410	veor		q12, q12, q1
411	veor		q13, q13, q2
412	veor		q14, q14, q3
413
414	vtbl.8		d30, {d30}, d16
415	vtbl.8		d31, {d31}, d16
416	vtbl.8		d24, {d24}, d16
417	vtbl.8		d25, {d25}, d16
418	vtbl.8		d26, {d26}, d16
419	vtbl.8		d27, {d27}, d16
420	vtbl.8		d28, {d28}, d16
421	vtbl.8		d29, {d29}, d16
422
423	vld1.32		{q8-q9}, [sp, :256]
424
425	// x10 += x15, x5 = rotl32(x5 ^ x10, 7)
426	// x11 += x12, x6 = rotl32(x6 ^ x11, 7)
427	// x8 += x13, x7 = rotl32(x7 ^ x8, 7)
428	// x9 += x14, x4 = rotl32(x4 ^ x9, 7)
429	vadd.i32	q10, q10, q15
430	vadd.i32	q11, q11, q12
431	vadd.i32	q8, q8, q13
432	vadd.i32	q9, q9, q14
433
434	vst1.32		{q8-q9}, [sp, :256]
435
436	veor		q8, q7, q8
437	veor		q9, q4, q9
438	vshl.u32	q7, q8, #7
439	vshl.u32	q4, q9, #7
440	vsri.u32	q7, q8, #25
441	vsri.u32	q4, q9, #25
442
443	veor		q8, q5, q10
444	veor		q9, q6, q11
445	vshl.u32	q5, q8, #7
446	vshl.u32	q6, q9, #7
447	vsri.u32	q5, q8, #25
448	vsri.u32	q6, q9, #25
449
450	subs		r3, r3, #2
451	bne		.Ldoubleround4
452
453	// x0..7[0-3] are in q0-q7, x10..15[0-3] are in q10-q15.
454	// x8..9[0-3] are on the stack.
455
456	// Re-interleave the words in the first two rows of each block (x0..7).
457	// Also add the counter values 0-3 to x12[0-3].
458	  vld1.32	{q8}, [r5, :128]	// load counter values 0-3
459	vzip.32		q0, q1			// => (0 1 0 1) (0 1 0 1)
460	vzip.32		q2, q3			// => (2 3 2 3) (2 3 2 3)
461	vzip.32		q4, q5			// => (4 5 4 5) (4 5 4 5)
462	vzip.32		q6, q7			// => (6 7 6 7) (6 7 6 7)
463	  vadd.u32	q12, q8			// x12 += counter values 0-3
464	vswp		d1, d4
465	vswp		d3, d6
466	  vld1.32	{q8-q9}, [r0]!		// load s0..7
467	vswp		d9, d12
468	vswp		d11, d14
469
470	// Swap q1 and q4 so that we'll free up consecutive registers (q0-q1)
471	// after XORing the first 32 bytes.
472	vswp		q1, q4
473
474	// First two rows of each block are (q0 q1) (q2 q6) (q4 q5) (q3 q7)
475
476	// x0..3[0-3] += s0..3[0-3]	(add orig state to 1st row of each block)
477	vadd.u32	q0, q0, q8
478	vadd.u32	q2, q2, q8
479	vadd.u32	q4, q4, q8
480	vadd.u32	q3, q3, q8
481
482	// x4..7[0-3] += s4..7[0-3]	(add orig state to 2nd row of each block)
483	vadd.u32	q1, q1, q9
484	vadd.u32	q6, q6, q9
485	vadd.u32	q5, q5, q9
486	vadd.u32	q7, q7, q9
487
488	// XOR first 32 bytes using keystream from first two rows of first block
489	vld1.8		{q8-q9}, [r2]!
490	veor		q8, q8, q0
491	veor		q9, q9, q1
492	vst1.8		{q8-q9}, [r1]!
493
494	// Re-interleave the words in the last two rows of each block (x8..15).
495	vld1.32		{q8-q9}, [sp, :256]
496	vzip.32		q12, q13	// => (12 13 12 13) (12 13 12 13)
497	vzip.32		q14, q15	// => (14 15 14 15) (14 15 14 15)
498	vzip.32		q8, q9		// => (8 9 8 9) (8 9 8 9)
499	vzip.32		q10, q11	// => (10 11 10 11) (10 11 10 11)
500	  vld1.32	{q0-q1}, [r0]	// load s8..15
501	vswp		d25, d28
502	vswp		d27, d30
503	vswp		d17, d20
504	vswp		d19, d22
505
506	// Last two rows of each block are (q8 q12) (q10 q14) (q9 q13) (q11 q15)
507
508	// x8..11[0-3] += s8..11[0-3]	(add orig state to 3rd row of each block)
509	vadd.u32	q8,  q8,  q0
510	vadd.u32	q10, q10, q0
511	vadd.u32	q9,  q9,  q0
512	vadd.u32	q11, q11, q0
513
514	// x12..15[0-3] += s12..15[0-3] (add orig state to 4th row of each block)
515	vadd.u32	q12, q12, q1
516	vadd.u32	q14, q14, q1
517	vadd.u32	q13, q13, q1
518	vadd.u32	q15, q15, q1
519
520	// XOR the rest of the data with the keystream
521
522	vld1.8		{q0-q1}, [r2]!
523	veor		q0, q0, q8
524	veor		q1, q1, q12
525	vst1.8		{q0-q1}, [r1]!
526
527	vld1.8		{q0-q1}, [r2]!
528	veor		q0, q0, q2
529	veor		q1, q1, q6
530	vst1.8		{q0-q1}, [r1]!
531
532	vld1.8		{q0-q1}, [r2]!
533	veor		q0, q0, q10
534	veor		q1, q1, q14
535	vst1.8		{q0-q1}, [r1]!
536
537	vld1.8		{q0-q1}, [r2]!
538	veor		q0, q0, q4
539	veor		q1, q1, q5
540	vst1.8		{q0-q1}, [r1]!
541
542	vld1.8		{q0-q1}, [r2]!
543	veor		q0, q0, q9
544	veor		q1, q1, q13
545	vst1.8		{q0-q1}, [r1]!
546
547	vld1.8		{q0-q1}, [r2]!
548	veor		q0, q0, q3
549	veor		q1, q1, q7
550	vst1.8		{q0-q1}, [r1]!
551
552	vld1.8		{q0-q1}, [r2]
553	  mov		sp, r4		// restore original stack pointer
554	veor		q0, q0, q11
555	veor		q1, q1, q15
556	vst1.8		{q0-q1}, [r1]
557
558	pop		{r4-r5}
559	bx		lr
560ENDPROC(chacha_4block_xor_neon)
561