Text file src/crypto/internal/fips140/aes/aes_loong64.s

     1  // Copyright 2026 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // AES for loong64 using LSX VSHUFB-based software S-box lookup.
     6  // No hardware AES instructions exist on LoongArch (as of LA464/LA664).
     7  //
     8  // SubBytes: 256-byte S-box split into 8×32-byte chunks; 8 VSHUFB ops
     9  //           select the right byte per input nibble group.
    10  // ShiftRows: implemented via a VSHUFB with a fixed permutation table.
    11  // MixColumns: GF(2^8) multiply-by-2 via shift+conditional-XOR, then
    12  //             combine columns with VXORV.
    13  // InvMixColumns: implemented via GFMULCONST, a nibble-split VSHUFB table
    14  //   lookup (separate lo/hi 16-entry tables per GF(2^8) constant). The
    15  //   9/11/13/14 constant multiples are computed sequentially — the four
    16  //   GFMULCONST calls share the same midx1/midx2/tlo scratch registers,
    17  //   so they are NOT computed in parallel — then combined with
    18  //   rot1/rot2/rot3 (VSHUFB) and VXORV. This replaces an earlier
    19  //   XTIME-chain implementation (three chained GF(2^8) doublings), which
    20  //   was measured to be slower on AESCBCDecrypt1K/Decrypt benchmarks on
    21  //   this target despite a theoretically shorter critical path; see the
    22  //   GFMULCONST-table generation note below for how the lo/hi constant
    23  //   tables were produced and verified.
    24  //
    25  // expandKeyAsm: Phase 1/2 (deriving enc[] from the input key) is a
    26  //               scalar port of expandKeyGeneric — the key schedule has
    27  //               a true serial dependency chain and operates on 32-bit
    28  //               words smaller than the vector width, so it is not
    29  //               vectorized. Phase 3 (deriving dec[] from enc[]) has
    30  //               independent iterations over 16-byte word groups and is
    31  //               vectorized using INVMIXCOLUMNS directly (no SubBytes —
    32  //               the scalar td0/td1/td2/td3 tables already fold in and
    33  //               cancel out sbox0/sbox1, see aes_generic.go), with a
    34  //               byte-order swap (VSHUFB with byteSwap32) before/after
    35  //               to convert between word-storage order and the state
    36  //               byte order that INVMIXCOLUMNS's rot1/rot2/rot3 tables
    37  //               assume.
    38  
    39  //go:build !purego
    40  
    41  #include "textflag.h"
    42  
    43  // AES S-box tables for loong64 scalar implementation.
    44  // Stored as raw byte arrays; each DATA line encodes 8 consecutive
    45  // sbox bytes as a little-endian uint64 (byte[0] in the lowest address).
    46  //
    47  // Formula: value = b7<<56 | b6<<48 | b5<<40 | b4<<32
    48  //                | b3<<24 | b2<<16 | b1<<8  | b0
    49  
    50  // -----------------------------------------------------------------------
    51  // sbox0 — AES encryption S-box (256 bytes)
    52  // Source: FIPS-197 Figure 7 / const.go var sbox0
    53  // -----------------------------------------------------------------------
    54  DATA	sbox0_0+0x00(SB)/8, $0xc56f6bf27b777c63		// sbox0[  0.. 7]
    55  DATA	sbox0_0+0x08(SB)/8, $0x76abd7fe2b670130		// sbox0[  8..15]
    56  DATA	sbox0_0+0x10(SB)/8, $0xf04759fa7dc982ca		// sbox0[ 16..23]
    57  DATA	sbox0_0+0x18(SB)/8, $0xc072a49cafa2d4ad		// sbox0[ 24..31]
    58  DATA	sbox0_0+0x20(SB)/8, $0xccf73f362693fdb7		// sbox0[ 32..39]
    59  DATA	sbox0_0+0x28(SB)/8, $0x1531d871f1e5a534		// sbox0[ 40..47]
    60  DATA	sbox0_0+0x30(SB)/8, $0x9a059618c323c704		// sbox0[ 48..55]
    61  DATA	sbox0_0+0x38(SB)/8, $0x75b227ebe2801207		// sbox0[ 56..63]
    62  DATA	sbox0_0+0x40(SB)/8, $0xa05a6e1b1a2c8309		// sbox0[ 64..71]
    63  DATA	sbox0_0+0x48(SB)/8, $0x842fe329b3d63b52		// sbox0[ 72..79]
    64  DATA	sbox0_0+0x50(SB)/8, $0x5bb1fc20ed00d153		// sbox0[ 80..87]
    65  DATA	sbox0_0+0x58(SB)/8, $0xcf584c4a39becb6a		// sbox0[ 88..95]
    66  DATA	sbox0_0+0x60(SB)/8, $0x85334d43fbaaefd0		// sbox0[ 96..103]
    67  DATA	sbox0_0+0x68(SB)/8, $0xa89f3c507f02f945		// sbox0[104..111]
    68  DATA	sbox0_0+0x70(SB)/8, $0xf5389d928f40a351		// sbox0[112..119]
    69  DATA	sbox0_0+0x78(SB)/8, $0xd2f3ff1021dab6bc		// sbox0[120..127]
    70  DATA	sbox0_0+0x80(SB)/8, $0x1744975fec130ccd		// sbox0[128..135]
    71  DATA	sbox0_0+0x88(SB)/8, $0x73195d643d7ea7c4		// sbox0[136..143]
    72  DATA	sbox0_0+0x90(SB)/8, $0x88902a22dc4f8160		// sbox0[144..151]
    73  DATA	sbox0_0+0x98(SB)/8, $0xdb0b5ede14b8ee46		// sbox0[152..159]
    74  DATA	sbox0_0+0xa0(SB)/8, $0x5c2406490a3a32e0		// sbox0[160..167]
    75  DATA	sbox0_0+0xa8(SB)/8, $0x79e4959162acd3c2		// sbox0[168..175]
    76  DATA	sbox0_0+0xb0(SB)/8, $0xa94ed58d6d37c8e7		// sbox0[176..183]
    77  DATA	sbox0_0+0xb8(SB)/8, $0x08ae7a65eaf4566c		// sbox0[184..191]
    78  DATA	sbox0_0+0xc0(SB)/8, $0xc6b4a61c2e2578ba		// sbox0[192..199]
    79  DATA	sbox0_0+0xc8(SB)/8, $0x8a8bbd4b1f74dde8		// sbox0[200..207]
    80  DATA	sbox0_0+0xd0(SB)/8, $0x0ef6034866b53e70		// sbox0[208..215]
    81  DATA	sbox0_0+0xd8(SB)/8, $0x9e1dc186b9573561		// sbox0[216..223]
    82  DATA	sbox0_0+0xe0(SB)/8, $0x948ed9691198f8e1		// sbox0[224..231]
    83  DATA	sbox0_0+0xe8(SB)/8, $0xdf2855cee9871e9b		// sbox0[232..239]
    84  DATA	sbox0_0+0xf0(SB)/8, $0x6842e6bf0d89a18c		// sbox0[240..247]
    85  DATA	sbox0_0+0xf8(SB)/8, $0x16bb54b00f2d9941		// sbox0[248..255]
    86  GLOBL	sbox0_0(SB), (NOPTR+RODATA), $256
    87  
    88  // -----------------------------------------------------------------------
    89  // sbox1 — AES decryption (inverse) S-box (256 bytes)
    90  // Source: FIPS-197 Figure 14 / const.go var sbox1
    91  // -----------------------------------------------------------------------
    92  DATA	sbox1_0<>+0x00(SB)/8, $0x38a53630d56a0952	// sbox1[  0.. 7]
    93  DATA	sbox1_0<>+0x08(SB)/8, $0xfbd7f3819ea340bf	// sbox1[  8..15]
    94  DATA	sbox1_0<>+0x10(SB)/8, $0x87ff2f9b8239e37c	// sbox1[ 16..23]
    95  DATA	sbox1_0<>+0x18(SB)/8, $0xcbe9dec444438e34	// sbox1[ 24..31]
    96  DATA	sbox1_0<>+0x20(SB)/8, $0x3d23c2a632947b54	// sbox1[ 32..39]
    97  DATA	sbox1_0<>+0x28(SB)/8, $0x4ec3fa420b954cee	// sbox1[ 40..47]
    98  DATA	sbox1_0<>+0x30(SB)/8, $0xb224d92866a12e08	// sbox1[ 48..55]
    99  DATA	sbox1_0<>+0x38(SB)/8, $0x25d18b6d49a25b76	// sbox1[ 56..63]
   100  DATA	sbox1_0<>+0x40(SB)/8, $0x1698688664f6f872	// sbox1[ 64..71]
   101  DATA	sbox1_0<>+0x48(SB)/8, $0x92b6655dcc5ca4d4	// sbox1[ 72..79]
   102  DATA	sbox1_0<>+0x50(SB)/8, $0xdab9edfd5048706c	// sbox1[ 80..87]
   103  DATA	sbox1_0<>+0x58(SB)/8, $0x849d8da75746155e	// sbox1[ 88..95]
   104  DATA	sbox1_0<>+0x60(SB)/8, $0x0ad3bc8c00abd890	// sbox1[ 96..103]
   105  DATA	sbox1_0<>+0x68(SB)/8, $0x0645b3b80558e4f7	// sbox1[104..111]
   106  DATA	sbox1_0<>+0x70(SB)/8, $0x020f3fca8f1e2cd0	// sbox1[112..119]
   107  DATA	sbox1_0<>+0x78(SB)/8, $0x6b8a130103bdafc1	// sbox1[120..127]
   108  DATA	sbox1_0<>+0x80(SB)/8, $0xeadc674f4111913a	// sbox1[128..135]
   109  DATA	sbox1_0<>+0x88(SB)/8, $0x73e6b4f0cecff297	// sbox1[136..143]
   110  DATA	sbox1_0<>+0x90(SB)/8, $0x8535ade72274ac96	// sbox1[144..151]
   111  DATA	sbox1_0<>+0x98(SB)/8, $0x6edf751ce837f9e2	// sbox1[152..159]
   112  DATA	sbox1_0<>+0xa0(SB)/8, $0x89c5291d711af147	// sbox1[160..167]
   113  DATA	sbox1_0<>+0xa8(SB)/8, $0x1bbe18aa0e62b76f	// sbox1[168..175]
   114  DATA	sbox1_0<>+0xb0(SB)/8, $0x2079d2c64b3e56fc	// sbox1[176..183]
   115  DATA	sbox1_0<>+0xb8(SB)/8, $0xf45acd78fec0db9a	// sbox1[184..191]
   116  DATA	sbox1_0<>+0xc0(SB)/8, $0x31c7078833a8dd1f	// sbox1[192..199]
   117  DATA	sbox1_0<>+0xc8(SB)/8, $0x5fec8027591012b1	// sbox1[200..207]
   118  DATA	sbox1_0<>+0xd0(SB)/8, $0x0d4ab519a97f5160	// sbox1[208..215]
   119  DATA	sbox1_0<>+0xd8(SB)/8, $0xef9cc9939f7ae52d	// sbox1[216..223]
   120  DATA	sbox1_0<>+0xe0(SB)/8, $0xb0f52aae4d3be0a0	// sbox1[224..231]
   121  DATA	sbox1_0<>+0xe8(SB)/8, $0x619953833cbbebc8	// sbox1[232..239]
   122  DATA	sbox1_0<>+0xf0(SB)/8, $0x26d677ba7e042b17	// sbox1[240..247]
   123  DATA	sbox1_0<>+0xf8(SB)/8, $0x7d0c2155631469e1	// sbox1[248..255]
   124  GLOBL	sbox1_0<>(SB), (NOPTR+RODATA), $256
   125  
   126  // ShiftRows permutation for encryption:
   127  //   state byte positions after ShiftRows (row i rotated left by i):
   128  //   row0: 0,1,2,3  row1: 5,6,7,4  row2: 10,11,8,9  row3: 15,12,13,14
   129  DATA	shiftRows+0x00(SB)/8, $0x030e09040f0a0500
   130  DATA	shiftRows+0x08(SB)/8, $0x0b06010c07020d08
   131  GLOBL	shiftRows(SB), (NOPTR+RODATA), $16
   132  
   133  // rot1: {1,2,3,0, 5,6,7,4, 9,10,11,8, 13,14,15,12}
   134  DATA	rot1+0x00(SB)/8, $0x0407060500030201
   135  DATA	rot1+0x08(SB)/8, $0x0c0f0e0d080b0a09
   136  GLOBL	rot1(SB), (NOPTR+RODATA), $16
   137  
   138  // rot2: {2,3,0,1, 6,7,4,5, 10,11,8,9, 14,15,12,13}
   139  DATA	rot2+0x00(SB)/8, $0x0504070601000302
   140  DATA	rot2+0x08(SB)/8, $0x0d0c0f0e09080b0a
   141  GLOBL	rot2(SB), (NOPTR+RODATA), $16
   142  
   143  // rot3: {3,0,1,2, 7,4,5,6, 11,8,9,10, 15,12,13,14}
   144  DATA	rot3+0x00(SB)/8, $0x0605040702010003
   145  DATA	rot3+0x08(SB)/8, $0x0e0d0c0f0a09080b
   146  GLOBL	rot3(SB), (NOPTR+RODATA), $16
   147  
   148  // InvShiftRows permutation for decryption.
   149  DATA	invShiftRows<>+0x00(SB)/8, $0x0b0e0104070a0d00
   150  DATA	invShiftRows<>+0x08(SB)/8, $0x0306090c0f020508
   151  GLOBL	invShiftRows<>(SB), (NOPTR+RODATA), $16
   152  
   153  DATA	byteSwap32+0x00(SB)/8, $0x0405060700010203	// index 0-7:  {3,2,1,0,7,6,5,4}
   154  DATA	byteSwap32+0x08(SB)/8, $0x0c0d0e0f08090a0b	// index 8-15: {11,10,9,8,15,14,13,12}
   155  GLOBL	byteSwap32(SB), (NOPTR+RODATA), $16
   156  
   157  // mul9lo[v] = mul(9, v), v = 0..15
   158  DATA	mul9lo<>+0x00(SB)/8, $0x3f362d241b120900
   159  DATA	mul9lo<>+0x08(SB)/8, $0x777e656c535a4148
   160  GLOBL	mul9lo<>(SB), (NOPTR+RODATA), $16
   161  
   162  // mul9hi[v] = mul(9, v<<4), v = 0..15
   163  DATA	mul9hi<>+0x00(SB)/8, $0xdd4de676ab3b9000
   164  DATA	mul9hi<>+0x08(SB)/8, $0x31a10a9a47d77cec
   165  GLOBL	mul9hi<>(SB), (NOPTR+RODATA), $16
   166  
   167  // mul11lo[v] = mul(11, v), v = 0..15
   168  DATA	mul11lo<>+0x00(SB)/8, $0x313a272c1d160b00
   169  DATA	mul11lo<>+0x08(SB)/8, $0x69627f74454e5358
   170  GLOBL	mul11lo<>(SB), (NOPTR+RODATA), $16
   171  
   172  // mul11hi[v] = mul(11, v<<4), v = 0..15
   173  DATA	mul11hi<>+0x00(SB)/8, $0x3d8d46f6cb7bb000
   174  DATA	mul11hi<>+0x08(SB)/8, $0xca7ab1013c8c47f7
   175  GLOBL	mul11hi<>(SB), (NOPTR+RODATA), $16
   176  
   177  // mul13lo[v] = mul(13, v), v = 0..15
   178  DATA	mul13lo<>+0x00(SB)/8, $0x232e3934171a0d00
   179  DATA	mul13lo<>+0x08(SB)/8, $0x4b46515c7f726568
   180  GLOBL	mul13lo<>(SB), (NOPTR+RODATA), $16
   181  
   182  // mul13hi[v] = mul(13, v<<4), v = 0..15
   183  DATA	mul13hi<>+0x00(SB)/8, $0x06d6bd6d6bbbd000
   184  DATA	mul13hi<>+0x08(SB)/8, $0xdc0c67b7b1610ada
   185  GLOBL	mul13hi<>(SB), (NOPTR+RODATA), $16
   186  
   187  // mul14lo[v] = mul(14, v), v = 0..15
   188  DATA	mul14lo<>+0x00(SB)/8, $0x2a243638121c0e00
   189  DATA	mul14lo<>+0x08(SB)/8, $0x5a544648626c7e70
   190  GLOBL	mul14lo<>(SB), (NOPTR+RODATA), $16
   191  
   192  // mul14hi[v] = mul(14, v<<4), v = 0..15
   193  DATA	mul14hi<>+0x00(SB)/8, $0x96764dad3bdbe000
   194  DATA	mul14hi<>+0x08(SB)/8, $0xd7370cec7a9aa141
   195  GLOBL	mul14hi<>(SB), (NOPTR+RODATA), $16
   196  
   197  #define SUBBYTES(src, sbox_ptr, out) \
   198  	VANDB	$0xe0, src, V25;	\  // V25 = src & 0xe0, keep only bit7/bit6/bit5
   199  	VSRAB	$7, V25, V31;		\  // V31 = byte-wise broadcast of bit7 (0xFF/0x00)
   200  	VSLLB	$1, V25, V24;		\  // shift bit6 into the sign position
   201  	VSRAB	$7, V24, V30;		\  // V30 = byte-wise broadcast of bit6
   202  	VSLLB	$2, V25, V24;		\  // shift bit5 into the sign position
   203  	VSRAB	$7, V24, V29;		\  // V29 = byte-wise broadcast of bit5
   204  	VANDB	$0x1f, src, V26;	\  // V26 = src & 0x1f, index for VSHUFB 32-way lookup
   205  	\
   206  	/* ---- 2. Chunk0/1 -> r01 (V16..V19,V28,V27 freed after use) ---- */ \
   207  	VMOVQ	(sbox_ptr), V16;   \
   208  	VMOVQ	16(sbox_ptr), V17;   \
   209  	VMOVQ	32(sbox_ptr), V18;   \
   210  	VMOVQ	48(sbox_ptr), V19;   \
   211  	VSHUFB	V26, V16, V17, V28;  \		// V28 = chunk0_raw
   212  	VSHUFB	V26, V18, V19, V27;  \		// V27 = chunk1_raw
   213  	VBITSELV	V29, V27, V28, V20; \	// V20 = r01 = M5 ? chunk1 : chunk0
   214  	\
   215  	/* ---- 3. Chunk2/3 -> r23 ---- */ \
   216  	VMOVQ	64(sbox_ptr), V16;  \
   217  	VMOVQ	80(sbox_ptr), V17;  \
   218  	VMOVQ	96(sbox_ptr), V18;  \
   219  	VMOVQ	112(sbox_ptr), V19;  \
   220  	VSHUFB	V26, V16, V17, V28;  \		// V28 = chunk2_raw
   221  	VSHUFB	V26, V18, V19, V27;  \		// V27 = chunk3_raw
   222  	VBITSELV	V29, V27, V28, V21; \	// V21 = r23
   223  	\
   224  	/* ---- 4. Merge first group (chunk0..3) -> r0123 ---- */ \
   225  	VBITSELV	V30, V21, V20, V22; \	// V22 = r0123 = M6 ? r23 : r01
   226  	\
   227  	/* ---- 5. Chunk4/5 -> r45 ---- */ \
   228  	VMOVQ	128(sbox_ptr), V16;  \
   229  	VMOVQ	144(sbox_ptr), V17;  \
   230  	VMOVQ	160(sbox_ptr), V18;  \
   231  	VMOVQ	176(sbox_ptr), V19;  \
   232  	VSHUFB	V26, V16, V17, V28;  \		// V28 = chunk4_raw
   233  	VSHUFB	V26, V18, V19, V27;  \		// V27 = chunk5_raw
   234  	VBITSELV	V29, V27, V28, V20; \	// V20 reused = r45
   235  	\
   236  	/* ---- 6. Chunk6/7 -> r67 ---- */ \
   237  	VMOVQ	192(sbox_ptr), V16;  \
   238  	VMOVQ	208(sbox_ptr), V17;  \
   239  	VMOVQ	224(sbox_ptr), V18;  \
   240  	VMOVQ	240(sbox_ptr), V19;  \
   241  	VSHUFB	V26, V16, V17, V28;  \		// V28 = chunk6_raw
   242  	VSHUFB	V26, V18, V19, V27;  \		// V27 = chunk7_raw
   243  	VBITSELV	V29, V27, V28, V21; \	// V21 reused = r67
   244  	\
   245  	/* ---- 7. Merge second group (chunk4..7) -> r4567 ---- */ \
   246  	VBITSELV	V30, V21, V20, V23; \	// V23 = r4567 = M6 ? r67 : r45
   247  	\
   248  	/* ---- 8. Final top-level merge ---- */ \
   249  	VBITSELV	V31, V23, V22, out	// out = M7 ? r4567 : r0123
   250  
   251  // -----------------------------------------------------------------------
   252  // MixColumns helper: multiply each byte of Vn by 2 in GF(2^8).
   253  //   xtime(a) = (a << 1) ^ (0x1b if a & 0x80 else 0)
   254  // -----------------------------------------------------------------------
   255  #define XTIME(src, dst, tmp) \
   256  	VSLLB	$1, src, dst; \		/* dst = src << 1 */
   257  	VSRAB	$7, src, tmp; \		/* tmp[i] = 0xff if src[i]>=0x80, else 0x00 */
   258  	VANDB	$0x1b, tmp, tmp; \	/* tmp[i] = 0x1b if src[i]>=0x80, else 0x00 */
   259  	VXORV	tmp, dst, dst		/* dst ^= tmp */
   260  
   261  #define MIXCOLUMNS(src, out, r1, r2, r3, t1, t2, t3) \
   262  	VSHUFB	r1, src, src, t1; \	/* t1 = rot1(a) */
   263  	VXORV	src, t1, t2; \		/* t2 = a ^ rot1(a) */
   264  	VSHUFB	r2, src, src, t3; \	/* t3 = rot2(a) */
   265  	VXORV	t2, t3, out; \		/* out = a^rot1(a)^rot2(a) */
   266  	VSHUFB	r3, src, src, t3; \	/* t3 = rot3(a) */
   267  	VXORV	out, t3, out; \		/* out = t = a0^a1^a2^a3(each byte) */
   268  	XTIME(t2, t1, t3); \		/* t1 = xtime(a^rot1(a)) */
   269  	VXORV	src, out, out; \	/* out = a ^ t */
   270  	VXORV	out, t1, out		/* out ^= xtime(...) */
   271  
   272  // t = subw(t): S-box substitution for each byte of the 32-bit word t, results written back to t
   273  #define SUBW_INLINE(t, sbox, tmp1, tmp2) \
   274  	SRLV	$24, t, tmp1; \
   275  	AND	$0xff, tmp1; \
   276  	ADDV	sbox, tmp1, tmp1; \
   277  	MOVBU	(tmp1), tmp1; \
   278  	SLLV	$24, tmp1, tmp2; \
   279  	SRLV	$16, t, tmp1; \
   280  	AND	$0xff, tmp1; \
   281  	ADDV	sbox, tmp1, tmp1; \
   282  	MOVBU	(tmp1), tmp1; \
   283  	SLLV	$16, tmp1, tmp1; \
   284  	OR	tmp1, tmp2; \
   285  	SRLV	$8, t, tmp1; \
   286  	AND	$0xff, tmp1; \
   287  	ADDV	sbox, tmp1, tmp1; \
   288  	MOVBU	(tmp1), tmp1; \
   289  	SLLV	$8, tmp1, tmp1; \
   290  	OR	tmp1, tmp2; \
   291  	AND	$0xff, t, tmp1; \
   292  	ADDV	sbox, tmp1, tmp1; \
   293  	MOVBU	(tmp1), tmp1; \
   294  	OR	tmp1, tmp2; \
   295  	MOVV	tmp2, t
   296  
   297  TEXT ·encryptBlockAsm(SB), NOSPLIT, $0-32
   298  	MOVV	nr+0(FP), R4
   299  	MOVV	xk+8(FP), R5
   300  	MOVV	dst+16(FP), R6
   301  	MOVV	src+24(FP), R7
   302  
   303  	// Load 16-byte state
   304  	VMOVQ	(R7), V0
   305  
   306  	// Load constant table addresses
   307  	MOVV	$sbox0_0(SB), R9
   308  	MOVV	$shiftRows(SB), R10
   309  	MOVV	$rot1(SB), R11
   310  	MOVV	$rot2(SB), R12
   311  	MOVV	$rot3(SB), R13
   312  	MOVV	$byteSwap32(SB), R14
   313  
   314  	// Load vector constants
   315  	VMOVQ	(R10), V7	// shiftRows index
   316  	VMOVQ	(R11), V4	// rot1 index
   317  	VMOVQ	(R12), V5	// rot2 index
   318  	VMOVQ	(R13), V6	// rot3 index
   319  	VMOVQ	(R14), V9	// byteSwap32 index
   320  
   321  	// AddRoundKey (round 0)
   322  	VMOVQ	(R5), V8
   323  	VSHUFB	V9, V8, V8, V8
   324  	VXORV	V8, V0, V0
   325  	ADDV	$16, R5
   326  
   327  	// Middle rounds: nr - 1 iterations
   328  	ADDV	$-1, R4, R8
   329  Lenc_loop:
   330  	SUBBYTES(V0, R9, V0)
   331  
   332  	// ShiftRows
   333  	VSHUFB	V7, V0, V0, V0
   334  
   335  	// MixColumns
   336  	MIXCOLUMNS(V0, V15, V4, V5, V6, V10, V11, V12)
   337  
   338  	// AddRoundKey
   339  	VMOVQ	(R5), V8
   340  	VSHUFB	V9, V8, V8, V8
   341  	VXORV	V8, V15, V0
   342  	ADDV	$16, R5
   343  
   344  	SUBV	$1, R8
   345  	BNE	R8, R0, Lenc_loop
   346  
   347  	// Final round: SubBytes + ShiftRows + AddRoundKey (no MixColumns)
   348  	SUBBYTES(V0, R9, V0)
   349  	VSHUFB	V7, V0, V0, V0
   350  	VMOVQ	(R5), V8
   351  	VSHUFB	V9, V8, V8, V8
   352  	VXORV	V8, V0, V0
   353  
   354  	// Store ciphertext
   355  	VMOVQ	V0, (R6)
   356  	RET
   357  
   358  // GFMULCONST(src, lo_tbl_ptr, hi_tbl_ptr, out, lo_idx, hi_idx, tlo)
   359  // out = const * src  (GF(2^8)), via nibble-split lookup.
   360  // lo_tbl_ptr/hi_tbl_ptr: base address registers for this constant's 16B tables.
   361  // lo_idx/hi_idx/tlo: scratch, freed immediately after the macro.
   362  #define GFMULCONST(src, lo_tbl, hi_tbl, out, lo_idx, hi_idx, tlo) \
   363  	VANDB	$0x0f, src, lo_idx;      \	// lo_idx = src & 0x0f
   364  	VSRLB	$4,   src, hi_idx;       \	// hi_idx = src >> 4 (unsigned, top nibble)
   365  	VMOVQ	(lo_tbl), tlo;           \	// load this constant's lo table (16B)
   366  	VSHUFB	lo_idx, tlo, tlo, lo_idx;\	// lo_idx = lo_table[src&0xf]  (single-source 16-way lookup)
   367  	VMOVQ	(hi_tbl), tlo;           \	// load this constant's hi table (16B), tlo reused
   368  	VSHUFB	hi_idx, tlo, tlo, hi_idx;\	// hi_idx = hi_table[src>>4]
   369  	VXORV	lo_idx, hi_idx, out		// out = lo_part ^ hi_part
   370  
   371  // INVMIXCOLUMNS(src, out, r1, r2, r3, t9, t11, t13, t14, midx1, midx2, tlo)
   372  // r1/r2/r3: rotation index tables (already loaded, e.g. V4/V5/V6)
   373  // t9/t11/t13/t14: four accumulator registers, each holds one const*src result
   374  // midx1/midx2/tlo: shared scratch reused sequentially across the 4 GFMULCONST calls
   375  #define INVMIXCOLUMNS(src, out, r1, r2, r3, t9, t11, t13, t14, midx1, midx2, tlo) \
   376  	MOVV	$mul9lo<>(SB), R13; \
   377  	MOVV	$mul9hi<>(SB), R14; \
   378  	GFMULCONST(src, R13, R14, t9,  midx1, midx2, tlo); \	// t9  = 9*src
   379  	\
   380  	MOVV	$mul11lo<>(SB), R13; \
   381  	MOVV	$mul11hi<>(SB), R14; \
   382  	GFMULCONST(src, R13, R14, t11, midx1, midx2, tlo); \	// t11 = 11*src
   383  	\
   384  	MOVV	$mul13lo<>(SB), R13; \
   385  	MOVV	$mul13hi<>(SB), R14; \
   386  	GFMULCONST(src, R13, R14, t13, midx1, midx2, tlo); \	// t13 = 13*src
   387  	\
   388  	MOVV	$mul14lo<>(SB), R13; \
   389  	MOVV	$mul14hi<>(SB), R14; \
   390  	GFMULCONST(src, R13, R14, t14, midx1, midx2, tlo); \	// t14 = 14*src
   391  	\
   392  	/* out = 14*src ^ rot1(11*src) ^ rot2(13*src) ^ rot3(9*src) */ \
   393  	VSHUFB	r1, t11, t11, t11; \	// t11 = rot1(11*src)
   394  	VSHUFB	r2, t13, t13, t13; \	// t13 = rot2(13*src)
   395  	VSHUFB	r3, t9,  t9,  t9;  \	// t9  = rot3(9*src)
   396  	VXORV	t11, t14, out;     \	// out = 14*src ^ rot1(11*src)
   397  	VXORV	t13, out, out;     \	// out ^= rot2(13*src)
   398  	VXORV	t9,  out, out		// out ^= rot3(9*src)
   399  
   400  TEXT ·decryptBlockAsm(SB), NOSPLIT, $0-32
   401  	MOVV	nr+0(FP), R4
   402  	MOVV	xk+8(FP), R5
   403  	MOVV	dst+16(FP), R6
   404  	MOVV	src+24(FP), R7
   405  
   406  	// Load state
   407  	VMOVQ	(R7), V0
   408  
   409  	// Load invShiftRows index vector
   410  	MOVV	$invShiftRows<>(SB), R9
   411  	VMOVQ	(R9), V7
   412  
   413  	// Load sbox1 base for InvSubBytes
   414  	MOVV	$sbox1_0<>(SB), R10
   415  
   416  	// Load byte-swap table for round-key endianness
   417  	MOVV	$byteSwap32(SB), R11
   418  	VMOVQ	(R11), V2
   419  
   420  	// Load rotation indices for InvMixColumns
   421  	MOVV	$rot1(SB), R12
   422  	VMOVQ	(R12), V4
   423  	MOVV	$rot2(SB), R12
   424  	VMOVQ	(R12), V5
   425  	MOVV	$rot3(SB), R12
   426  	VMOVQ	(R12), V6
   427  
   428  	// Initial AddRoundKey (last round key for decryption)
   429  	VMOVQ	(R5), V8
   430  	VSHUFB	V2, V8, V8, V8	// byte-swap each 32-bit word
   431  	VXORV	V8, V0, V0
   432  	ADDV	$16, R5
   433  
   434  	// R8 = nr - 1 (middle rounds with InvMixColumns)
   435  	ADDV	$-1, R4, R8
   436  
   437  Ldec_loop:
   438  	SUBBYTES(V0, R10, V13)
   439  	VSHUFB	V7, V13, V13, V0	// InvShiftRows
   440  
   441  	INVMIXCOLUMNS(V0, V0, V4, V5, V6, V9, V10, V11, V12, V13, V14, V16)
   442  
   443  	VMOVQ	(R5), V8		// AddRoundKey(put in last)
   444  	VSHUFB	V2, V8, V8, V8
   445  	VXORV	V8, V0, V0
   446  	ADDV	$16, R5
   447  
   448  	SUBV	$1, R8
   449  	BNE	R8, R0, Ldec_loop
   450  
   451  	// Final round: InvSubBytes + InvShiftRows + AddRoundKey (no InvMixColumns)
   452  	SUBBYTES(V0, R10, V13)
   453  	VSHUFB	V7, V13, V13, V0
   454  	VMOVQ	(R5), V8
   455  	VSHUFB	V2, V8, V8, V8
   456  	VXORV	V8, V0, V0
   457  
   458  	// Store result
   459  	VMOVQ	V0, (R6)
   460  	RET
   461  
   462  // -----------------------------------------------------------------------
   463  // func expandKeyAsm(nr int, key *byte, enc *uint32, dec *uint32)
   464  //
   465  // Uses scalar S-box lookup (sbox0) for subw(); no VSHUFB needed here
   466  // since key schedule processes 4 bytes at a time, not 16.
   467  // -----------------------------------------------------------------------
   468  TEXT ·expandKeyAsm(SB), NOSPLIT, $0-32
   469  	MOVV	nr+0(FP), R4
   470  	MOVV	key+8(FP), R5
   471  	MOVV	enc+16(FP), R6
   472  	MOVV	dec+24(FP), R7
   473  
   474  	MOVV	$sbox0_0(SB), R8	// S-box for subw()
   475  	MOVV	$·powx(SB), R14		// Rcon table
   476  
   477  	// roundKeysSize = (nr+1)*4
   478  	ADDV	$1, R4, R9
   479  	SLLV	$2, R9, R9		// R9 = (nr+1)*4
   480  
   481  	// nk = nr - 6  (AES-128→4, AES-192→6, AES-256→8)
   482  	ADDV	$-6, R4, R10
   483  
   484  	// Precompute the "nk > 6" flag once, instead of re-deriving it
   485  	// (via SUBV $6, R10, R18 + BEQ/BLT) on every iteration where pos==4.
   486  	MOVV	$0, R25			// R25 = 0 (false) by default
   487  	MOVV	$6, R18
   488  	BGE	R18, R10, Lnk6_done	// nk <= 6  ->  flag stays false
   489  	MOVV	$1, R25			// nk > 6   ->  flag = true
   490  
   491  Lnk6_done:
   492  	// Hoist the "pos == 4" comparison constant out of the per-word loop.
   493  	MOVV	$4, R20
   494  
   495  	MOVV	R6, R17			// save enc base for Phase 3
   496  
   497  	// Phase 1: load initial key words (i = 0..nk-1)
   498  	MOVV	$0, R11			// i = 0
   499  Linit_loop:
   500  	BEQ	R11, R10, Linit_done
   501  	SLLV	$2, R11, R13
   502  	ADDV	R5, R13, R13		// &key[i*4]
   503  	MOVWU	(R13), R12		// load 4 bytes (little-endian)
   504  	REVB2W	R12, R12		// → big-endian word
   505  	MOVW	R12, (R6)		// enc[i] = word
   506  	ADDV	$4, R6
   507  	ADDV	$1, R11
   508  	JMP	Linit_loop
   509  
   510  Linit_done:
   511  	// Phase 2: key expansion (i = nk..roundKeysSize-1)
   512  	// pos = i%nk (counter, reset to 0 when reaches nk)
   513  	// rcon_idx = i/nk - 1 (incremented each time pos wraps)
   514  	MOVV	$0, R15		// pos = 0  (nk%nk = 0)
   515  	MOVV	$0, R16		// rcon_idx = 0
   516  
   517  Lexpand_loop:
   518  	BEQ	R11, R9, Lexpand_enc_done
   519  
   520  	MOVWU	-4(R6), R12		// t = enc[i-1]
   521  
   522  	BNE	R15, R0, Lcheck_nk6	// pos != 0 → skip i%nk==0 branch
   523  
   524  	// i%nk == 0: t = subw(rotw(t)) ^ (powx[rcon_idx] << 24)
   525  	ROTR	$24, R12, R12		// rotw: rotate-left-8 = rotate-right-24
   526  	SUBW_INLINE(R12, R8, R13, R19)
   527  	ADDV	R14, R16, R13		// &powx[rcon_idx]
   528  	MOVBU	(R13), R13
   529  	SLLV	$24, R13, R13
   530  	XOR	R13, R12		// t ^= rcon
   531  	ADDV	$1, R16			// rcon_idx++
   532  	JMP	Ldo_xor
   533  
   534  Lcheck_nk6:
   535  	BNE	R15, R20, Ldo_xor	// pos != 4 -> skip (R20 preloaded with 4)
   536  	BEQ	R25, R0, Ldo_xor	// nk <= 6 -> skip (R25 preloaded flag)
   537  	// nk > 6 && pos == 4: t = subw(t)
   538  	SUBW_INLINE(R12, R8, R13, R19)
   539  
   540  Ldo_xor:
   541  	SLLV	$2, R10, R13
   542  	SUBV	R13, R6, R13		// &enc[i-nk]  (R6 - nk*4)
   543  	MOVWU	(R13), R13		// enc[i-nk]
   544  	XOR	R13, R12		// t ^= enc[i-nk]
   545  	MOVW	R12, (R6)		// enc[i] = t
   546  	ADDV	$4, R6
   547  	ADDV	$1, R11
   548  	ADDV	$1, R15			// pos++
   549  	BNE	R15, R10, Lexpand_loop
   550  	MOVV	$0, R15			// pos reset
   551  	JMP	Lexpand_loop
   552  
   553  Lexpand_enc_done:
   554  	BEQ	R7, R0, Lexpand_done	// dec == nil → skip
   555  
   556  	// ---- Phase 3 (vectorized): derive dec[] from enc[] ----
   557  	// n = roundKeysSize (R9), enc base = R17 (saved earlier), dec base = R7
   558  	// Load rotation tables (rot1/rot2/rot3) needed by INVMIXCOLUMNS.
   559  	MOVV	$rot1(SB), R26
   560  	MOVV	$rot2(SB), R27
   561  	MOVV	$rot3(SB), R28
   562  	MOVV	$byteSwap32(SB), R24
   563  	VMOVQ	(R26), V4
   564  	VMOVQ	(R27), V5
   565  	VMOVQ	(R28), V6
   566  	VMOVQ	(R24), V2
   567  
   568  	MOVV	$sbox0_0(SB), R29	// sbox0 table base (for SUBBYTES)
   569  
   570  	MOVV	$0, R19			// i = 0
   571  Ldec_outer_vec:
   572  	BEQ	R19, R9, Lexpand_done
   573  	SUBV	R19, R9, R20
   574  	ADDV	$-4, R20, R20		// R20 = ei = n - i - 4
   575  	SLLV	$2, R20, R25
   576  	ADDV	R17, R25, R25		// &enc[ei]  (R17 = saved enc base)
   577  	VMOVQ	(R25), V0		// load 4 consecutive enc words as one 16-byte group
   578  
   579  	// boundary: first (i==0) and last (i+4==n) groups get no InvMixColumns
   580  	BEQ	R19, R0, Ldec_copy_vec
   581  	ADDV	$4, R19, R20
   582  	BEQ	R20, R9, Ldec_copy_vec
   583  
   584  	VSHUFB	V2, V0, V0, V0		// word-storage order -> state order (V2 = byteSwap32 index, same table used in decryptBlockAsm)
   585  	INVMIXCOLUMNS(V0, V0, V4, V5, V6, V9, V10, V11, V12, V13, V14, V16)
   586  	VSHUFB	V2, V0, V0, V0		// state order -> word-storage order (so decryptBlockAsm's own byte-swap-before-use still works)
   587  
   588  Ldec_copy_vec:
   589  	SLLV	$2, R19, R25
   590  	ADDV	R7, R25, R25		// &dec[i]
   591  	VMOVQ	V0, (R25)
   592  
   593  	ADDV	$4, R19
   594  	JMP	Ldec_outer_vec
   595  
   596  Lexpand_done:
   597  	RET
   598  

View as plain text