[PATCH 06/10] serpent: reduce CTR bulk counter carry handling to 16 bits
Jussi Kivilinna <[email protected]> Fri, 24 Jul 2026 21:50:13 +0300
| Newsgroups | gmane.comp.encryption.gpg.libgcrypt.devel |
|---|---|
| Message-ID | <[email protected]> |
* cipher/serpent.c (serpent_setkey): Assign ctr16be_enc bulk op. (_gcry_serpent_ctr_enc): Use cipher_block_add_be16. * cipher/serpent-avx2-amd64.S (inc_le128): Remove. (_gcry_serpent_avx2_ctr_enc): Add to low 16 counter bits only, drop full-width overflow path. * cipher/serpent-sse2-amd64.S (_gcry_serpent_sse2_ctr_enc): Add to low 16 counter bits only, drop full-width overflow path. * cipher/serpent-avx512-x86.c (ctr_generate): Do 16-bit big-endian counter addition, add carry slow path with vpaddw/vpshufb. * cipher/serpent-armv7-neon.S (_gcry_serpent_neon_ctr_enc): Drop 64-bit counter overflow path. * cipher/Makefile.am (avx512f_cflags): Add -mavx512bw. * configure.ac: Add -mavx512bw and _mm512_shuffle_epi8 to AVX512 intrinsics check. -- Serpent CTR keystream generators did full 128-bit counter increment with separate 64-bit overflow path. Under ctr16be_enc contract generic ctr code splits work at low 16-bit overflow, so bulk function adds only to low 16 counter bits. Drop overflow paths, use 16-bit stepping. AVX512 slow path now does 16-bit vector addition and byte shuffle (vpaddw, vpshufb), which need AVX512BW. Add -mavx512bw to AVX512 intrinsics build flags and probe _mm512_shuffle_epi8 in configure. Moves serpent off transitional ctr_enc alias. Signed-off-by: Jussi Kivilinna <[email protected]> --- cipher/Makefile.am | 2 +- cipher/serpent-armv7-neon.S | 39 ------------- cipher/serpent-avx2-amd64.S | 71 +++++------------------- cipher/serpent-avx512-x86.c | 108 ++++++++++++++++-------------------- cipher/serpent-sse2-amd64.S | 67 +++++----------------- cipher/serpent.c | 4 +- configure.ac | 3 +- 7 files changed, 80 insertions(+), 214 deletions(-) diff --git a/cipher/Makefile.am b/cipher/Makefile.am index 8f54ea47..021a7ab6 100644 --- a/cipher/Makefile.am +++ b/cipher/Makefile.am @@ -357,7 +357,7 @@ rijndael-vp-aarch64.lo: $(srcdir)/rijndael-vp-aarch64.c Makefile `echo $(LTCOMPILE) $(aarch64_simd_cflags) -c $< | $(instrumentation_munging) ` if ENABLE_X86_AVX512_INTRINSICS_EXTRA_CFLAGS -avx512f_cflags = -mavx512f +avx512f_cflags = -mavx512f -mavx512bw else avx512f_cflags = endif diff --git a/cipher/serpent-armv7-neon.S b/cipher/serpent-armv7-neon.S index 4179ba2c..c8b9660b 100644 --- a/cipher/serpent-armv7-neon.S +++ b/cipher/serpent-armv7-neon.S @@ -702,10 +702,6 @@ _gcry_serpent_neon_ctr_enc: vmov RB3d0, RT0d0; vmov RT2d0, RT0d0; - /* check need for handling 64-bit overflow and carry */ - beq .Ldo_ctr_carry; - -.Lctr_carry_done: /* le => be */ vrev64.u8 RA1, RA1; vrev64.u8 RA2, RA2; @@ -757,41 +753,6 @@ _gcry_serpent_neon_ctr_enc: veor RB3, RB3; pop {r4,pc}; - -.Ldo_ctr_carry: - cmp r4, #-8; - blo .Lctr_carry_done; - beq .Lcarry_RT2; - - cmp r4, #-6; - blo .Lcarry_RB3; - beq .Lcarry_RB2; - - cmp r4, #-4; - blo .Lcarry_RB1; - beq .Lcarry_RB0; - - cmp r4, #-2; - blo .Lcarry_RA3; - beq .Lcarry_RA2; - - vsub.u64 RA1d0, RT1d0; -.Lcarry_RA2: - vsub.u64 RA2d0, RT1d0; -.Lcarry_RA3: - vsub.u64 RA3d0, RT1d0; -.Lcarry_RB0: - vsub.u64 RB0d0, RT1d0; -.Lcarry_RB1: - vsub.u64 RB1d0, RT1d0; -.Lcarry_RB2: - vsub.u64 RB2d0, RT1d0; -.Lcarry_RB3: - vsub.u64 RB3d0, RT1d0; -.Lcarry_RT2: - vsub.u64 RT2d0, RT1d0; - - b .Lctr_carry_done; .size _gcry_serpent_neon_ctr_enc,.-_gcry_serpent_neon_ctr_enc; .align 3 diff --git a/cipher/serpent-avx2-amd64.S b/cipher/serpent-avx2-amd64.S index 7aba235f..a419eb29 100644 --- a/cipher/serpent-avx2-amd64.S +++ b/cipher/serpent-avx2-amd64.S @@ -633,12 +633,6 @@ _gcry_serpent_avx2_blk16: CFI_ENDPROC(); ELF(.size _gcry_serpent_avx2_blk16,.-_gcry_serpent_avx2_blk16;) -#define inc_le128(x, minus_one, tmp) \ - vpcmpeqq minus_one, x, tmp; \ - vpsubq minus_one, x, x; \ - vpslldq $8, tmp, tmp; \ - vpsubq tmp, x, x; - .align 16 .globl _gcry_serpent_avx2_ctr_enc ELF(.type _gcry_serpent_avx2_ctr_enc,@function;) @@ -651,79 +645,40 @@ _gcry_serpent_avx2_ctr_enc: */ CFI_STARTPROC(); - movq 8(%rcx), %rax; - bswapq %rax; - vzeroupper; vbroadcasti128 .Lbswap128_mask rRIP, RTMP3; vpcmpeqd RNOT, RNOT, RNOT; - vpsrldq $8, RNOT, RNOT; /* ab: -1:0 ; cd: -1:0 */ - vpaddq RNOT, RNOT, RTMP2; /* ab: -2:0 ; cd: -2:0 */ + vpsrldq $14, RNOT, RNOT; /* ab: -1:0 ; cd: -1:0 */ + vpaddw RNOT, RNOT, RTMP2; /* ab: -2:0 ; cd: -2:0 */ /* load IV and byteswap */ vmovdqu (%rcx), RTMP4x; vpshufb RTMP3x, RTMP4x, RTMP4x; vmovdqa RTMP4x, RTMP0x; - inc_le128(RTMP4x, RNOTx, RTMP1x); + vpsubw RNOTx, RTMP4x, RTMP4x; vinserti128 $1, RTMP4x, RTMP0, RTMP0; vpshufb RTMP3, RTMP0, RA0; /* +1 ; +0 */ - /* check need for handling 64-bit overflow and carry */ - cmpq $(0xffffffffffffffff - 16), %rax; - ja .Lhandle_ctr_carry; - /* construct IVs */ - vpsubq RTMP2, RTMP0, RTMP0; /* +3 ; +2 */ + vpsubw RTMP2, RTMP0, RTMP0; /* +3 ; +2 */ vpshufb RTMP3, RTMP0, RA1; - vpsubq RTMP2, RTMP0, RTMP0; /* +5 ; +4 */ + vpsubw RTMP2, RTMP0, RTMP0; /* +5 ; +4 */ vpshufb RTMP3, RTMP0, RA2; - vpsubq RTMP2, RTMP0, RTMP0; /* +7 ; +6 */ + vpsubw RTMP2, RTMP0, RTMP0; /* +7 ; +6 */ vpshufb RTMP3, RTMP0, RA3; - vpsubq RTMP2, RTMP0, RTMP0; /* +9 ; +8 */ + vpsubw RTMP2, RTMP0, RTMP0; /* +9 ; +8 */ vpshufb RTMP3, RTMP0, RB0; - vpsubq RTMP2, RTMP0, RTMP0; /* +11 ; +10 */ + vpsubw RTMP2, RTMP0, RTMP0; /* +11 ; +10 */ vpshufb RTMP3, RTMP0, RB1; - vpsubq RTMP2, RTMP0, RTMP0; /* +13 ; +12 */ + vpsubw RTMP2, RTMP0, RTMP0; /* +13 ; +12 */ vpshufb RTMP3, RTMP0, RB2; - vpsubq RTMP2, RTMP0, RTMP0; /* +15 ; +14 */ + vpsubw RTMP2, RTMP0, RTMP0; /* +15 ; +14 */ vpshufb RTMP3, RTMP0, RB3; - vpsubq RTMP2, RTMP0, RTMP0; /* +16 */ - vpshufb RTMP3x, RTMP0x, RTMP0x; - - jmp .Lctr_carry_done; -.Lhandle_ctr_carry: - /* construct IVs */ - inc_le128(RTMP0, RNOT, RTMP1); - inc_le128(RTMP0, RNOT, RTMP1); - vpshufb RTMP3, RTMP0, RA1; /* +3 ; +2 */ - inc_le128(RTMP0, RNOT, RTMP1); - inc_le128(RTMP0, RNOT, RTMP1); - vpshufb RTMP3, RTMP0, RA2; /* +5 ; +4 */ - inc_le128(RTMP0, RNOT, RTMP1); - inc_le128(RTMP0, RNOT, RTMP1); - vpshufb RTMP3, RTMP0, RA3; /* +7 ; +6 */ - inc_le128(RTMP0, RNOT, RTMP1); - inc_le128(RTMP0, RNOT, RTMP1); - vpshufb RTMP3, RTMP0, RB0; /* +9 ; +8 */ - inc_le128(RTMP0, RNOT, RTMP1); - inc_le128(RTMP0, RNOT, RTMP1); - vpshufb RTMP3, RTMP0, RB1; /* +11 ; +10 */ - inc_le128(RTMP0, RNOT, RTMP1); - inc_le128(RTMP0, RNOT, RTMP1); - vpshufb RTMP3, RTMP0, RB2; /* +13 ; +12 */ - inc_le128(RTMP0, RNOT, RTMP1); - inc_le128(RTMP0, RNOT, RTMP1); - vpshufb RTMP3, RTMP0, RB3; /* +15 ; +14 */ - inc_le128(RTMP0, RNOT, RTMP1); - vextracti128 $1, RTMP0, RTMP0x; - vpshufb RTMP3x, RTMP0x, RTMP0x; /* +16 */ - -.align 4 -.Lctr_carry_done: - /* store new IV */ - vmovdqu RTMP0x, (%rcx); + /* Update IV */ + addb $16, 15(%rcx); + adcb $0, 14(%rcx); call __serpent_enc_blk16; diff --git a/cipher/serpent-avx512-x86.c b/cipher/serpent-avx512-x86.c index 5b5c2483..dfbe2432 100644 --- a/cipher/serpent-avx512-x86.c +++ b/cipher/serpent-avx512-x86.c @@ -618,74 +618,62 @@ ctr_generate(unsigned char *ctr, __m512i vin[8]) 4LL << 56, 0, 4LL << 56, 0, 4LL << 56, 0); - const __m512i add4567 = _mm512_add_epi32(add0123, add4444); - const __m512i add8888 = _mm512_add_epi32(add4444, add4444); + const __m512i add4567 = _mm512_add_epi8(add0123, add4444); + const __m512i add8888 = _mm512_add_epi8(add4444, add4444); // Fast path without carry handling. __m512i vctr = _mm512_broadcast_i32x4(_mm_loadu_si128((const void *)ctr)); - cipher_block_add(ctr, 32, blocksize); - vin[0] = _mm512_add_epi32(vctr, add0123); - vin[1] = _mm512_add_epi32(vctr, add4567); - vin[2] = _mm512_add_epi32(vin[0], add8888); - vin[3] = _mm512_add_epi32(vin[1], add8888); - vin[4] = _mm512_add_epi32(vin[2], add8888); - vin[5] = _mm512_add_epi32(vin[3], add8888); - vin[6] = _mm512_add_epi32(vin[4], add8888); - vin[7] = _mm512_add_epi32(vin[5], add8888); + cipher_block_add_be16(ctr, 32, blocksize); + vin[0] = _mm512_add_epi8(vctr, add0123); + vin[1] = _mm512_add_epi8(vctr, add4567); + vin[2] = _mm512_add_epi8(vin[0], add8888); + vin[3] = _mm512_add_epi8(vin[1], add8888); + vin[4] = _mm512_add_epi8(vin[2], add8888); + vin[5] = _mm512_add_epi8(vin[3], add8888); + vin[6] = _mm512_add_epi8(vin[4], add8888); + vin[7] = _mm512_add_epi8(vin[5], add8888); } else { - // Slow path. - u32 blocks[4][blocksize / sizeof(u32)]; - - cipher_block_cpy(blocks[0], ctr, blocksize); - cipher_block_cpy(blocks[1], ctr, blocksize); - cipher_block_cpy(blocks[2], ctr, blocksize); - cipher_block_cpy(blocks[3], ctr, blocksize); - cipher_block_add(ctr, 32, blocksize); - cipher_block_add(blocks[1], 1, blocksize); - cipher_block_add(blocks[2], 2, blocksize); - cipher_block_add(blocks[3], 3, blocksize); - vin[0] = _mm512_loadu_epi32 (blocks); - cipher_block_add(blocks[0], 4, blocksize); - cipher_block_add(blocks[1], 4, blocksize); - cipher_block_add(blocks[2], 4, blocksize); - cipher_block_add(blocks[3], 4, blocksize); - vin[1] = _mm512_loadu_epi32 (blocks); - cipher_block_add(blocks[0], 4, blocksize); - cipher_block_add(blocks[1], 4, blocksize); - cipher_block_add(blocks[2], 4, blocksize); - cipher_block_add(blocks[3], 4, blocksize); - vin[2] = _mm512_loadu_epi32 (blocks); - cipher_block_add(blocks[0], 4, blocksize); - cipher_block_add(blocks[1], 4, blocksize); - cipher_block_add(blocks[2], 4, blocksize); - cipher_block_add(blocks[3], 4, blocksize); - vin[3] = _mm512_loadu_epi32 (blocks); - cipher_block_add(blocks[0], 4, blocksize); - cipher_block_add(blocks[1], 4, blocksize); - cipher_block_add(blocks[2], 4, blocksize); - cipher_block_add(blocks[3], 4, blocksize); - vin[4] = _mm512_loadu_epi32 (blocks); - cipher_block_add(blocks[0], 4, blocksize); - cipher_block_add(blocks[1], 4, blocksize); - cipher_block_add(blocks[2], 4, blocksize); - cipher_block_add(blocks[3], 4, blocksize); - vin[5] = _mm512_loadu_epi32 (blocks); - cipher_block_add(blocks[0], 4, blocksize); - cipher_block_add(blocks[1], 4, blocksize); - cipher_block_add(blocks[2], 4, blocksize); - cipher_block_add(blocks[3], 4, blocksize); - vin[6] = _mm512_loadu_epi32 (blocks); - cipher_block_add(blocks[0], 4, blocksize); - cipher_block_add(blocks[1], 4, blocksize); - cipher_block_add(blocks[2], 4, blocksize); - cipher_block_add(blocks[3], 4, blocksize); - vin[7] = _mm512_loadu_epi32 (blocks); - - wipememory(blocks, sizeof(blocks)); + const __m512i add0123 = _mm512_set_epi64(0, 3, + 0, 2, + 0, 1, + 0, 0); + const __m512i add4444 = _mm512_set_epi64(0, 4, + 0, 4, + 0, 4, + 0, 4); + const __m512i add4567 = _mm512_add_epi16(add0123, add4444); + const __m512i add8888 = _mm512_add_epi16(add4444, add4444); + static const unsigned char bswap_mask[16] __attribute__ ((aligned (16))) = + { 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 }; + const __m512i vbswap = + _mm512_broadcast_i32x4(_mm_loadu_si128((const void *)bswap_mask)); + + // Slow path with 16-bit carry. + __m512i vctr = + _mm512_broadcast_i32x4(_mm_loadu_si128((const void *)ctr)); + + cipher_block_add_be16(ctr, 32, blocksize); + vctr = _mm512_shuffle_epi8(vctr, vbswap); + vin[0] = _mm512_add_epi16(vctr, add0123); + vin[1] = _mm512_add_epi16(vctr, add4567); + vin[2] = _mm512_add_epi16(vin[0], add8888); + vin[3] = _mm512_add_epi16(vin[1], add8888); + vin[4] = _mm512_add_epi16(vin[2], add8888); + vin[5] = _mm512_add_epi16(vin[3], add8888); + vin[6] = _mm512_add_epi16(vin[4], add8888); + vin[7] = _mm512_add_epi16(vin[5], add8888); + vin[0] = _mm512_shuffle_epi8(vin[0], vbswap); + vin[1] = _mm512_shuffle_epi8(vin[1], vbswap); + vin[2] = _mm512_shuffle_epi8(vin[2], vbswap); + vin[3] = _mm512_shuffle_epi8(vin[3], vbswap); + vin[4] = _mm512_shuffle_epi8(vin[4], vbswap); + vin[5] = _mm512_shuffle_epi8(vin[5], vbswap); + vin[6] = _mm512_shuffle_epi8(vin[6], vbswap); + vin[7] = _mm512_shuffle_epi8(vin[7], vbswap); } } diff --git a/cipher/serpent-sse2-amd64.S b/cipher/serpent-sse2-amd64.S index 885c2bf1..93912935 100644 --- a/cipher/serpent-sse2-amd64.S +++ b/cipher/serpent-sse2-amd64.S @@ -688,68 +688,27 @@ _gcry_serpent_sse2_ctr_enc: pbswap(RTMP0, RTMP1); /* be => le */ pcmpeqd RNOT, RNOT; - psrldq $8, RNOT; /* low: -1, high: 0 */ + psrldq $14, RNOT; /* low: -1, high: 0 */ movdqa RNOT, RTMP2; - paddq RTMP2, RTMP2; /* low: -2, high: 0 */ + paddw RTMP2, RTMP2; /* low: -2, high: 0 */ /* construct IVs */ movdqa RTMP0, RTMP1; - psubq RNOT, RTMP0; /* +1 */ + psubw RNOT, RTMP0; /* +1 */ movdqa RTMP0, RA1; - psubq RTMP2, RTMP1; /* +2 */ + psubw RTMP2, RTMP1; /* +2 */ movdqa RTMP1, RA2; - psubq RTMP2, RTMP0; /* +3 */ + psubw RTMP2, RTMP0; /* +3 */ movdqa RTMP0, RA3; - psubq RTMP2, RTMP1; /* +4 */ + psubw RTMP2, RTMP1; /* +4 */ movdqa RTMP1, RB0; - psubq RTMP2, RTMP0; /* +5 */ + psubw RTMP2, RTMP0; /* +5 */ movdqa RTMP0, RB1; - psubq RTMP2, RTMP1; /* +6 */ + psubw RTMP2, RTMP1; /* +6 */ movdqa RTMP1, RB2; - psubq RTMP2, RTMP0; /* +7 */ + psubw RTMP2, RTMP0; /* +7 */ movdqa RTMP0, RB3; - psubq RTMP2, RTMP1; /* +8 */ - - /* check need for handling 64-bit overflow and carry */ - cmpl $0xffffffff, 8(%rcx); - jne .Lno_ctr_carry; - - movl 12(%rcx), %eax; - bswapl %eax; - cmpl $-8, %eax; - jb .Lno_ctr_carry; - pslldq $8, RNOT; /* low: 0, high: -1 */ - je .Lcarry_RTMP0; - - cmpl $-6, %eax; - jb .Lcarry_RB3; - je .Lcarry_RB2; - - cmpl $-4, %eax; - jb .Lcarry_RB1; - je .Lcarry_RB0; - - cmpl $-2, %eax; - jb .Lcarry_RA3; - je .Lcarry_RA2; - - psubq RNOT, RA1; -.Lcarry_RA2: - psubq RNOT, RA2; -.Lcarry_RA3: - psubq RNOT, RA3; -.Lcarry_RB0: - psubq RNOT, RB0; -.Lcarry_RB1: - psubq RNOT, RB1; -.Lcarry_RB2: - psubq RNOT, RB2; -.Lcarry_RB3: - psubq RNOT, RB3; -.Lcarry_RTMP0: - psubq RNOT, RTMP1; - -.Lno_ctr_carry: + /* le => be */ pbswap(RA1, RTMP0); pbswap(RA2, RTMP0); @@ -759,8 +718,10 @@ _gcry_serpent_sse2_ctr_enc: pbswap(RB2, RTMP0); pbswap(RB3, RTMP0); pbswap(RTMP1, RTMP0); - /* store new IV */ - movdqu RTMP1, (%rcx); + + /* Update IV */ + addb $8, 15(%rcx); + adcb $0, 14(%rcx); call __serpent_enc_blk8; diff --git a/cipher/serpent.c b/cipher/serpent.c index 72a2287d..37e0c63a 100644 --- a/cipher/serpent.c +++ b/cipher/serpent.c @@ -854,7 +854,7 @@ serpent_setkey (void *ctx, memset (bulk_ops, 0, sizeof(*bulk_ops)); bulk_ops->cbc_dec = _gcry_serpent_cbc_dec; bulk_ops->cfb_dec = _gcry_serpent_cfb_dec; - bulk_ops->ctr_enc = _gcry_serpent_ctr_enc; + bulk_ops->ctr16be_enc = _gcry_serpent_ctr_enc; bulk_ops->ocb_crypt = _gcry_serpent_ocb_crypt; bulk_ops->ocb_auth = _gcry_serpent_ocb_auth; bulk_ops->xts_crypt = _gcry_serpent_xts_crypt; @@ -1126,7 +1126,7 @@ _gcry_serpent_ctr_enc(void *context, unsigned char *ctr, outbuf += sizeof(serpent_block_t); inbuf += sizeof(serpent_block_t); /* Increment the counter. */ - cipher_block_add(ctr, 1, sizeof(serpent_block_t)); + cipher_block_add_be16(ctr, 1, sizeof(serpent_block_t)); } wipememory(tmpbuf, sizeof(tmpbuf)); diff --git a/configure.ac b/configure.ac index 604f063e..631ed81e 100644 --- a/configure.ac +++ b/configure.ac @@ -1834,7 +1834,7 @@ fi # Check whether compiler supports x86/AVX512 intrinsics # _gcc_cflags_save=$CFLAGS -CFLAGS="$CFLAGS -mavx512f" +CFLAGS="$CFLAGS -mavx512f -mavx512bw" AC_CACHE_CHECK([whether compiler supports x86/AVX512 intrinsics], [gcry_cv_cc_x86_avx512_intrinsics], @@ -1851,6 +1851,7 @@ AC_CACHE_CHECK([whether compiler supports x86/AVX512 intrinsics], x = _mm512_loadu_epi32 (in); /* check the GCC bug 90980. */ x = _mm512_maskz_loadu_epi32(_cvtu32_mask16(0xfff0), in) ^ _mm512_castsi128_si512(y); + x = _mm512_shuffle_epi8(x, x); asm volatile ("vinserti32x4 \$3, %0, %%zmm6, %%zmm6;\n\t" "vpxord %%zmm6, %%zmm6, %%zmm6" ::"x"(y),"r"(in):"memory","xmm6"); -- 2.53.0