Botan 3.13.0
Crypto and TLS for C&
ghash_avx512_clmul.cpp
Go to the documentation of this file.
1/*
2* (C) 2026 Jack Lloyd
3*
4* Botan is released under the Simplified BSD License (see license.txt)
5*/
6
7#include <botan/internal/ghash.h>
8
9#include <botan/internal/isa_extn.h>
10#include <botan/internal/polyval_fn.h>
11#include <botan/internal/simd_4x32.h>
12#include <botan/internal/target_info.h>
13#include <immintrin.h>
14
15#if defined(BOTAN_HAS_POLYVAL)
16 #include <botan/internal/polyval.h>
17#endif
18
19namespace Botan {
20
21namespace {
22
23template <bool BSWAP>
24BOTAN_FORCE_INLINE __m512i BOTAN_FN_ISA_AVX512_CLMUL load_blocks(const uint8_t in[]) {
25 const auto B = _mm512_loadu_si512(in);
26
27 if constexpr(BSWAP) {
28 // Byte swap each lane
29 const auto swap = _mm512_set_epi64(0x0001020304050607,
30 0x08090A0B0C0D0E0F,
31 0x0001020304050607,
32 0x08090A0B0C0D0E0F,
33 0x0001020304050607,
34 0x08090A0B0C0D0E0F,
35 0x0001020304050607,
36 0x08090A0B0C0D0E0F);
37 return _mm512_shuffle_epi8(B, swap);
38 } else {
39 return B;
40 }
41}
42
43BOTAN_FORCE_INLINE __m512i BOTAN_FN_ISA_AVX512_CLMUL fold(__m512i H) {
44 return _mm512_xor_si512(H, _mm512_bsrli_epi128(H, 8));
45}
46
47BOTAN_FORCE_INLINE SIMD_4x32 BOTAN_FN_ISA_AVX512_CLMUL reduce_xor(__m512i z) {
48 auto y = _mm256_xor_si256(_mm512_castsi512_si256(z), _mm512_extracti64x4_epi64(z, 0x1));
49 auto x = _mm_xor_si128(_mm256_castsi256_si128(y), _mm256_extracti32x4_epi32(y, 0x1));
50 return SIMD_4x32(x);
51}
52
53BOTAN_FORCE_INLINE void BOTAN_FN_ISA_AVX512_CLMUL
54ghash_x4_accum(__m512i H, __m512i H_fold, __m512i M, __m512i& lo, __m512i& hi, __m512i& mid) {
55 lo = _mm512_xor_si512(lo, _mm512_clmulepi64_epi128(H, M, 0x00));
56 hi = _mm512_xor_si512(hi, _mm512_clmulepi64_epi128(H, M, 0x11));
57 mid = _mm512_xor_si512(mid, _mm512_clmulepi64_epi128(H_fold, fold(M), 0x00));
58}
59
60BOTAN_FORCE_INLINE SIMD_4x32 BOTAN_FN_ISA_AVX512_CLMUL ghash_reduce(__m512i lo, __m512i hi, __m512i mid) {
61 mid = _mm512_ternarylogic_epi64(lo, mid, hi, 0x96); // mid ^= lo ^ hi
62 hi = _mm512_xor_si512(hi, _mm512_bsrli_epi128(mid, 8));
63 lo = _mm512_xor_si512(lo, _mm512_bslli_epi128(mid, 8));
64 return polyval_reduce(reduce_xor(hi), reduce_xor(lo));
65}
66
67BOTAN_FORCE_INLINE __m512i BOTAN_FN_ISA_AVX512_CLMUL insert_a(__m512i M, const SIMD_4x32& a) {
68 return _mm512_xor_epi64(M, _mm512_inserti64x2(_mm512_setzero_si512(), a.raw(), 0));
69}
70
71void BOTAN_FN_ISA_AVX512_CLMUL precompute_avx512(const SIMD_4x32& H1, uint64_t H_pow[16 * 2]) {
72 const SIMD_4x32 H2 = polyval_multiply(H1, H1);
73 const SIMD_4x32 H3 = polyval_multiply(H1, H2);
74 const SIMD_4x32 H4 = polyval_multiply(H2, H2);
75
76 const SIMD_4x32 H5 = polyval_multiply(H4, H1);
77 const SIMD_4x32 H6 = polyval_multiply(H4, H2);
78 const SIMD_4x32 H7 = polyval_multiply(H4, H3);
79 const SIMD_4x32 H8 = polyval_multiply(H4, H4);
80
81 const SIMD_4x32 H9 = polyval_multiply(H8, H1);
82 const SIMD_4x32 H10 = polyval_multiply(H8, H2);
83 const SIMD_4x32 H11 = polyval_multiply(H8, H3);
84 const SIMD_4x32 H12 = polyval_multiply(H8, H4);
85
86 const SIMD_4x32 H13 = polyval_multiply(H8, H5);
87 const SIMD_4x32 H14 = polyval_multiply(H8, H6);
88 const SIMD_4x32 H15 = polyval_multiply(H8, H7);
89 const SIMD_4x32 H16 = polyval_multiply(H8, H8);
90
91 // Store in reversed order in blocks of 4 so that the zmm load
92 // of H powers matches up with the message blocks
93 H4.store_le(H_pow);
94 H3.store_le(H_pow + 2);
95 H2.store_le(H_pow + 4);
96 H1.store_le(H_pow + 6);
97
98 H8.store_le(H_pow + 8);
99 H7.store_le(H_pow + 10);
100 H6.store_le(H_pow + 12);
101 H5.store_le(H_pow + 14);
102
103 H12.store_le(H_pow + 16);
104 H11.store_le(H_pow + 18);
105 H10.store_le(H_pow + 20);
106 H9.store_le(H_pow + 22);
107
108 H16.store_le(H_pow + 24);
109 H15.store_le(H_pow + 26);
110 H14.store_le(H_pow + 28);
111 H13.store_le(H_pow + 30);
112}
113
114template <bool BSWAP>
115void BOTAN_FN_ISA_AVX512_CLMUL
116multiply_avx512(uint8_t x[16], const uint64_t H_pow[16 * 2], const uint8_t input[], size_t blocks) {
118
119 if(blocks >= 16) {
120 const auto H1 = _mm512_loadu_si512(H_pow); // [H4,H3,H2,H1]
121 const auto H2 = _mm512_loadu_si512(H_pow + 8); // [H8,H7,H6,H5]
122 const auto H3 = _mm512_loadu_si512(H_pow + 16); // [H12,H11,H10,H9]
123 const auto H4 = _mm512_loadu_si512(H_pow + 24); // [H16,H15,H14,H13]
124
125 // Precompute H folds (H ^ (H >> 64)) for Karatsuba - loop invariant
126 const auto H1_fold = fold(H1);
127 const auto H2_fold = fold(H2);
128 const auto H3_fold = fold(H3);
129 const auto H4_fold = fold(H4);
130
131 while(blocks >= 16) {
132 __m512i M1 = load_blocks<BSWAP>(input);
133 const auto M2 = load_blocks<BSWAP>(input + 64);
134 const auto M3 = load_blocks<BSWAP>(input + 128);
135 const auto M4 = load_blocks<BSWAP>(input + 192);
136
137 M1 = insert_a(M1, a);
138
139 auto lo = _mm512_setzero_si512();
140 auto hi = _mm512_setzero_si512();
141 auto mid = _mm512_setzero_si512();
142
143 ghash_x4_accum(H4, H4_fold, M1, lo, hi, mid);
144 ghash_x4_accum(H3, H3_fold, M2, lo, hi, mid);
145 ghash_x4_accum(H2, H2_fold, M3, lo, hi, mid);
146 ghash_x4_accum(H1, H1_fold, M4, lo, hi, mid);
147
148 a = ghash_reduce(lo, hi, mid);
149
150 input += 16 * 16;
151 blocks -= 16;
152 }
153 }
154
155 if(blocks >= 8) {
156 const auto H1 = _mm512_loadu_si512(H_pow); // [H4,H3,H2,H1]
157 const auto H2 = _mm512_loadu_si512(H_pow + 8); // [H8,H7,H6,H5]
158
159 const auto H1_fold = fold(H1);
160 const auto H2_fold = fold(H2);
161
162 while(blocks >= 8) {
163 __m512i M1 = load_blocks<BSWAP>(input);
164 const __m512i M2 = load_blocks<BSWAP>(input + 64);
165
166 M1 = insert_a(M1, a);
167
168 auto lo = _mm512_setzero_si512();
169 auto hi = _mm512_setzero_si512();
170 auto mid = _mm512_setzero_si512();
171
172 ghash_x4_accum(H2, H2_fold, M1, lo, hi, mid);
173 ghash_x4_accum(H1, H1_fold, M2, lo, hi, mid);
174
175 a = ghash_reduce(lo, hi, mid);
176
177 input += 8 * 16;
178 blocks -= 8;
179 }
180 }
181
182 if(blocks >= 4) {
183 const auto H1 = _mm512_loadu_si512(H_pow); // [H4,H3,H2,H1]
184 const auto H1_fold = fold(H1);
185
186 while(blocks >= 4) {
187 __m512i M = load_blocks<BSWAP>(input);
188 M = insert_a(M, a);
189
190 auto lo = _mm512_clmulepi64_epi128(H1, M, 0x00);
191 auto hi = _mm512_clmulepi64_epi128(H1, M, 0x11);
192 auto mid = _mm512_clmulepi64_epi128(H1_fold, fold(M), 0x00);
193
194 a = ghash_reduce(lo, hi, mid);
195
196 input += 4 * 16;
197 blocks -= 4;
198 }
199 }
200
201 if(blocks > 0) {
202 // H1 is at offset 6 in the reversed layout [H4,H3,H2,H1,...]
203 const SIMD_4x32 H1 = SIMD_4x32::load_le(H_pow + 6);
204
205 for(size_t i = 0; i != blocks; ++i) {
206 const SIMD_4x32 m = load_block<BSWAP>(input + 16 * i);
207 a ^= m;
208 a = polyval_multiply(H1, a);
209 }
210 }
211
212 store_block<BSWAP>(a, x);
213}
214
215} // namespace
216
217void BOTAN_FN_ISA_AVX512_CLMUL GHASH::ghash_precompute_avx512_clmul(const uint8_t H_bytes[16], uint64_t H_pow[16 * 2]) {
218 precompute_avx512(mulx_polyval(reverse_vector(SIMD_4x32::load_le(H_bytes))), H_pow);
219}
220
221void BOTAN_FN_ISA_AVX512_CLMUL GHASH::ghash_multiply_avx512_clmul(uint8_t x[16],
222 const uint64_t H_pow[16 * 2],
223 const uint8_t input[],
224 size_t blocks) {
225 multiply_avx512<true>(x, H_pow, input, blocks);
226}
227
228#if defined(BOTAN_HAS_POLYVAL)
229
230void BOTAN_FN_ISA_AVX512_CLMUL Polyval::polyval_precompute_avx512_clmul(const uint8_t H[16], uint64_t H_pow[16 * 2]) {
231 precompute_avx512(SIMD_4x32::load_le(H), H_pow);
232}
233
234void BOTAN_FN_ISA_AVX512_CLMUL Polyval::polyval_multiply_avx512_clmul(uint8_t x[16],
235 const uint64_t H_pow[16 * 2],
236 const uint8_t input[],
237 size_t blocks) {
238 multiply_avx512<false>(x, H_pow, input, blocks);
239}
240
241#endif
242
243} // namespace Botan
static SIMD_4x32 BOTAN_FN_ISA_SIMD_4X32 load_le(const void *in) noexcept
Definition simd_4x32.h:162
#define BOTAN_FORCE_INLINE
Definition compiler.h:87
BOTAN_FORCE_INLINE SIMD_4x32 BOTAN_FN_ISA_SIMD_4X32 mulx_polyval(const SIMD_4x32 &h)
Definition polyval_fn.h:114
BOTAN_FORCE_INLINE BOTAN_FN_ISA_SIMD_4X32 SIMD_4x32 load_block(const uint8_t in[])
Definition polyval_fn.h:97
BOTAN_FORCE_INLINE SIMD_4x32 BOTAN_FN_ISA_CLMUL polyval_multiply(const SIMD_4x32 &H, const SIMD_4x32 &x)
Definition polyval_fn.h:150
BOTAN_FORCE_INLINE SIMD_4x32 BOTAN_FN_ISA_CLMUL polyval_reduce(const SIMD_4x32 &hi, const SIMD_4x32 &lo)
Definition polyval_fn.h:129
BOTAN_FORCE_INLINE BOTAN_FN_ISA_SIMD_4X32 SIMD_4x32 reverse_vector(const SIMD_4x32 &in)
Definition polyval_fn.h:16
BOTAN_FORCE_INLINE BOTAN_FN_ISA_SIMD_4X32 void store_block(const SIMD_4x32 &b, uint8_t out[])
Definition polyval_fn.h:106