| /* SPDX-License-Identifier: GPL-2.0-only */ |
| /* |
| * Accelerated AES-GCM implementation with ARMv8 Crypto Extensions. |
| * |
| * Copyright (C) 2015 - 2017 Linaro Ltd. |
| * Copyright (C) 2023 Google LLC. <ardb@google.com> |
| */ |
| |
| #include <linux/linkage.h> |
| #include <asm/assembler.h> |
| |
| .arch armv8-a |
| .fpu crypto-neon-fp-armv8 |
| |
| SHASH .req q0 |
| T1 .req q1 |
| XL .req q2 |
| XM .req q3 |
| XH .req q4 |
| IN1 .req q4 |
| |
| SHASH_L .req d0 |
| SHASH_H .req d1 |
| T1_L .req d2 |
| T1_H .req d3 |
| XL_L .req d4 |
| XL_H .req d5 |
| XM_L .req d6 |
| XM_H .req d7 |
| XH_L .req d8 |
| |
| XH2 .req q9 |
| |
| MASK .req d28 |
| |
| SHASH2_p64 .req d31 |
| |
| HH .req q10 |
| HH3 .req q11 |
| HH4 .req q12 |
| HH34 .req q13 |
| |
| HH_L .req d20 |
| HH_H .req d21 |
| HH3_L .req d22 |
| HH3_H .req d23 |
| HH4_L .req d24 |
| HH4_H .req d25 |
| HH34_L .req d26 |
| HH34_H .req d27 |
| SHASH2_H .req d29 |
| |
| XL2 .req q5 |
| XM2 .req q6 |
| T2 .req q7 |
| T3 .req q8 |
| |
| XL2_L .req d10 |
| XL2_H .req d11 |
| XM2_L .req d12 |
| XM2_H .req d13 |
| T3_L .req d16 |
| T3_H .req d17 |
| |
| .text |
| |
| .macro __pmull_reduce_p64 |
| vmull.p64 T1, XL_L, MASK |
| |
| veor XH_L, XH_L, XM_H |
| vext.8 T1, T1, T1, #8 |
| veor XL_H, XL_H, XM_L |
| veor T1, T1, XL |
| |
| vmull.p64 XL, T1_H, MASK |
| .endm |
| |
| .macro ghash_update, enc, aggregate=1, head=1 |
| vld1.64 {XL}, [r1] |
| |
| .if \head |
| /* do the head block first, if supplied */ |
| ldr ip, [sp] |
| teq ip, #0 |
| beq 0f |
| vld1.64 {T1}, [ip] |
| teq r0, #0 |
| b 3f |
| .endif |
| |
| 0: .if \aggregate |
| tst r0, #3 // skip until #blocks is a |
| bne 2f // round multiple of 4 |
| |
| vld1.8 {XL2-XM2}, [r2]! |
| 1: vld1.8 {T2-T3}, [r2]! |
| |
| .ifnb \enc |
| \enc\()_4x XL2, XM2, T2, T3 |
| |
| add ip, r3, #16 |
| vld1.64 {HH}, [ip, :128]! |
| vld1.64 {HH3-HH4}, [ip, :128] |
| |
| veor SHASH2_p64, SHASH_L, SHASH_H |
| veor SHASH2_H, HH_L, HH_H |
| veor HH34_L, HH3_L, HH3_H |
| veor HH34_H, HH4_L, HH4_H |
| |
| vmov.i8 MASK, #0xe1 |
| vshl.u64 MASK, MASK, #57 |
| .endif |
| |
| vrev64.8 XL2, XL2 |
| vrev64.8 XM2, XM2 |
| |
| subs r0, r0, #4 |
| |
| vext.8 T1, XL2, XL2, #8 |
| veor XL2_H, XL2_H, XL_L |
| veor XL, XL, T1 |
| |
| vrev64.8 T1, T3 |
| vrev64.8 T3, T2 |
| |
| vmull.p64 XH, HH4_H, XL_H // a1 * b1 |
| veor XL2_H, XL2_H, XL_H |
| vmull.p64 XL, HH4_L, XL_L // a0 * b0 |
| vmull.p64 XM, HH34_H, XL2_H // (a1 + a0)(b1 + b0) |
| |
| vmull.p64 XH2, HH3_H, XM2_L // a1 * b1 |
| veor XM2_L, XM2_L, XM2_H |
| vmull.p64 XL2, HH3_L, XM2_H // a0 * b0 |
| vmull.p64 XM2, HH34_L, XM2_L // (a1 + a0)(b1 + b0) |
| |
| veor XH, XH, XH2 |
| veor XL, XL, XL2 |
| veor XM, XM, XM2 |
| |
| vmull.p64 XH2, HH_H, T3_L // a1 * b1 |
| veor T3_L, T3_L, T3_H |
| vmull.p64 XL2, HH_L, T3_H // a0 * b0 |
| vmull.p64 XM2, SHASH2_H, T3_L // (a1 + a0)(b1 + b0) |
| |
| veor XH, XH, XH2 |
| veor XL, XL, XL2 |
| veor XM, XM, XM2 |
| |
| vmull.p64 XH2, SHASH_H, T1_L // a1 * b1 |
| veor T1_L, T1_L, T1_H |
| vmull.p64 XL2, SHASH_L, T1_H // a0 * b0 |
| vmull.p64 XM2, SHASH2_p64, T1_L // (a1 + a0)(b1 + b0) |
| |
| veor XH, XH, XH2 |
| veor XL, XL, XL2 |
| veor XM, XM, XM2 |
| |
| beq 4f |
| |
| vld1.8 {XL2-XM2}, [r2]! |
| |
| veor T1, XL, XH |
| veor XM, XM, T1 |
| |
| __pmull_reduce_p64 |
| |
| veor T1, T1, XH |
| veor XL, XL, T1 |
| |
| b 1b |
| .endif |
| |
| 2: vld1.8 {T1}, [r2]! |
| |
| .ifnb \enc |
| \enc\()_1x T1 |
| veor SHASH2_p64, SHASH_L, SHASH_H |
| vmov.i8 MASK, #0xe1 |
| vshl.u64 MASK, MASK, #57 |
| .endif |
| |
| subs r0, r0, #1 |
| |
| 3: /* multiply XL by SHASH in GF(2^128) */ |
| vrev64.8 T1, T1 |
| |
| vext.8 IN1, T1, T1, #8 |
| veor T1_L, T1_L, XL_H |
| veor XL, XL, IN1 |
| |
| vmull.p64 XH, XL_H, SHASH_H @ a1 * b1 |
| veor T1, T1, XL |
| vmull.p64 XL, XL_L, SHASH_L @ a0 * b0 |
| vmull.p64 XM, T1_L, SHASH2_p64 @ (a1+a0)(b1+b0) |
| |
| 4: veor T1, XL, XH |
| veor XM, XM, T1 |
| |
| __pmull_reduce_p64 |
| |
| veor T1, T1, XH |
| veor XL, XL, T1 |
| |
| bne 0b |
| .endm |
| |
| /* |
| * void pmull_ghash_update_p64(int blocks, u64 dg[], const char *src, |
| * u64 const h[4][2], const char *head) |
| */ |
| ENTRY(pmull_ghash_update_p64) |
| vld1.64 {SHASH}, [r3]! |
| vld1.64 {HH}, [r3]! |
| vld1.64 {HH3-HH4}, [r3] |
| |
| veor SHASH2_p64, SHASH_L, SHASH_H |
| veor SHASH2_H, HH_L, HH_H |
| veor HH34_L, HH3_L, HH3_H |
| veor HH34_H, HH4_L, HH4_H |
| |
| vmov.i8 MASK, #0xe1 |
| vshl.u64 MASK, MASK, #57 |
| |
| ghash_update |
| vst1.64 {XL}, [r1] |
| |
| bx lr |
| ENDPROC(pmull_ghash_update_p64) |
| |
| e0 .req q9 |
| e1 .req q10 |
| e2 .req q11 |
| e3 .req q12 |
| e0l .req d18 |
| e0h .req d19 |
| e2l .req d22 |
| e2h .req d23 |
| e3l .req d24 |
| e3h .req d25 |
| ctr .req q13 |
| ctr0 .req d26 |
| ctr1 .req d27 |
| |
| ek0 .req q14 |
| ek1 .req q15 |
| |
| .macro round, rk:req, regs:vararg |
| .irp r, \regs |
| aese.8 \r, \rk |
| aesmc.8 \r, \r |
| .endr |
| .endm |
| |
| .macro aes_encrypt, rkp, rounds, regs:vararg |
| vld1.8 {ek0-ek1}, [\rkp, :128]! |
| cmp \rounds, #12 |
| blt .L\@ // AES-128 |
| |
| round ek0, \regs |
| vld1.8 {ek0}, [\rkp, :128]! |
| round ek1, \regs |
| vld1.8 {ek1}, [\rkp, :128]! |
| |
| beq .L\@ // AES-192 |
| |
| round ek0, \regs |
| vld1.8 {ek0}, [\rkp, :128]! |
| round ek1, \regs |
| vld1.8 {ek1}, [\rkp, :128]! |
| |
| .L\@: .rept 4 |
| round ek0, \regs |
| vld1.8 {ek0}, [\rkp, :128]! |
| round ek1, \regs |
| vld1.8 {ek1}, [\rkp, :128]! |
| .endr |
| |
| round ek0, \regs |
| vld1.8 {ek0}, [\rkp, :128] |
| |
| .irp r, \regs |
| aese.8 \r, ek1 |
| .endr |
| .irp r, \regs |
| veor \r, \r, ek0 |
| .endr |
| .endm |
| |
| pmull_aes_encrypt: |
| add ip, r5, #4 |
| vld1.8 {ctr0}, [r5] // load 12 byte IV |
| vld1.8 {ctr1}, [ip] |
| rev r8, r7 |
| vext.8 ctr1, ctr1, ctr1, #4 |
| add r7, r7, #1 |
| vmov.32 ctr1[1], r8 |
| vmov e0, ctr |
| |
| add ip, r3, #64 |
| aes_encrypt ip, r6, e0 |
| bx lr |
| ENDPROC(pmull_aes_encrypt) |
| |
| pmull_aes_encrypt_4x: |
| add ip, r5, #4 |
| vld1.8 {ctr0}, [r5] |
| vld1.8 {ctr1}, [ip] |
| rev r8, r7 |
| vext.8 ctr1, ctr1, ctr1, #4 |
| add r7, r7, #1 |
| vmov.32 ctr1[1], r8 |
| rev ip, r7 |
| vmov e0, ctr |
| add r7, r7, #1 |
| vmov.32 ctr1[1], ip |
| rev r8, r7 |
| vmov e1, ctr |
| add r7, r7, #1 |
| vmov.32 ctr1[1], r8 |
| rev ip, r7 |
| vmov e2, ctr |
| add r7, r7, #1 |
| vmov.32 ctr1[1], ip |
| vmov e3, ctr |
| |
| add ip, r3, #64 |
| aes_encrypt ip, r6, e0, e1, e2, e3 |
| bx lr |
| ENDPROC(pmull_aes_encrypt_4x) |
| |
| pmull_aes_encrypt_final: |
| add ip, r5, #4 |
| vld1.8 {ctr0}, [r5] |
| vld1.8 {ctr1}, [ip] |
| rev r8, r7 |
| vext.8 ctr1, ctr1, ctr1, #4 |
| mov r7, #1 << 24 // BE #1 for the tag |
| vmov.32 ctr1[1], r8 |
| vmov e0, ctr |
| vmov.32 ctr1[1], r7 |
| vmov e1, ctr |
| |
| add ip, r3, #64 |
| aes_encrypt ip, r6, e0, e1 |
| bx lr |
| ENDPROC(pmull_aes_encrypt_final) |
| |
| .macro enc_1x, in0 |
| bl pmull_aes_encrypt |
| veor \in0, \in0, e0 |
| vst1.8 {\in0}, [r4]! |
| .endm |
| |
| .macro dec_1x, in0 |
| bl pmull_aes_encrypt |
| veor e0, e0, \in0 |
| vst1.8 {e0}, [r4]! |
| .endm |
| |
| .macro enc_4x, in0, in1, in2, in3 |
| bl pmull_aes_encrypt_4x |
| |
| veor \in0, \in0, e0 |
| veor \in1, \in1, e1 |
| veor \in2, \in2, e2 |
| veor \in3, \in3, e3 |
| |
| vst1.8 {\in0-\in1}, [r4]! |
| vst1.8 {\in2-\in3}, [r4]! |
| .endm |
| |
| .macro dec_4x, in0, in1, in2, in3 |
| bl pmull_aes_encrypt_4x |
| |
| veor e0, e0, \in0 |
| veor e1, e1, \in1 |
| veor e2, e2, \in2 |
| veor e3, e3, \in3 |
| |
| vst1.8 {e0-e1}, [r4]! |
| vst1.8 {e2-e3}, [r4]! |
| .endm |
| |
| /* |
| * void pmull_gcm_encrypt(int blocks, u64 dg[], const char *src, |
| * struct gcm_key const *k, char *dst, |
| * char *iv, int rounds, u32 counter) |
| */ |
| ENTRY(pmull_gcm_encrypt) |
| push {r4-r8, lr} |
| ldrd r4, r5, [sp, #24] |
| ldrd r6, r7, [sp, #32] |
| |
| vld1.64 {SHASH}, [r3] |
| |
| ghash_update enc, head=0 |
| vst1.64 {XL}, [r1] |
| |
| pop {r4-r8, pc} |
| ENDPROC(pmull_gcm_encrypt) |
| |
| /* |
| * void pmull_gcm_decrypt(int blocks, u64 dg[], const char *src, |
| * struct gcm_key const *k, char *dst, |
| * char *iv, int rounds, u32 counter) |
| */ |
| ENTRY(pmull_gcm_decrypt) |
| push {r4-r8, lr} |
| ldrd r4, r5, [sp, #24] |
| ldrd r6, r7, [sp, #32] |
| |
| vld1.64 {SHASH}, [r3] |
| |
| ghash_update dec, head=0 |
| vst1.64 {XL}, [r1] |
| |
| pop {r4-r8, pc} |
| ENDPROC(pmull_gcm_decrypt) |
| |
| /* |
| * void pmull_gcm_enc_final(int bytes, u64 dg[], char *tag, |
| * struct gcm_key const *k, char *head, |
| * char *iv, int rounds, u32 counter) |
| */ |
| ENTRY(pmull_gcm_enc_final) |
| push {r4-r8, lr} |
| ldrd r4, r5, [sp, #24] |
| ldrd r6, r7, [sp, #32] |
| |
| bl pmull_aes_encrypt_final |
| |
| cmp r0, #0 |
| beq .Lenc_final |
| |
| mov_l ip, .Lpermute |
| sub r4, r4, #16 |
| add r8, ip, r0 |
| add ip, ip, #32 |
| add r4, r4, r0 |
| sub ip, ip, r0 |
| |
| vld1.8 {e3}, [r8] // permute vector for key stream |
| vld1.8 {e2}, [ip] // permute vector for ghash input |
| |
| vtbl.8 e3l, {e0}, e3l |
| vtbl.8 e3h, {e0}, e3h |
| |
| vld1.8 {e0}, [r4] // encrypt tail block |
| veor e0, e0, e3 |
| vst1.8 {e0}, [r4] |
| |
| vtbl.8 T1_L, {e0}, e2l |
| vtbl.8 T1_H, {e0}, e2h |
| |
| vld1.64 {XL}, [r1] |
| .Lenc_final: |
| vld1.64 {SHASH}, [r3, :128] |
| vmov.i8 MASK, #0xe1 |
| veor SHASH2_p64, SHASH_L, SHASH_H |
| vshl.u64 MASK, MASK, #57 |
| mov r0, #1 |
| bne 3f // process head block first |
| ghash_update aggregate=0, head=0 |
| |
| vrev64.8 XL, XL |
| vext.8 XL, XL, XL, #8 |
| veor XL, XL, e1 |
| |
| sub r2, r2, #16 // rewind src pointer |
| vst1.8 {XL}, [r2] // store tag |
| |
| pop {r4-r8, pc} |
| ENDPROC(pmull_gcm_enc_final) |
| |
| /* |
| * int pmull_gcm_dec_final(int bytes, u64 dg[], char *tag, |
| * struct gcm_key const *k, char *head, |
| * char *iv, int rounds, u32 counter, |
| * const char *otag, int authsize) |
| */ |
| ENTRY(pmull_gcm_dec_final) |
| push {r4-r8, lr} |
| ldrd r4, r5, [sp, #24] |
| ldrd r6, r7, [sp, #32] |
| |
| bl pmull_aes_encrypt_final |
| |
| cmp r0, #0 |
| beq .Ldec_final |
| |
| mov_l ip, .Lpermute |
| sub r4, r4, #16 |
| add r8, ip, r0 |
| add ip, ip, #32 |
| add r4, r4, r0 |
| sub ip, ip, r0 |
| |
| vld1.8 {e3}, [r8] // permute vector for key stream |
| vld1.8 {e2}, [ip] // permute vector for ghash input |
| |
| vtbl.8 e3l, {e0}, e3l |
| vtbl.8 e3h, {e0}, e3h |
| |
| vld1.8 {e0}, [r4] |
| |
| vtbl.8 T1_L, {e0}, e2l |
| vtbl.8 T1_H, {e0}, e2h |
| |
| veor e0, e0, e3 |
| vst1.8 {e0}, [r4] |
| |
| vld1.64 {XL}, [r1] |
| .Ldec_final: |
| vld1.64 {SHASH}, [r3] |
| vmov.i8 MASK, #0xe1 |
| veor SHASH2_p64, SHASH_L, SHASH_H |
| vshl.u64 MASK, MASK, #57 |
| mov r0, #1 |
| bne 3f // process head block first |
| ghash_update aggregate=0, head=0 |
| |
| vrev64.8 XL, XL |
| vext.8 XL, XL, XL, #8 |
| veor XL, XL, e1 |
| |
| mov_l ip, .Lpermute |
| ldrd r2, r3, [sp, #40] // otag and authsize |
| vld1.8 {T1}, [r2] |
| add ip, ip, r3 |
| vceq.i8 T1, T1, XL // compare tags |
| vmvn T1, T1 // 0 for eq, -1 for ne |
| |
| vld1.8 {e0}, [ip] |
| vtbl.8 XL_L, {T1}, e0l // keep authsize bytes only |
| vtbl.8 XL_H, {T1}, e0h |
| |
| vpmin.s8 XL_L, XL_L, XL_H // take the minimum s8 across the vector |
| vpmin.s8 XL_L, XL_L, XL_L |
| vmov.32 r0, XL_L[0] // fail if != 0x0 |
| |
| pop {r4-r8, pc} |
| ENDPROC(pmull_gcm_dec_final) |
| |
| .section ".rodata", "a", %progbits |
| .align 5 |
| .Lpermute: |
| .byte 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff |
| .byte 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff |
| .byte 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 |
| .byte 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f |
| .byte 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff |
| .byte 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff |