Re: [PATCH v3 3/5] crypto: arm/speck - add NEON-accelerated implementation of Speck-XTS

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 17.06.2018 11:40, Ard Biesheuvel wrote:
> On 17 June 2018 at 11:30, Ard Biesheuvel <ard.biesheuvel@xxxxxxxxxx> wrote:
>> On 17 June 2018 at 00:40, Stefan Agner <stefan@xxxxxxxx> wrote:
>>> Hi Eric,
>>>
>>> On 14.02.2018 19:42, Eric Biggers wrote:
>>>> Add an ARM NEON-accelerated implementation of Speck-XTS.  It operates on
>>>> 128-byte chunks at a time, i.e. 8 blocks for Speck128 or 16 blocks for
>>>> Speck64.  Each 128-byte chunk goes through XTS preprocessing, then is
>>>> encrypted/decrypted (doing one cipher round for all the blocks, then the
>>>> next round, etc.), then goes through XTS postprocessing.
>>>>
>>>> The performance depends on the processor but can be about 3 times faster
>>>> than the generic code.  For example, on an ARMv7 processor we observe
>>>> the following performance with Speck128/256-XTS:
>>>>
>>>>     xts-speck128-neon:     Encryption 107.9 MB/s, Decryption 108.1 MB/s
>>>>     xts(speck128-generic): Encryption  32.1 MB/s, Decryption  36.6 MB/s
>>>>
>>>> In comparison to AES-256-XTS without the Cryptography Extensions:
>>>>
>>>>     xts-aes-neonbs:        Encryption  41.2 MB/s, Decryption  36.7 MB/s
>>>>     xts(aes-asm):          Encryption  31.7 MB/s, Decryption  30.8 MB/s
>>>>     xts(aes-generic):      Encryption  21.2 MB/s, Decryption  20.9 MB/s
>>>>
>>>> Speck64/128-XTS is even faster:
>>>>
>>>>     xts-speck64-neon:      Encryption 138.6 MB/s, Decryption 139.1 MB/s
>>>>
>>>> Note that as with the generic code, only the Speck128 and Speck64
>>>> variants are supported.  Also, for now only the XTS mode of operation is
>>>> supported, to target the disk and file encryption use cases.  The NEON
>>>> code also only handles the portion of the data that is evenly divisible
>>>> into 128-byte chunks, with any remainder handled by a C fallback.  Of
>>>> course, other modes of operation could be added later if needed, and/or
>>>> the NEON code could be updated to handle other buffer sizes.
>>>>
>>>> The XTS specification is only defined for AES which has a 128-bit block
>>>> size, so for the GF(2^64) math needed for Speck64-XTS we use the
>>>> reducing polynomial 'x^64 + x^4 + x^3 + x + 1' given by the original XEX
>>>> paper.  Of course, when possible users should use Speck128-XTS, but even
>>>> that may be too slow on some processors; Speck64-XTS can be faster.
>>>>
>>>> Signed-off-by: Eric Biggers <ebiggers@xxxxxxxxxx>
>>>> ---
>>>>  arch/arm/crypto/Kconfig           |   6 +
>>>>  arch/arm/crypto/Makefile          |   2 +
>>>>  arch/arm/crypto/speck-neon-core.S | 432 ++++++++++++++++++++++++++++++
>>>>  arch/arm/crypto/speck-neon-glue.c | 288 ++++++++++++++++++++
>>>>  4 files changed, 728 insertions(+)
>>>>  create mode 100644 arch/arm/crypto/speck-neon-core.S
>>>>  create mode 100644 arch/arm/crypto/speck-neon-glue.c
>>>>
>>>> diff --git a/arch/arm/crypto/Kconfig b/arch/arm/crypto/Kconfig
>>>> index b8e69fe282b8..925d1364727a 100644
>>>> --- a/arch/arm/crypto/Kconfig
>>>> +++ b/arch/arm/crypto/Kconfig
>>>> @@ -121,4 +121,10 @@ config CRYPTO_CHACHA20_NEON
>>>>       select CRYPTO_BLKCIPHER
>>>>       select CRYPTO_CHACHA20
>>>>
>>>> +config CRYPTO_SPECK_NEON
>>>> +     tristate "NEON accelerated Speck cipher algorithms"
>>>> +     depends on KERNEL_MODE_NEON
>>>> +     select CRYPTO_BLKCIPHER
>>>> +     select CRYPTO_SPECK
>>>> +
>>>>  endif
>>>> diff --git a/arch/arm/crypto/Makefile b/arch/arm/crypto/Makefile
>>>> index 30ef8e291271..a758107c5525 100644
>>>> --- a/arch/arm/crypto/Makefile
>>>> +++ b/arch/arm/crypto/Makefile
>>>> @@ -10,6 +10,7 @@ obj-$(CONFIG_CRYPTO_SHA1_ARM_NEON) += sha1-arm-neon.o
>>>>  obj-$(CONFIG_CRYPTO_SHA256_ARM) += sha256-arm.o
>>>>  obj-$(CONFIG_CRYPTO_SHA512_ARM) += sha512-arm.o
>>>>  obj-$(CONFIG_CRYPTO_CHACHA20_NEON) += chacha20-neon.o
>>>> +obj-$(CONFIG_CRYPTO_SPECK_NEON) += speck-neon.o
>>>>
>>>>  ce-obj-$(CONFIG_CRYPTO_AES_ARM_CE) += aes-arm-ce.o
>>>>  ce-obj-$(CONFIG_CRYPTO_SHA1_ARM_CE) += sha1-arm-ce.o
>>>> @@ -53,6 +54,7 @@ ghash-arm-ce-y      := ghash-ce-core.o ghash-ce-glue.o
>>>>  crct10dif-arm-ce-y   := crct10dif-ce-core.o crct10dif-ce-glue.o
>>>>  crc32-arm-ce-y:= crc32-ce-core.o crc32-ce-glue.o
>>>>  chacha20-neon-y := chacha20-neon-core.o chacha20-neon-glue.o
>>>> +speck-neon-y := speck-neon-core.o speck-neon-glue.o
>>>>
>>>>  quiet_cmd_perl = PERL    $@
>>>>        cmd_perl = $(PERL) $(<) > $(@)
>>>> diff --git a/arch/arm/crypto/speck-neon-core.S
>>>> b/arch/arm/crypto/speck-neon-core.S
>>>> new file mode 100644
>>>> index 000000000000..3c1e203e53b9
>>>> --- /dev/null
>>>> +++ b/arch/arm/crypto/speck-neon-core.S
>>>> @@ -0,0 +1,432 @@
>>>> +// SPDX-License-Identifier: GPL-2.0
>>>> +/*
>>>> + * NEON-accelerated implementation of Speck128-XTS and Speck64-XTS
>>>> + *
>>>> + * Copyright (c) 2018 Google, Inc
>>>> + *
>>>> + * Author: Eric Biggers <ebiggers@xxxxxxxxxx>
>>>> + */
>>>> +
>>>> +#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
>>>
>>>
>>> This fails here when building with CONFIG_THUMB2_KERNEL=y
>>>
>>>   AS      arch/arm/crypto/speck-neon-core.o
>>>
>>> arch/arm/crypto/speck-neon-core.S: Assembler messages:
>>>
>>> arch/arm/crypto/speck-neon-core.S:419: Error: r13 not allowed here --
>>> `bic sp,#0xf'
>>> arch/arm/crypto/speck-neon-core.S:423: Error: r13 not allowed here --
>>> `bic sp,#0xf'
>>> arch/arm/crypto/speck-neon-core.S:427: Error: r13 not allowed here --
>>> `bic sp,#0xf'
>>> arch/arm/crypto/speck-neon-core.S:431: Error: r13 not allowed here --
>>> `bic sp,#0xf'
>>>
>>> In a quick hack this change seems to address it:
>>>
>>>
>>> -       sub             sp, #128
>>> -       bic             sp, #0xf
>>> +       mov             r6, sp
>>> +       sub             r6, #128
>>> +       bic             r6, #0xf
>>> +       mov             sp, r6
>>>
>>> But there is probably a better solution to address this.
>>>
>>
>> Given that there is no NEON on M class cores, I recommend we put something like
>>
>> THUMB(bx pc)
>> THUMB(nop.w)
>> THUMB(.arm)
>>
>> at the beginning and be done with it.
> 
> I mean nop.n or just nop, of course, and we may need a '.align 2' at
> the beginning as well.

Wouldn't it be preferable to have it assemble it in Thumb2 too? It seems
that bic sp,#0xf is the only issue...

--
Stefan



[Index of Archives]     [Kernel]     [Gnu Classpath]     [Gnu Crypto]     [DM Crypt]     [Netfilter]     [Bugtraq]

  Powered by Linux