433 lines
10 KiB
ArmAsm
433 lines
10 KiB
ArmAsm
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* NEON-accelerated implementation of Speck128-XTS and Speck64-XTS
|
|
*
|
|
* Copyright (c) 2018 Google, Inc
|
|
*
|
|
* Author: Eric Biggers <ebiggers@google.com>
|
|
*/
|
|
|
|
#include <linux/linkage.h>
|
|
|
|
.text
|
|
.fpu neon
|
|
|
|
// arguments
|
|
ROUND_KEYS .req r0 // const {u64,u32} *round_keys
|
|
NROUNDS .req r1 // int nrounds
|
|
DST .req r2 // void *dst
|
|
SRC .req r3 // const void *src
|
|
NBYTES .req r4 // unsigned int nbytes
|
|
TWEAK .req r5 // void *tweak
|
|
|
|
// registers which hold the data being encrypted/decrypted
|
|
X0 .req q0
|
|
X0_L .req d0
|
|
X0_H .req d1
|
|
Y0 .req q1
|
|
Y0_H .req d3
|
|
X1 .req q2
|
|
X1_L .req d4
|
|
X1_H .req d5
|
|
Y1 .req q3
|
|
Y1_H .req d7
|
|
X2 .req q4
|
|
X2_L .req d8
|
|
X2_H .req d9
|
|
Y2 .req q5
|
|
Y2_H .req d11
|
|
X3 .req q6
|
|
X3_L .req d12
|
|
X3_H .req d13
|
|
Y3 .req q7
|
|
Y3_H .req d15
|
|
|
|
// the round key, duplicated in all lanes
|
|
ROUND_KEY .req q8
|
|
ROUND_KEY_L .req d16
|
|
ROUND_KEY_H .req d17
|
|
|
|
// index vector for vtbl-based 8-bit rotates
|
|
ROTATE_TABLE .req d18
|
|
|
|
// multiplication table for updating XTS tweaks
|
|
GF128MUL_TABLE .req d19
|
|
GF64MUL_TABLE .req d19
|
|
|
|
// current XTS tweak value(s)
|
|
TWEAKV .req q10
|
|
TWEAKV_L .req d20
|
|
TWEAKV_H .req d21
|
|
|
|
TMP0 .req q12
|
|
TMP0_L .req d24
|
|
TMP0_H .req d25
|
|
TMP1 .req q13
|
|
TMP2 .req q14
|
|
TMP3 .req q15
|
|
|
|
.align 4
|
|
.Lror64_8_table:
|
|
.byte 1, 2, 3, 4, 5, 6, 7, 0
|
|
.Lror32_8_table:
|
|
.byte 1, 2, 3, 0, 5, 6, 7, 4
|
|
.Lrol64_8_table:
|
|
.byte 7, 0, 1, 2, 3, 4, 5, 6
|
|
.Lrol32_8_table:
|
|
.byte 3, 0, 1, 2, 7, 4, 5, 6
|
|
.Lgf128mul_table:
|
|
.byte 0, 0x87
|
|
.fill 14
|
|
.Lgf64mul_table:
|
|
.byte 0, 0x1b, (0x1b << 1), (0x1b << 1) ^ 0x1b
|
|
.fill 12
|
|
|
|
/*
|
|
* _speck_round_128bytes() - Speck encryption round on 128 bytes at a time
|
|
*
|
|
* Do one Speck encryption round on the 128 bytes (8 blocks for Speck128, 16 for
|
|
* Speck64) stored in X0-X3 and Y0-Y3, using the round key stored in all lanes
|
|
* of ROUND_KEY. 'n' is the lane size: 64 for Speck128, or 32 for Speck64.
|
|
*
|
|
* The 8-bit rotates are implemented using vtbl instead of vshr + vsli because
|
|
* the vtbl approach is faster on some processors and the same speed on others.
|
|
*/
|
|
.macro _speck_round_128bytes n
|
|
|
|
// x = ror(x, 8)
|
|
vtbl.8 X0_L, {X0_L}, ROTATE_TABLE
|
|
vtbl.8 X0_H, {X0_H}, ROTATE_TABLE
|
|
vtbl.8 X1_L, {X1_L}, ROTATE_TABLE
|
|
vtbl.8 X1_H, {X1_H}, ROTATE_TABLE
|
|
vtbl.8 X2_L, {X2_L}, ROTATE_TABLE
|
|
vtbl.8 X2_H, {X2_H}, ROTATE_TABLE
|
|
vtbl.8 X3_L, {X3_L}, ROTATE_TABLE
|
|
vtbl.8 X3_H, {X3_H}, ROTATE_TABLE
|
|
|
|
// x += y
|
|
vadd.u\n X0, Y0
|
|
vadd.u\n X1, Y1
|
|
vadd.u\n X2, Y2
|
|
vadd.u\n X3, Y3
|
|
|
|
// x ^= k
|
|
veor X0, ROUND_KEY
|
|
veor X1, ROUND_KEY
|
|
veor X2, ROUND_KEY
|
|
veor X3, ROUND_KEY
|
|
|
|
// y = rol(y, 3)
|
|
vshl.u\n TMP0, Y0, #3
|
|
vshl.u\n TMP1, Y1, #3
|
|
vshl.u\n TMP2, Y2, #3
|
|
vshl.u\n TMP3, Y3, #3
|
|
vsri.u\n TMP0, Y0, #(\n - 3)
|
|
vsri.u\n TMP1, Y1, #(\n - 3)
|
|
vsri.u\n TMP2, Y2, #(\n - 3)
|
|
vsri.u\n TMP3, Y3, #(\n - 3)
|
|
|
|
// y ^= x
|
|
veor Y0, TMP0, X0
|
|
veor Y1, TMP1, X1
|
|
veor Y2, TMP2, X2
|
|
veor Y3, TMP3, X3
|
|
.endm
|
|
|
|
/*
|
|
* _speck_unround_128bytes() - Speck decryption round on 128 bytes at a time
|
|
*
|
|
* This is the inverse of _speck_round_128bytes().
|
|
*/
|
|
.macro _speck_unround_128bytes n
|
|
|
|
// y ^= x
|
|
veor TMP0, Y0, X0
|
|
veor TMP1, Y1, X1
|
|
veor TMP2, Y2, X2
|
|
veor TMP3, Y3, X3
|
|
|
|
// y = ror(y, 3)
|
|
vshr.u\n Y0, TMP0, #3
|
|
vshr.u\n Y1, TMP1, #3
|
|
vshr.u\n Y2, TMP2, #3
|
|
vshr.u\n Y3, TMP3, #3
|
|
vsli.u\n Y0, TMP0, #(\n - 3)
|
|
vsli.u\n Y1, TMP1, #(\n - 3)
|
|
vsli.u\n Y2, TMP2, #(\n - 3)
|
|
vsli.u\n Y3, TMP3, #(\n - 3)
|
|
|
|
// x ^= k
|
|
veor X0, ROUND_KEY
|
|
veor X1, ROUND_KEY
|
|
veor X2, ROUND_KEY
|
|
veor X3, ROUND_KEY
|
|
|
|
// x -= y
|
|
vsub.u\n X0, Y0
|
|
vsub.u\n X1, Y1
|
|
vsub.u\n X2, Y2
|
|
vsub.u\n X3, Y3
|
|
|
|
// x = rol(x, 8);
|
|
vtbl.8 X0_L, {X0_L}, ROTATE_TABLE
|
|
vtbl.8 X0_H, {X0_H}, ROTATE_TABLE
|
|
vtbl.8 X1_L, {X1_L}, ROTATE_TABLE
|
|
vtbl.8 X1_H, {X1_H}, ROTATE_TABLE
|
|
vtbl.8 X2_L, {X2_L}, ROTATE_TABLE
|
|
vtbl.8 X2_H, {X2_H}, ROTATE_TABLE
|
|
vtbl.8 X3_L, {X3_L}, ROTATE_TABLE
|
|
vtbl.8 X3_H, {X3_H}, ROTATE_TABLE
|
|
.endm
|
|
|
|
.macro _xts128_precrypt_one dst_reg, tweak_buf, tmp
|
|
|
|
// Load the next source block
|
|
vld1.8 {\dst_reg}, [SRC]!
|
|
|
|
// Save the current tweak in the tweak buffer
|
|
vst1.8 {TWEAKV}, [\tweak_buf:128]!
|
|
|
|
// XOR the next source block with the current tweak
|
|
veor \dst_reg, TWEAKV
|
|
|
|
/*
|
|
* Calculate the next tweak by multiplying the current one by x,
|
|
* modulo p(x) = x^128 + x^7 + x^2 + x + 1.
|
|
*/
|
|
vshr.u64 \tmp, TWEAKV, #63
|
|
vshl.u64 TWEAKV, #1
|
|
veor TWEAKV_H, \tmp\()_L
|
|
vtbl.8 \tmp\()_H, {GF128MUL_TABLE}, \tmp\()_H
|
|
veor TWEAKV_L, \tmp\()_H
|
|
.endm
|
|
|
|
.macro _xts64_precrypt_two dst_reg, tweak_buf, tmp
|
|
|
|
// Load the next two source blocks
|
|
vld1.8 {\dst_reg}, [SRC]!
|
|
|
|
// Save the current two tweaks in the tweak buffer
|
|
vst1.8 {TWEAKV}, [\tweak_buf:128]!
|
|
|
|
// XOR the next two source blocks with the current two tweaks
|
|
veor \dst_reg, TWEAKV
|
|
|
|
/*
|
|
* Calculate the next two tweaks by multiplying the current ones by x^2,
|
|
* modulo p(x) = x^64 + x^4 + x^3 + x + 1.
|
|
*/
|
|
vshr.u64 \tmp, TWEAKV, #62
|
|
vshl.u64 TWEAKV, #2
|
|
vtbl.8 \tmp\()_L, {GF64MUL_TABLE}, \tmp\()_L
|
|
vtbl.8 \tmp\()_H, {GF64MUL_TABLE}, \tmp\()_H
|
|
veor TWEAKV, \tmp
|
|
.endm
|
|
|
|
/*
|
|
* _speck_xts_crypt() - Speck-XTS encryption/decryption
|
|
*
|
|
* Encrypt or decrypt NBYTES bytes of data from the SRC buffer to the DST buffer
|
|
* using Speck-XTS, specifically the variant with a block size of '2n' and round
|
|
* count given by NROUNDS. The expanded round keys are given in ROUND_KEYS, and
|
|
* the current XTS tweak value is given in TWEAK. It's assumed that NBYTES is a
|
|
* nonzero multiple of 128.
|
|
*/
|
|
.macro _speck_xts_crypt n, decrypting
|
|
push {r4-r7}
|
|
mov r7, sp
|
|
|
|
/*
|
|
* The first four parameters were passed in registers r0-r3. Load the
|
|
* additional parameters, which were passed on the stack.
|
|
*/
|
|
ldr NBYTES, [sp, #16]
|
|
ldr TWEAK, [sp, #20]
|
|
|
|
/*
|
|
* If decrypting, modify the ROUND_KEYS parameter to point to the last
|
|
* round key rather than the first, since for decryption the round keys
|
|
* are used in reverse order.
|
|
*/
|
|
.if \decrypting
|
|
.if \n == 64
|
|
add ROUND_KEYS, ROUND_KEYS, NROUNDS, lsl #3
|
|
sub ROUND_KEYS, #8
|
|
.else
|
|
add ROUND_KEYS, ROUND_KEYS, NROUNDS, lsl #2
|
|
sub ROUND_KEYS, #4
|
|
.endif
|
|
.endif
|
|
|
|
// Load the index vector for vtbl-based 8-bit rotates
|
|
.if \decrypting
|
|
ldr r12, =.Lrol\n\()_8_table
|
|
.else
|
|
ldr r12, =.Lror\n\()_8_table
|
|
.endif
|
|
vld1.8 {ROTATE_TABLE}, [r12:64]
|
|
|
|
// One-time XTS preparation
|
|
|
|
/*
|
|
* Allocate stack space to store 128 bytes worth of tweaks. For
|
|
* performance, this space is aligned to a 16-byte boundary so that we
|
|
* can use the load/store instructions that declare 16-byte alignment.
|
|
*/
|
|
sub sp, #128
|
|
bic sp, #0xf
|
|
|
|
.if \n == 64
|
|
// Load first tweak
|
|
vld1.8 {TWEAKV}, [TWEAK]
|
|
|
|
// Load GF(2^128) multiplication table
|
|
ldr r12, =.Lgf128mul_table
|
|
vld1.8 {GF128MUL_TABLE}, [r12:64]
|
|
.else
|
|
// Load first tweak
|
|
vld1.8 {TWEAKV_L}, [TWEAK]
|
|
|
|
// Load GF(2^64) multiplication table
|
|
ldr r12, =.Lgf64mul_table
|
|
vld1.8 {GF64MUL_TABLE}, [r12:64]
|
|
|
|
// Calculate second tweak, packing it together with the first
|
|
vshr.u64 TMP0_L, TWEAKV_L, #63
|
|
vtbl.u8 TMP0_L, {GF64MUL_TABLE}, TMP0_L
|
|
vshl.u64 TWEAKV_H, TWEAKV_L, #1
|
|
veor TWEAKV_H, TMP0_L
|
|
.endif
|
|
|
|
.Lnext_128bytes_\@:
|
|
|
|
/*
|
|
* Load the source blocks into {X,Y}[0-3], XOR them with their XTS tweak
|
|
* values, and save the tweaks on the stack for later. Then
|
|
* de-interleave the 'x' and 'y' elements of each block, i.e. make it so
|
|
* that the X[0-3] registers contain only the second halves of blocks,
|
|
* and the Y[0-3] registers contain only the first halves of blocks.
|
|
* (Speck uses the order (y, x) rather than the more intuitive (x, y).)
|
|
*/
|
|
mov r12, sp
|
|
.if \n == 64
|
|
_xts128_precrypt_one X0, r12, TMP0
|
|
_xts128_precrypt_one Y0, r12, TMP0
|
|
_xts128_precrypt_one X1, r12, TMP0
|
|
_xts128_precrypt_one Y1, r12, TMP0
|
|
_xts128_precrypt_one X2, r12, TMP0
|
|
_xts128_precrypt_one Y2, r12, TMP0
|
|
_xts128_precrypt_one X3, r12, TMP0
|
|
_xts128_precrypt_one Y3, r12, TMP0
|
|
vswp X0_L, Y0_H
|
|
vswp X1_L, Y1_H
|
|
vswp X2_L, Y2_H
|
|
vswp X3_L, Y3_H
|
|
.else
|
|
_xts64_precrypt_two X0, r12, TMP0
|
|
_xts64_precrypt_two Y0, r12, TMP0
|
|
_xts64_precrypt_two X1, r12, TMP0
|
|
_xts64_precrypt_two Y1, r12, TMP0
|
|
_xts64_precrypt_two X2, r12, TMP0
|
|
_xts64_precrypt_two Y2, r12, TMP0
|
|
_xts64_precrypt_two X3, r12, TMP0
|
|
_xts64_precrypt_two Y3, r12, TMP0
|
|
vuzp.32 Y0, X0
|
|
vuzp.32 Y1, X1
|
|
vuzp.32 Y2, X2
|
|
vuzp.32 Y3, X3
|
|
.endif
|
|
|
|
// Do the cipher rounds
|
|
|
|
mov r12, ROUND_KEYS
|
|
mov r6, NROUNDS
|
|
|
|
.Lnext_round_\@:
|
|
.if \decrypting
|
|
.if \n == 64
|
|
vld1.64 ROUND_KEY_L, [r12]
|
|
sub r12, #8
|
|
vmov ROUND_KEY_H, ROUND_KEY_L
|
|
.else
|
|
vld1.32 {ROUND_KEY_L[],ROUND_KEY_H[]}, [r12]
|
|
sub r12, #4
|
|
.endif
|
|
_speck_unround_128bytes \n
|
|
.else
|
|
.if \n == 64
|
|
vld1.64 ROUND_KEY_L, [r12]!
|
|
vmov ROUND_KEY_H, ROUND_KEY_L
|
|
.else
|
|
vld1.32 {ROUND_KEY_L[],ROUND_KEY_H[]}, [r12]!
|
|
.endif
|
|
_speck_round_128bytes \n
|
|
.endif
|
|
subs r6, r6, #1
|
|
bne .Lnext_round_\@
|
|
|
|
// Re-interleave the 'x' and 'y' elements of each block
|
|
.if \n == 64
|
|
vswp X0_L, Y0_H
|
|
vswp X1_L, Y1_H
|
|
vswp X2_L, Y2_H
|
|
vswp X3_L, Y3_H
|
|
.else
|
|
vzip.32 Y0, X0
|
|
vzip.32 Y1, X1
|
|
vzip.32 Y2, X2
|
|
vzip.32 Y3, X3
|
|
.endif
|
|
|
|
// XOR the encrypted/decrypted blocks with the tweaks we saved earlier
|
|
mov r12, sp
|
|
vld1.8 {TMP0, TMP1}, [r12:128]!
|
|
vld1.8 {TMP2, TMP3}, [r12:128]!
|
|
veor X0, TMP0
|
|
veor Y0, TMP1
|
|
veor X1, TMP2
|
|
veor Y1, TMP3
|
|
vld1.8 {TMP0, TMP1}, [r12:128]!
|
|
vld1.8 {TMP2, TMP3}, [r12:128]!
|
|
veor X2, TMP0
|
|
veor Y2, TMP1
|
|
veor X3, TMP2
|
|
veor Y3, TMP3
|
|
|
|
// Store the ciphertext in the destination buffer
|
|
vst1.8 {X0, Y0}, [DST]!
|
|
vst1.8 {X1, Y1}, [DST]!
|
|
vst1.8 {X2, Y2}, [DST]!
|
|
vst1.8 {X3, Y3}, [DST]!
|
|
|
|
// Continue if there are more 128-byte chunks remaining, else return
|
|
subs NBYTES, #128
|
|
bne .Lnext_128bytes_\@
|
|
|
|
// Store the next tweak
|
|
.if \n == 64
|
|
vst1.8 {TWEAKV}, [TWEAK]
|
|
.else
|
|
vst1.8 {TWEAKV_L}, [TWEAK]
|
|
.endif
|
|
|
|
mov sp, r7
|
|
pop {r4-r7}
|
|
bx lr
|
|
.endm
|
|
|
|
ENTRY(speck128_xts_encrypt_neon)
|
|
_speck_xts_crypt n=64, decrypting=0
|
|
ENDPROC(speck128_xts_encrypt_neon)
|
|
|
|
ENTRY(speck128_xts_decrypt_neon)
|
|
_speck_xts_crypt n=64, decrypting=1
|
|
ENDPROC(speck128_xts_decrypt_neon)
|
|
|
|
ENTRY(speck64_xts_encrypt_neon)
|
|
_speck_xts_crypt n=32, decrypting=0
|
|
ENDPROC(speck64_xts_encrypt_neon)
|
|
|
|
ENTRY(speck64_xts_decrypt_neon)
|
|
_speck_xts_crypt n=32, decrypting=1
|
|
ENDPROC(speck64_xts_decrypt_neon)
|