| /* |
| * ChaCha20 256-bit cipher algorithm, RFC7539, ARM NEON functions |
| * |
| * Copyright (C) 2016 Linaro, Ltd. <ard.biesheuvel@linaro.org> |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License version 2 as |
| * published by the Free Software Foundation. |
| * |
| * Based on: |
| * ChaCha20 256-bit cipher algorithm, RFC7539, x64 SSE3 functions |
| * |
| * Copyright (C) 2015 Martin Willi |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; either version 2 of the License, or |
| * (at your option) any later version. |
| */ |
| |
| #include <linux/linkage.h> |
| |
| .text |
| .fpu neon |
| .align 5 |
| |
| ENTRY(chacha20_block_xor_neon) |
| // r0: Input state matrix, s |
| // r1: 1 data block output, o |
| // r2: 1 data block input, i |
| |
| // |
| // This function encrypts one ChaCha20 block by loading the state matrix |
| // in four NEON registers. It performs matrix operation on four words in |
| // parallel, but requireds shuffling to rearrange the words after each |
| // round. |
| // |
| |
| // x0..3 = s0..3 |
| add ip, r0, #0x20 |
| vld1.32 {q0-q1}, [r0] |
| vld1.32 {q2-q3}, [ip] |
| |
| vmov q8, q0 |
| vmov q9, q1 |
| vmov q10, q2 |
| vmov q11, q3 |
| |
| mov r3, #10 |
| |
| .Ldoubleround: |
| // x0 += x1, x3 = rotl32(x3 ^ x0, 16) |
| vadd.i32 q0, q0, q1 |
| veor q3, q3, q0 |
| vrev32.16 q3, q3 |
| |
| // x2 += x3, x1 = rotl32(x1 ^ x2, 12) |
| vadd.i32 q2, q2, q3 |
| veor q4, q1, q2 |
| vshl.u32 q1, q4, #12 |
| vsri.u32 q1, q4, #20 |
| |
| // x0 += x1, x3 = rotl32(x3 ^ x0, 8) |
| vadd.i32 q0, q0, q1 |
| veor q4, q3, q0 |
| vshl.u32 q3, q4, #8 |
| vsri.u32 q3, q4, #24 |
| |
| // x2 += x3, x1 = rotl32(x1 ^ x2, 7) |
| vadd.i32 q2, q2, q3 |
| veor q4, q1, q2 |
| vshl.u32 q1, q4, #7 |
| vsri.u32 q1, q4, #25 |
| |
| // x1 = shuffle32(x1, MASK(0, 3, 2, 1)) |
| vext.8 q1, q1, q1, #4 |
| // x2 = shuffle32(x2, MASK(1, 0, 3, 2)) |
| vext.8 q2, q2, q2, #8 |
| // x3 = shuffle32(x3, MASK(2, 1, 0, 3)) |
| vext.8 q3, q3, q3, #12 |
| |
| // x0 += x1, x3 = rotl32(x3 ^ x0, 16) |
| vadd.i32 q0, q0, q1 |
| veor q3, q3, q0 |
| vrev32.16 q3, q3 |
| |
| // x2 += x3, x1 = rotl32(x1 ^ x2, 12) |
| vadd.i32 q2, q2, q3 |
| veor q4, q1, q2 |
| vshl.u32 q1, q4, #12 |
| vsri.u32 q1, q4, #20 |
| |
| // x0 += x1, x3 = rotl32(x3 ^ x0, 8) |
| vadd.i32 q0, q0, q1 |
| veor q4, q3, q0 |
| vshl.u32 q3, q4, #8 |
| vsri.u32 q3, q4, #24 |
| |
| // x2 += x3, x1 = rotl32(x1 ^ x2, 7) |
| vadd.i32 q2, q2, q3 |
| veor q4, q1, q2 |
| vshl.u32 q1, q4, #7 |
| vsri.u32 q1, q4, #25 |
| |
| // x1 = shuffle32(x1, MASK(2, 1, 0, 3)) |
| vext.8 q1, q1, q1, #12 |
| // x2 = shuffle32(x2, MASK(1, 0, 3, 2)) |
| vext.8 q2, q2, q2, #8 |
| // x3 = shuffle32(x3, MASK(0, 3, 2, 1)) |
| vext.8 q3, q3, q3, #4 |
| |
| subs r3, r3, #1 |
| bne .Ldoubleround |
| |
| add ip, r2, #0x20 |
| vld1.8 {q4-q5}, [r2] |
| vld1.8 {q6-q7}, [ip] |
| |
| // o0 = i0 ^ (x0 + s0) |
| vadd.i32 q0, q0, q8 |
| veor q0, q0, q4 |
| |
| // o1 = i1 ^ (x1 + s1) |
| vadd.i32 q1, q1, q9 |
| veor q1, q1, q5 |
| |
| // o2 = i2 ^ (x2 + s2) |
| vadd.i32 q2, q2, q10 |
| veor q2, q2, q6 |
| |
| // o3 = i3 ^ (x3 + s3) |
| vadd.i32 q3, q3, q11 |
| veor q3, q3, q7 |
| |
| add ip, r1, #0x20 |
| vst1.8 {q0-q1}, [r1] |
| vst1.8 {q2-q3}, [ip] |
| |
| bx lr |
| ENDPROC(chacha20_block_xor_neon) |
| |
| .align 5 |
| ENTRY(chacha20_4block_xor_neon) |
| push {r4-r6, lr} |
| mov ip, sp // preserve the stack pointer |
| sub r3, sp, #0x20 // allocate a 32 byte buffer |
| bic r3, r3, #0x1f // aligned to 32 bytes |
| mov sp, r3 |
| |
| // r0: Input state matrix, s |
| // r1: 4 data blocks output, o |
| // r2: 4 data blocks input, i |
| |
| // |
| // This function encrypts four consecutive ChaCha20 blocks by loading |
| // the state matrix in NEON registers four times. The algorithm performs |
| // each operation on the corresponding word of each state matrix, hence |
| // requires no word shuffling. For final XORing step we transpose the |
| // matrix by interleaving 32- and then 64-bit words, which allows us to |
| // do XOR in NEON registers. |
| // |
| |
| // x0..15[0-3] = s0..3[0..3] |
| add r3, r0, #0x20 |
| vld1.32 {q0-q1}, [r0] |
| vld1.32 {q2-q3}, [r3] |
| |
| adr r3, CTRINC |
| vdup.32 q15, d7[1] |
| vdup.32 q14, d7[0] |
| vld1.32 {q11}, [r3, :128] |
| vdup.32 q13, d6[1] |
| vdup.32 q12, d6[0] |
| vadd.i32 q12, q12, q11 // x12 += counter values 0-3 |
| vdup.32 q11, d5[1] |
| vdup.32 q10, d5[0] |
| vdup.32 q9, d4[1] |
| vdup.32 q8, d4[0] |
| vdup.32 q7, d3[1] |
| vdup.32 q6, d3[0] |
| vdup.32 q5, d2[1] |
| vdup.32 q4, d2[0] |
| vdup.32 q3, d1[1] |
| vdup.32 q2, d1[0] |
| vdup.32 q1, d0[1] |
| vdup.32 q0, d0[0] |
| |
| mov r3, #10 |
| |
| .Ldoubleround4: |
| // x0 += x4, x12 = rotl32(x12 ^ x0, 16) |
| // x1 += x5, x13 = rotl32(x13 ^ x1, 16) |
| // x2 += x6, x14 = rotl32(x14 ^ x2, 16) |
| // x3 += x7, x15 = rotl32(x15 ^ x3, 16) |
| vadd.i32 q0, q0, q4 |
| vadd.i32 q1, q1, q5 |
| vadd.i32 q2, q2, q6 |
| vadd.i32 q3, q3, q7 |
| |
| veor q12, q12, q0 |
| veor q13, q13, q1 |
| veor q14, q14, q2 |
| veor q15, q15, q3 |
| |
| vrev32.16 q12, q12 |
| vrev32.16 q13, q13 |
| vrev32.16 q14, q14 |
| vrev32.16 q15, q15 |
| |
| // x8 += x12, x4 = rotl32(x4 ^ x8, 12) |
| // x9 += x13, x5 = rotl32(x5 ^ x9, 12) |
| // x10 += x14, x6 = rotl32(x6 ^ x10, 12) |
| // x11 += x15, x7 = rotl32(x7 ^ x11, 12) |
| vadd.i32 q8, q8, q12 |
| vadd.i32 q9, q9, q13 |
| vadd.i32 q10, q10, q14 |
| vadd.i32 q11, q11, q15 |
| |
| vst1.32 {q8-q9}, [sp, :256] |
| |
| veor q8, q4, q8 |
| veor q9, q5, q9 |
| vshl.u32 q4, q8, #12 |
| vshl.u32 q5, q9, #12 |
| vsri.u32 q4, q8, #20 |
| vsri.u32 q5, q9, #20 |
| |
| veor q8, q6, q10 |
| veor q9, q7, q11 |
| vshl.u32 q6, q8, #12 |
| vshl.u32 q7, q9, #12 |
| vsri.u32 q6, q8, #20 |
| vsri.u32 q7, q9, #20 |
| |
| // x0 += x4, x12 = rotl32(x12 ^ x0, 8) |
| // x1 += x5, x13 = rotl32(x13 ^ x1, 8) |
| // x2 += x6, x14 = rotl32(x14 ^ x2, 8) |
| // x3 += x7, x15 = rotl32(x15 ^ x3, 8) |
| vadd.i32 q0, q0, q4 |
| vadd.i32 q1, q1, q5 |
| vadd.i32 q2, q2, q6 |
| vadd.i32 q3, q3, q7 |
| |
| veor q8, q12, q0 |
| veor q9, q13, q1 |
| vshl.u32 q12, q8, #8 |
| vshl.u32 q13, q9, #8 |
| vsri.u32 q12, q8, #24 |
| vsri.u32 q13, q9, #24 |
| |
| veor q8, q14, q2 |
| veor q9, q15, q3 |
| vshl.u32 q14, q8, #8 |
| vshl.u32 q15, q9, #8 |
| vsri.u32 q14, q8, #24 |
| vsri.u32 q15, q9, #24 |
| |
| vld1.32 {q8-q9}, [sp, :256] |
| |
| // x8 += x12, x4 = rotl32(x4 ^ x8, 7) |
| // x9 += x13, x5 = rotl32(x5 ^ x9, 7) |
| // x10 += x14, x6 = rotl32(x6 ^ x10, 7) |
| // x11 += x15, x7 = rotl32(x7 ^ x11, 7) |
| vadd.i32 q8, q8, q12 |
| vadd.i32 q9, q9, q13 |
| vadd.i32 q10, q10, q14 |
| vadd.i32 q11, q11, q15 |
| |
| vst1.32 {q8-q9}, [sp, :256] |
| |
| veor q8, q4, q8 |
| veor q9, q5, q9 |
| vshl.u32 q4, q8, #7 |
| vshl.u32 q5, q9, #7 |
| vsri.u32 q4, q8, #25 |
| vsri.u32 q5, q9, #25 |
| |
| veor q8, q6, q10 |
| veor q9, q7, q11 |
| vshl.u32 q6, q8, #7 |
| vshl.u32 q7, q9, #7 |
| vsri.u32 q6, q8, #25 |
| vsri.u32 q7, q9, #25 |
| |
| vld1.32 {q8-q9}, [sp, :256] |
| |
| // x0 += x5, x15 = rotl32(x15 ^ x0, 16) |
| // x1 += x6, x12 = rotl32(x12 ^ x1, 16) |
| // x2 += x7, x13 = rotl32(x13 ^ x2, 16) |
| // x3 += x4, x14 = rotl32(x14 ^ x3, 16) |
| vadd.i32 q0, q0, q5 |
| vadd.i32 q1, q1, q6 |
| vadd.i32 q2, q2, q7 |
| vadd.i32 q3, q3, q4 |
| |
| veor q15, q15, q0 |
| veor q12, q12, q1 |
| veor q13, q13, q2 |
| veor q14, q14, q3 |
| |
| vrev32.16 q15, q15 |
| vrev32.16 q12, q12 |
| vrev32.16 q13, q13 |
| vrev32.16 q14, q14 |
| |
| // x10 += x15, x5 = rotl32(x5 ^ x10, 12) |
| // x11 += x12, x6 = rotl32(x6 ^ x11, 12) |
| // x8 += x13, x7 = rotl32(x7 ^ x8, 12) |
| // x9 += x14, x4 = rotl32(x4 ^ x9, 12) |
| vadd.i32 q10, q10, q15 |
| vadd.i32 q11, q11, q12 |
| vadd.i32 q8, q8, q13 |
| vadd.i32 q9, q9, q14 |
| |
| vst1.32 {q8-q9}, [sp, :256] |
| |
| veor q8, q7, q8 |
| veor q9, q4, q9 |
| vshl.u32 q7, q8, #12 |
| vshl.u32 q4, q9, #12 |
| vsri.u32 q7, q8, #20 |
| vsri.u32 q4, q9, #20 |
| |
| veor q8, q5, q10 |
| veor q9, q6, q11 |
| vshl.u32 q5, q8, #12 |
| vshl.u32 q6, q9, #12 |
| vsri.u32 q5, q8, #20 |
| vsri.u32 q6, q9, #20 |
| |
| // x0 += x5, x15 = rotl32(x15 ^ x0, 8) |
| // x1 += x6, x12 = rotl32(x12 ^ x1, 8) |
| // x2 += x7, x13 = rotl32(x13 ^ x2, 8) |
| // x3 += x4, x14 = rotl32(x14 ^ x3, 8) |
| vadd.i32 q0, q0, q5 |
| vadd.i32 q1, q1, q6 |
| vadd.i32 q2, q2, q7 |
| vadd.i32 q3, q3, q4 |
| |
| veor q8, q15, q0 |
| veor q9, q12, q1 |
| vshl.u32 q15, q8, #8 |
| vshl.u32 q12, q9, #8 |
| vsri.u32 q15, q8, #24 |
| vsri.u32 q12, q9, #24 |
| |
| veor q8, q13, q2 |
| veor q9, q14, q3 |
| vshl.u32 q13, q8, #8 |
| vshl.u32 q14, q9, #8 |
| vsri.u32 q13, q8, #24 |
| vsri.u32 q14, q9, #24 |
| |
| vld1.32 {q8-q9}, [sp, :256] |
| |
| // x10 += x15, x5 = rotl32(x5 ^ x10, 7) |
| // x11 += x12, x6 = rotl32(x6 ^ x11, 7) |
| // x8 += x13, x7 = rotl32(x7 ^ x8, 7) |
| // x9 += x14, x4 = rotl32(x4 ^ x9, 7) |
| vadd.i32 q10, q10, q15 |
| vadd.i32 q11, q11, q12 |
| vadd.i32 q8, q8, q13 |
| vadd.i32 q9, q9, q14 |
| |
| vst1.32 {q8-q9}, [sp, :256] |
| |
| veor q8, q7, q8 |
| veor q9, q4, q9 |
| vshl.u32 q7, q8, #7 |
| vshl.u32 q4, q9, #7 |
| vsri.u32 q7, q8, #25 |
| vsri.u32 q4, q9, #25 |
| |
| veor q8, q5, q10 |
| veor q9, q6, q11 |
| vshl.u32 q5, q8, #7 |
| vshl.u32 q6, q9, #7 |
| vsri.u32 q5, q8, #25 |
| vsri.u32 q6, q9, #25 |
| |
| subs r3, r3, #1 |
| beq 0f |
| |
| vld1.32 {q8-q9}, [sp, :256] |
| b .Ldoubleround4 |
| |
| // x0[0-3] += s0[0] |
| // x1[0-3] += s0[1] |
| // x2[0-3] += s0[2] |
| // x3[0-3] += s0[3] |
| 0: ldmia r0!, {r3-r6} |
| vdup.32 q8, r3 |
| vdup.32 q9, r4 |
| vadd.i32 q0, q0, q8 |
| vadd.i32 q1, q1, q9 |
| vdup.32 q8, r5 |
| vdup.32 q9, r6 |
| vadd.i32 q2, q2, q8 |
| vadd.i32 q3, q3, q9 |
| |
| // x4[0-3] += s1[0] |
| // x5[0-3] += s1[1] |
| // x6[0-3] += s1[2] |
| // x7[0-3] += s1[3] |
| ldmia r0!, {r3-r6} |
| vdup.32 q8, r3 |
| vdup.32 q9, r4 |
| vadd.i32 q4, q4, q8 |
| vadd.i32 q5, q5, q9 |
| vdup.32 q8, r5 |
| vdup.32 q9, r6 |
| vadd.i32 q6, q6, q8 |
| vadd.i32 q7, q7, q9 |
| |
| // interleave 32-bit words in state n, n+1 |
| vzip.32 q0, q1 |
| vzip.32 q2, q3 |
| vzip.32 q4, q5 |
| vzip.32 q6, q7 |
| |
| // interleave 64-bit words in state n, n+2 |
| vswp d1, d4 |
| vswp d3, d6 |
| vswp d9, d12 |
| vswp d11, d14 |
| |
| // xor with corresponding input, write to output |
| vld1.8 {q8-q9}, [r2]! |
| veor q8, q8, q0 |
| veor q9, q9, q4 |
| vst1.8 {q8-q9}, [r1]! |
| |
| vld1.32 {q8-q9}, [sp, :256] |
| |
| // x8[0-3] += s2[0] |
| // x9[0-3] += s2[1] |
| // x10[0-3] += s2[2] |
| // x11[0-3] += s2[3] |
| ldmia r0!, {r3-r6} |
| vdup.32 q0, r3 |
| vdup.32 q4, r4 |
| vadd.i32 q8, q8, q0 |
| vadd.i32 q9, q9, q4 |
| vdup.32 q0, r5 |
| vdup.32 q4, r6 |
| vadd.i32 q10, q10, q0 |
| vadd.i32 q11, q11, q4 |
| |
| // x12[0-3] += s3[0] |
| // x13[0-3] += s3[1] |
| // x14[0-3] += s3[2] |
| // x15[0-3] += s3[3] |
| ldmia r0!, {r3-r6} |
| vdup.32 q0, r3 |
| vdup.32 q4, r4 |
| adr r3, CTRINC |
| vadd.i32 q12, q12, q0 |
| vld1.32 {q0}, [r3, :128] |
| vadd.i32 q13, q13, q4 |
| vadd.i32 q12, q12, q0 // x12 += counter values 0-3 |
| |
| vdup.32 q0, r5 |
| vdup.32 q4, r6 |
| vadd.i32 q14, q14, q0 |
| vadd.i32 q15, q15, q4 |
| |
| // interleave 32-bit words in state n, n+1 |
| vzip.32 q8, q9 |
| vzip.32 q10, q11 |
| vzip.32 q12, q13 |
| vzip.32 q14, q15 |
| |
| // interleave 64-bit words in state n, n+2 |
| vswp d17, d20 |
| vswp d19, d22 |
| vswp d25, d28 |
| vswp d27, d30 |
| |
| vmov q4, q1 |
| |
| vld1.8 {q0-q1}, [r2]! |
| veor q0, q0, q8 |
| veor q1, q1, q12 |
| vst1.8 {q0-q1}, [r1]! |
| |
| vld1.8 {q0-q1}, [r2]! |
| veor q0, q0, q2 |
| veor q1, q1, q6 |
| vst1.8 {q0-q1}, [r1]! |
| |
| vld1.8 {q0-q1}, [r2]! |
| veor q0, q0, q10 |
| veor q1, q1, q14 |
| vst1.8 {q0-q1}, [r1]! |
| |
| vld1.8 {q0-q1}, [r2]! |
| veor q0, q0, q4 |
| veor q1, q1, q5 |
| vst1.8 {q0-q1}, [r1]! |
| |
| vld1.8 {q0-q1}, [r2]! |
| veor q0, q0, q9 |
| veor q1, q1, q13 |
| vst1.8 {q0-q1}, [r1]! |
| |
| vld1.8 {q0-q1}, [r2]! |
| veor q0, q0, q3 |
| veor q1, q1, q7 |
| vst1.8 {q0-q1}, [r1]! |
| |
| vld1.8 {q0-q1}, [r2] |
| veor q0, q0, q11 |
| veor q1, q1, q15 |
| vst1.8 {q0-q1}, [r1] |
| |
| mov sp, ip |
| pop {r4-r6, pc} |
| ENDPROC(chacha20_4block_xor_neon) |
| |
| .align 4 |
| CTRINC: .word 0, 1, 2, 3 |