ReactOS 0.4.17-dev-1005-g171e1de
ghash_definitions.h
Go to the documentation of this file.
1//
2// ghash_definitions.h
3//
4// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
5//
6
8// Constants & globals
9//
10
11#define GF128_FIELD_R_BYTE (0xe1)
12#define UINT64_NEG(x) ((UINT64)-(INT64)(x))
13
14
15
17// Pclmulqdq implementation
18//
19
20/*
21GHASH GF(2^128) multiplication using PCLMULQDQ
22
23The GF(2^128) field used in GHASH is GF(2)[x]/p(x) where p(x) is the primitive polynomial
24 x^128 + x^7 + x^2 + x + 1
25
26Notation: We use the standard mathematical notation '+' for the addition in the field,
27which corresponds to a xor of the bits.
28
29Multiplication:
30Given two field elements A and B (represented as 128-bit values),
31we first compute the polynomial product
32 (C,D) := A * B
33where C and D are also 128-bit values.
34
35The PCLMULQDQ instruction performs a 64 x 64 -> 128 bit carryless multiplication.
36To multiply 128-bit values we write A = (A1, A0) and B = (B1, B0) in two 64-bit halves.
37
38The schoolbook multiplication is computed by
39 (C, D) = (A1 * B1)x^128 + (A1 * B0 + A0 * B1)x^64 + (A0 * B0)
40This require four PCLMULQDQ instructions. The middle 128-bit result has to be shifted
41left and right, and each half added to the upper and lower 128-bit result to get (C,D).
42
43Alternatively, the middle 128-bit intermediate result be computed using Karatsuba:
44 (A1*B0 + A0*B1) = (A1 + A0) * (B1 + B0) + (A1*B1) + (A0*B0)
45This requires only one PCLMULQDQ instruction to multiply (A1 + A0) by (B1 + B0)
46as the other two products are already computed.
47Whether this is faster depends on the relative speed of shift/xor verses PCLMULQDQ.
48
49Both multiplication algorithms produce three 128-bit intermediate results (R1, Rmid, R0),
50with the full result defined by R1 x^128 + Rmid x^64 + R0.
51If we do Multiply-Accumulate then we can accumulate the three 128-bit intermediate results
52directly. As there are no carries, there is no overflow, and the combining of the three
53intermediate results into a 256-bit result can be shared amongst all multiplications.
54
55
56Modulo reduction:
57We use << and >> to denote shifts on 128-bit values.
58The modulo reduction can now be done as follows:
59given a 256-bit value (C,D) representing C x^128 + D we compute
60 (T1,T0) := C + C*x + C * x^2 + C * x^7
61 R := D + T0 + T1 + (T1 << 1) + (T1 << 2) + (T1 << 7)
62
63(T1,T0) is just the value C x^128 reduced one step modulo p(x).The value T1 is at most 7 bits,
64so in the next step the reduction, which computes the result R, is easy. The
65expression T1 + (T1 << 1) + (T1 << 2) + (T1 << 7) is just T1 * x^128 reduced modulo p(x).
66
67Let's first get rid of the polynomial arithmetic and write this completely using shifts on
68128-bit values.
69
70T0 := C + (C << 1) + (C << 2) + (C << 7)
71T1 := (C >> 127) + (C >> 126) + (C >> 121)
72R := D + T0 + T1 + (T1 << 1) + (T1 << 2) + (T1 << 7)
73
74We can optimize this by rewriting the equations
75
76T2 := T1 + C
77 = C + (C>>127) + (C>>126) + (C>>121)
78R = D + T0 + T1 + (T1 << 1) + (T1 << 2) + (T1 << 7)
79 = D + C + (C << 1) + (C << 2) + (C << 7) + T1 + (T1 << 1) + (T1 << 2) + (T1 << 7)
80 = D + T2 + (T2 << 1) + (T2 << 2) + (T2 << 7)
81
82Thus
83T2 = C + (C>>127) + (C>>126) + (C>>121)
84R = D + T2 + (T2 << 1) + (T2 << 2) + (T2 << 7)
85
86Gets the right result and uses only 6 shifts.
87
88The SSE instruction set does not implement bit-shifts of 128-bit values. Instead, we will
89use bit-shifts of the 32-bit subvalues, and byte shifts (shifts by a multiple of 8 bits)
90on the full 128-bit values.
91We use the <<<< and >>>> operators to denote shifts on 32-bit subwords.
92
93We can now do the modulo reduction by
94
95t1 := (C >> 127) = (C >>>> 31) >> 96
96t2 := (C >> 126) = (C >>>> 30) >> 96
97t3 := (C >> 121) = (C >>>> 25) >> 96
98T2 = C + t1 + t2 + t3
99
100left-shifts in the computation of R are a bit more involved as we have to move bits from
101one subword to the next
102
103u1 := (T2 << 1) = (T2 <<<< 1) + ((T2 >>>> 31) << 32)
104u2 := (T2 << 2) = (T2 <<<< 2) + ((T2 >>>> 30) << 32)
105u3 := (T2 << 7) = (T2 <<<< 7) + ((T2 >>>> 25) << 32)
106R = D + T2 + u1 + u2 + u3
107
108We can eliminate some common subexpressions. For any k we have
109(T2 >>>> k) = ((C + r) >>>> k)
110where r is a 7-bit value. If k>7 then this is equal to (C >>>> k). This means that
111the value (T2 >>>> 31) is equal to (C >>>> 31) so we don't have to compute it again.
112
113So we can rewrite our formulas as
114t4 := (C >>>> 31)
115t5 := (C >>>> 30)
116t6 := (C >>>> 25)
117ts = t4 + t5 + t6
118T2 = C + (ts >> 96)
119
120Note that ts = (C >>>> 31) + (C >>>> 30) + (C >>>> 25)
121which is equal to (T2 >>>> 31) + (T2 >>>> 30) + (T2 >>>> 25)
122
123R = D + T2 + u1 + u2 + u3
124 = D + T2 + (T2 <<<< 1) + (T2 <<<< 2) + (T2 <<<< 7) + (ts << 32)
125
126All together, we can do the modulo reduction using the following formulas
127
128ts := (C >>>> 31) + (C >>>> 30) + (C >>>> 25)
129T2 := C + (ts >> 96)
130R = D + T2 + (T2 <<<< 1) + (T2 <<<< 2) + (T2 <<<< 7) + (ts << 32)
131
132Using a total of 16 operations. (6 subword shifts, 2 byte shifts, and 8 additions)
133
134Reversed bit order:
135There is one more complication. GHASH uses the bits in the reverse order from normal representation.
136The bits b_0, b_1, ..., b_127 represent the polynomial b_0 + b_1 * x + ... + b_127 * x^127.
137This means that the most significant bit in each byte is actually the least significant bit in the
138polynomial.
139
140SSE CPUs use the LSBFirst convention. This means that the bits b_0, b_1, ..., b_127 of the polynomial
141end up at positions 7, 6, 5, ..., 1, 0, 15, 14, ..., 9, 8, 23, 22, ... of our XMM register.
142This is obviously not a useful representation to do arithmetic in.
143The first step is to BSWAP the value so that the bits appear in pure reverse order.
144That is at least algebraically useful.
145
146To compute the multiplication we use the fact that GF(2)[x] multiplication has no carries and
147thus no preference for bit order. After the BSWAP we don't have the values A and B, but rather
148rev(A) and rev(B) where rev() is a function that reverses the bit order. We can now compute
149
150 rev(A) * rev(B) = rev( A*B ) >> 1
151
152where the shift operator is on the 256-bit product.
153
154The modulo reduction remains the same, except that we change all the shifts to be the other direction.
155
156This gives us finally the outline of our multiplication:
157
158- Apply BSWAP to all values loaded from memory.
159 A := BSWAP( Abytes )
160 B := BSWAP( Bbytes )
161- Compute the 256-bit product, possibly using Karatsuba.
162 (P1, P0) := A * B // 128x128 carryless multiplication
163- Shift the result left one bit.
164 (Q1, Q0) := (P1, P0) << 1
165 which is computed as
166 Q0 = (P0 <<<< 1) + (P0 >>>> 31) << 32
167 Q1 = (P1 <<<< 1) + (P1 >>>> 31) << 32 + (P0 >>>> 31) >> 96
168- Perform the modulo reduction, with reversed bit order
169 ts := (Q0 <<<< 31) + (Q0 <<<< 30) + (Q0 <<<< 25)
170 T2 := Q0 + (ts << 96)
171 R = Q1 + T2 + (T2 >>>> 1) + (T2 >>>> 2) + (T2 >>>> 7) + (ts >> 32)
172
173Future work:
174It might be possible to construct a faster solution by merging the leftshift of (P1,P0)
175with the modulo reduction.
176
177*/
178
179#if SYMCRYPT_CPU_X86 | SYMCRYPT_CPU_AMD64
180
181#define SYMCRYPT_GHASH_PCLMULQDQ_HPOWERS 32
182
183#define GHASH_H_POWER( ghashTable, ind ) ( (ghashTable)[ SYMCRYPT_GHASH_PCLMULQDQ_HPOWERS - (ind)].m128i )
184#define GHASH_Hx_POWER( ghashTable, ind ) ( (ghashTable)[2*SYMCRYPT_GHASH_PCLMULQDQ_HPOWERS - (ind)].m128i )
185
186//
187// We define a few macros
188//
189
190//
191// CLMUL_4 multiplies two operands into three intermediate results using 4 pclmulqdq instructions
192//
193#define CLMUL_4( opA, opB, resl, resm, resh ) \
194{ \
195 resl = _mm_clmulepi64_si128( opA, opB, 0x00 ); \
196 resm = _mm_xor_si128( _mm_clmulepi64_si128( opA, opB, 0x01 ), _mm_clmulepi64_si128( opA, opB, 0x10 ) ); \
197 resh = _mm_clmulepi64_si128( opA, opB, 0x11 ); \
198};
199
200//
201// CLMUL_3 multiplies two operands into three intermediate results using 3 pclmulqdq instructions.
202// The second operand has a pre-computed difference of the two halves.
203// This uses Karatsuba, but we delay xorring the high and low piece into the middle piece.
204//
205#define CLMUL_3( opA, opB, opBx, resl, resm, resh ) \
206{ \
207 __m128i _tmpA; \
208 resl = _mm_clmulepi64_si128( opA, opB, 0x00 ); \
209 resh = _mm_clmulepi64_si128( opA, opB, 0x11 ); \
210 _tmpA = _mm_xor_si128( opA, _mm_srli_si128( opA, 8 ) ); \
211 resm = _mm_clmulepi64_si128( _tmpA, opBx, 0x00 ); \
212};
213//
214// CLMUL_X_3 is as CLMUL_3 only it takes precomputed differences of both multiplicands.
215//
216#define CLMUL_X_3( opA, opAx, opB, opBx, resl, resm, resh ) \
217{ \
218 resl = _mm_clmulepi64_si128( opA, opB, 0x00 ); \
219 resh = _mm_clmulepi64_si128( opA, opB, 0x11 ); \
220 resm = _mm_clmulepi64_si128( opAx, opBx, 0x00 ); \
221};
222
223//
224// Post-process the CLMUL_3 result to be compatible with the CLMUL_4
225//
226#define CLMUL_3_POST( resl, resm, resh ) \
227 resm = _mm_xor_si128( resm, _mm_xor_si128( resl, resh ) );
228
229//
230// Multiply-accumulate using CLMUL_4
231//
232#define CLMUL_ACC_4( opA, opB, resl, resm, resh ) \
233{\
234 __m128i _tmpl, _tmpm, _tmph;\
235 CLMUL_4( opA, opB, _tmpl, _tmpm, _tmph );\
236 resl = _mm_xor_si128( resl, _tmpl ); \
237 resm = _mm_xor_si128( resm, _tmpm ); \
238 resh = _mm_xor_si128( resh, _tmph ); \
239};
240
241//
242// Multiply-accumulate using CLMUL_3
243//
244#define CLMUL_ACC_3( opA, opB, opBx, resl, resm, resh ) \
245{\
246 __m128i _tmpl, _tmpm, _tmph;\
247 CLMUL_3( opA, opB, opBx, _tmpl, _tmpm, _tmph );\
248 resl = _mm_xor_si128( resl, _tmpl ); \
249 resm = _mm_xor_si128( resm, _tmpm ); \
250 resh = _mm_xor_si128( resh, _tmph ); \
251};
252#define CLMUL_ACC_3_Ymm( opA, opB, opBx, resl, resm, resh ) \
253{\
254 __m256i _tmpl, _tmpm, _tmph;\
255 __m256i _tmpA; \
256 _tmpl = _mm256_clmulepi64_epi128( opA, opB, 0x00 ); \
257 _tmph = _mm256_clmulepi64_epi128( opA, opB, 0x11 ); \
258 _tmpA = _mm256_xor_si256( opA, _mm256_srli_si256( opA, 8 ) ); \
259 _tmpm = _mm256_clmulepi64_epi128( _tmpA, opBx, 0x00 ); \
260 resl = _mm256_xor_si256( resl, _tmpl ); \
261 resm = _mm256_xor_si256( resm, _tmpm ); \
262 resh = _mm256_xor_si256( resh, _tmph ); \
263};
264
265
266//
267// Convert the 3 intermediate results to a 256-bit result,
268// and do the modulo reduction.
269#define MODREDUCE( vMultiplicationConstant, rl, rm, rh, res ) \
270{\
271 __m128i _T0, _T1; \
272\
273 /* multiply rl by constant which is (rev(0x87) << 1) - we'll eor the lost high bit in manually */ \
274 _T0 = _mm_clmulepi64_si128( rl, vMultiplicationConstant, 0x00 ); \
275\
276 /* we want the high 64b of rl to align with the low 64b of rm, because we haven't merged rm into rl and rh */ \
277 /* we want the low 64b of rl to align with the high 64b of rm, because we lost the high bit in the previous pmull */ \
278 rl = _mm_shuffle_epi32( rl, _MM_SHUFFLE( 1, 0, 3, 2 ) ); \
279\
280 rm = _mm_xor_si128( rm, _T0 ); \
281 rm = _mm_xor_si128( rm, rl ); \
282\
283 /* almost same again to fold rm into rh, but bit 63 needs no more multiplication and the result ultimately needs shifting left by 1 */ \
284 /* pre-shift bottom of rm left by 1 and accumulate the result when the other parts are aligned */ \
285 _T0 = _mm_clmulepi64_si128( _mm_slli_epi64( rm, 1 ), vMultiplicationConstant, 0x00 ); \
286\
287 rm = _mm_shuffle_epi32( rm, _MM_SHUFFLE( 1, 0, 3, 2 ) ); \
288 res = _mm_xor_si128( rh, rm ); \
289\
290 /* rotate res left by 1 and accumulate the aligned parts */ \
291 _T1 = _mm_slli_epi32( res, 1 ); \
292 res = _mm_srli_epi32( res, 31 ); \
293\
294 _T0 = _mm_xor_si128( _T0, _T1 ); \
295 res = _mm_shuffle_epi32( res, _MM_SHUFFLE( 2, 1, 0, 3 ) ); \
296\
297 res = _mm_xor_si128( res, _T0 ); \
298};
299
300//
301// See the large comment above on how this is done.
302// When we want to do MODREDUCE in parallel with other work, making use of pclmuldq to reduce
303// total instruction count (and register pressure) is beneficial. When testing on Haswell,
304// using the newer approach is beneficial. Keeping the old approach around in case we have significant
305// regression on older platforms.
306//
307#define MODREDUCE_OLD( rl, rm, rh, res ) \
308{\
309 __m128i _T0, _T1, _T2, _Q0, _Q1; \
310 rl = _mm_xor_si128( rl, _mm_slli_si128( rm, 8 ) ); \
311 rh = _mm_xor_si128( rh, _mm_srli_si128( rm, 8 ) ); \
312\
313 _Q0 = _mm_slli_epi32( rl, 1 ); \
314 _Q1 = _mm_slli_epi32( rh, 1 ); \
315\
316 _T0 = _mm_srli_epi32( rl, 31 ); \
317 _T1 = _mm_srli_epi32( rh, 31 ); \
318\
319 _T1 = _mm_alignr_epi8( _T1, _T0, 12 ); \
320 _T0 = _mm_slli_si128( _T0, 4 ); \
321\
322 _Q0 = _mm_xor_si128( _Q0, _T0 ); \
323 _Q1 = _mm_xor_si128( _Q1, _T1 ); \
324\
325 _T0 = _mm_slli_epi32( _Q0, 31 ); \
326 _T1 = _mm_slli_epi32( _Q0, 30 ); \
327 _T2 = _mm_slli_epi32( _Q0, 25 ); \
328 _T0 = _mm_xor_si128( _T0, _T1 ); \
329 _T0 = _mm_xor_si128( _T0, _T2 ); \
330\
331 _T1 = _mm_slli_si128( _T0, 12 ); \
332\
333 _T2 = _mm_xor_si128( _Q0, _T1 ); \
334\
335 res = _mm_xor_si128( _Q1, _T2 ); \
336 _T1 = _mm_srli_si128( _T0, 4 ); \
337 res = _mm_xor_si128( res, _T1 ); \
338\
339 _T0 = _mm_srli_epi32( _T2, 1 ); \
340 _T1 = _mm_srli_epi32( _T2, 2 ); \
341 _T2 = _mm_srli_epi32( _T2, 7 ); \
342\
343 _T1 = _mm_xor_si128( _T0, _T1 ); \
344 res = _mm_xor_si128( res, _T2 ); \
345 res = _mm_xor_si128( res, _T1 ); \
346};
347
348#endif // CPU_X86 || CPU_AMD64
349
350#if SYMCRYPT_CPU_ARM64
351
352#define SYMCRYPT_GHASH_PMULL_HPOWERS 32
353
354#define GHASH_H_POWER( ghashTable, ind ) ( (ghashTable)[ SYMCRYPT_GHASH_PMULL_HPOWERS - (ind)].n128 )
355#define GHASH_Hx_POWER( ghashTable, ind ) ( (ghashTable)[2*SYMCRYPT_GHASH_PMULL_HPOWERS - (ind)].n128 )
356
357#if SYMCRYPT_MS_VC
358#ifndef vshl_n_u64
359#define vshl_n_u64(src1, src2) neon_shlis64(src1, src2)
360#endif
361#endif
362//
363// CLMUL_4 multiplies two operands into three intermediate results using 4 pmull instructions
364//
365#define CLMUL_4( opA, opB, resl, resm, resh ) \
366{ \
367 __n128 _tmp; \
368 resl = vmullq_p64( opA, opB ); \
369 _tmp = vextq_u8( opA, opA, 8 ); \
370 resm = veorq_u8( vmullq_p64( opB, _tmp ), vmull_high_p64( opB, _tmp ) );\
371 resh = vmull_high_p64( opA, opB ); \
372};
373
374//
375// CLMUL_3 multiplies two operands into three intermediate results using 3 pmull instructions.
376// The second operand has a pre-computed difference of the two halves.
377// This uses Karatsuba, but we delay xorring the high and low piece into the middle piece.
378//
379#define CLMUL_3( opA, opB, opBx, resl, resm, resh ) \
380{ \
381 __n128 _tmpA; \
382 resl = vmullq_p64( opA, opB ); \
383 resh = vmull_high_p64( opA, opB ); \
384 _tmpA = veorq_u8( opA, vextq_u8( opA, opA, 8 ) ); \
385 resm = vmullq_p64( _tmpA, opBx ); \
386};
387//
388// CLMUL_X_3 is as CLMUL_3 only it takes precomputed differences of both multiplicands
389//
390#define CLMUL_X_3( opA, opAx, opB, opBx, resl, resm, resh ) \
391{ \
392 resl = vmullq_p64( opA, opB ); \
393 resh = vmull_high_p64( opA, opB ); \
394 resm = vmullq_p64( opAx, opBx ); \
395};
396
397//
398// Post-process the CLMUL_3 result to be compatible with the CLMUL_4
399//
400#define CLMUL_3_POST( resl, resm, resh ) \
401 resm = veorq_u8( resm, veorq_u8( resl, resh ) );
402
403//
404// Multiply-accumulate using CLMUL_4
405//
406#define CLMUL_ACC_4( opA, opB, resl, resm, resh ) \
407{\
408 __n128 _tmpl, _tmpm, _tmph;\
409 CLMUL_4( opA, opB, _tmpl, _tmpm, _tmph );\
410 resl = veorq_u8( resl, _tmpl ); \
411 resm = veorq_u8( resm, _tmpm ); \
412 resh = veorq_u8( resh, _tmph ); \
413};
414
415//
416// Multiply-accumulate two operands into 3 accumulators.
417// Takes the multiplicands and the pre-computed differences of the two halves of both multiplicands.
418//
419#define CLMUL_ACCX_3( opA, opAx, opB, opBx, resl, resm, resh ) \
420{\
421 __n128 _tmpl, _tmpm, _tmph;\
422 CLMUL_X_3( opA, opAx, opB, opBx, _tmpl, _tmpm, _tmph ); \
423 resl = veorq_u8( resl, _tmpl ); \
424 resm = veorq_u8( resm, _tmpm ); \
425 resh = veorq_u8( resh, _tmph ); \
426};
427
428
429//
430// Convert the 3 intermediate results to a 256-bit result,
431// and do the modulo reduction.
432// See the large comment above on how this is done.
433//
434#define MODREDUCE( vMultiplicationConstant, rl, rm, rh, res ) \
435{\
436 __n128 _T0, _T1; \
437\
438 /* multiply rl by constant which is (rev(0x87) << 1) - we'll eor the lost high bit in manually */ \
439 _T0 = vmull_p64( vget_low_p64(rl), vMultiplicationConstant ); \
440\
441 /* we want the high 64b of rl to align with the low 64b of rm, because we haven't merged rm into rl and rh */ \
442 /* we want the low 64b of rl to align with the high 64b of rm, because we lost the high bit in the previous pmull */ \
443 rl = vextq_u8( rl, rl, 8 ); \
444\
445 rm = veorq_u8( rm, _T0 ); \
446 rm = veorq_u8( rm, rl ); \
447\
448 /* almost same again to fold rm into rh, but bit 63 needs no more multiplication and the result ultimately needs shifting left by 1 */ \
449 /* pre-shift bottom of rm left by 1 and accumulate the result when the other parts are aligned */ \
450 _T0 = vmull_p64( vshl_n_u64(vget_low_p64(rm), 1), vMultiplicationConstant ); \
451\
452 rm = vextq_u8( rm, rm, 8 ); \
453 res = veorq_u8( rh, rm ); \
454\
455 /* rotate res left by 1 and accumulate the aligned parts */ \
456 _T1 = vshlq_n_u32( res, 1 ); \
457 res = vshrq_n_u32( res, 31 ); \
458\
459 _T0 = veorq_u8( _T0, _T1 ); \
460 res = vextq_u8( res, res, 12 ); \
461\
462 res = veorq_u8( res, _T0 ); \
463};
464
465#define REVERSE_BYTES( _in, _out )\
466{\
467 __n128 _t;\
468 _t = vrev64q_u8( _in ); \
469 _out = vextq_u8( _t, _t, 8 ); \
470}
471
472#endif // CPU_ARM64