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
/*
21
GHASH GF(2^128) multiplication using PCLMULQDQ
22
23
The 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
26
Notation: We use the standard mathematical notation '+' for the addition in the field,
27
which corresponds to a xor of the bits.
28
29
Multiplication:
30
Given two field elements A and B (represented as 128-bit values),
31
we first compute the polynomial product
32
(C,D) := A * B
33
where C and D are also 128-bit values.
34
35
The PCLMULQDQ instruction performs a 64 x 64 -> 128 bit carryless multiplication.
36
To multiply 128-bit values we write A = (A1, A0) and B = (B1, B0) in two 64-bit halves.
37
38
The schoolbook multiplication is computed by
39
(C, D) = (A1 * B1)x^128 + (A1 * B0 + A0 * B1)x^64 + (A0 * B0)
40
This require four PCLMULQDQ instructions. The middle 128-bit result has to be shifted
41
left and right, and each half added to the upper and lower 128-bit result to get (C,D).
42
43
Alternatively, the middle 128-bit intermediate result be computed using Karatsuba:
44
(A1*B0 + A0*B1) = (A1 + A0) * (B1 + B0) + (A1*B1) + (A0*B0)
45
This requires only one PCLMULQDQ instruction to multiply (A1 + A0) by (B1 + B0)
46
as the other two products are already computed.
47
Whether this is faster depends on the relative speed of shift/xor verses PCLMULQDQ.
48
49
Both multiplication algorithms produce three 128-bit intermediate results (R1, Rmid, R0),
50
with the full result defined by R1 x^128 + Rmid x^64 + R0.
51
If we do Multiply-Accumulate then we can accumulate the three 128-bit intermediate results
52
directly. As there are no carries, there is no overflow, and the combining of the three
53
intermediate results into a 256-bit result can be shared amongst all multiplications.
54
55
56
Modulo reduction:
57
We use << and >> to denote shifts on 128-bit values.
58
The modulo reduction can now be done as follows:
59
given 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,
64
so in the next step the reduction, which computes the result R, is easy. The
65
expression T1 + (T1 << 1) + (T1 << 2) + (T1 << 7) is just T1 * x^128 reduced modulo p(x).
66
67
Let's first get rid of the polynomial arithmetic and write this completely using shifts on
68
128-bit values.
69
70
T0 := C + (C << 1) + (C << 2) + (C << 7)
71
T1 := (C >> 127) + (C >> 126) + (C >> 121)
72
R := D + T0 + T1 + (T1 << 1) + (T1 << 2) + (T1 << 7)
73
74
We can optimize this by rewriting the equations
75
76
T2 := T1 + C
77
= C + (C>>127) + (C>>126) + (C>>121)
78
R = 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
82
Thus
83
T2 = C + (C>>127) + (C>>126) + (C>>121)
84
R = D + T2 + (T2 << 1) + (T2 << 2) + (T2 << 7)
85
86
Gets the right result and uses only 6 shifts.
87
88
The SSE instruction set does not implement bit-shifts of 128-bit values. Instead, we will
89
use bit-shifts of the 32-bit subvalues, and byte shifts (shifts by a multiple of 8 bits)
90
on the full 128-bit values.
91
We use the <<<< and >>>> operators to denote shifts on 32-bit subwords.
92
93
We can now do the modulo reduction by
94
95
t1 := (C >> 127) = (C >>>> 31) >> 96
96
t2 := (C >> 126) = (C >>>> 30) >> 96
97
t3 := (C >> 121) = (C >>>> 25) >> 96
98
T2 = C + t1 + t2 + t3
99
100
left-shifts in the computation of R are a bit more involved as we have to move bits from
101
one subword to the next
102
103
u1 := (T2 << 1) = (T2 <<<< 1) + ((T2 >>>> 31) << 32)
104
u2 := (T2 << 2) = (T2 <<<< 2) + ((T2 >>>> 30) << 32)
105
u3 := (T2 << 7) = (T2 <<<< 7) + ((T2 >>>> 25) << 32)
106
R = D + T2 + u1 + u2 + u3
107
108
We can eliminate some common subexpressions. For any k we have
109
(T2 >>>> k) = ((C + r) >>>> k)
110
where r is a 7-bit value. If k>7 then this is equal to (C >>>> k). This means that
111
the value (T2 >>>> 31) is equal to (C >>>> 31) so we don't have to compute it again.
112
113
So we can rewrite our formulas as
114
t4 := (C >>>> 31)
115
t5 := (C >>>> 30)
116
t6 := (C >>>> 25)
117
ts = t4 + t5 + t6
118
T2 = C + (ts >> 96)
119
120
Note that ts = (C >>>> 31) + (C >>>> 30) + (C >>>> 25)
121
which is equal to (T2 >>>> 31) + (T2 >>>> 30) + (T2 >>>> 25)
122
123
R = D + T2 + u1 + u2 + u3
124
= D + T2 + (T2 <<<< 1) + (T2 <<<< 2) + (T2 <<<< 7) + (ts << 32)
125
126
All together, we can do the modulo reduction using the following formulas
127
128
ts := (C >>>> 31) + (C >>>> 30) + (C >>>> 25)
129
T2 := C + (ts >> 96)
130
R = D + T2 + (T2 <<<< 1) + (T2 <<<< 2) + (T2 <<<< 7) + (ts << 32)
131
132
Using a total of 16 operations. (6 subword shifts, 2 byte shifts, and 8 additions)
133
134
Reversed bit order:
135
There is one more complication. GHASH uses the bits in the reverse order from normal representation.
136
The bits b_0, b_1, ..., b_127 represent the polynomial b_0 + b_1 * x + ... + b_127 * x^127.
137
This means that the most significant bit in each byte is actually the least significant bit in the
138
polynomial.
139
140
SSE CPUs use the LSBFirst convention. This means that the bits b_0, b_1, ..., b_127 of the polynomial
141
end up at positions 7, 6, 5, ..., 1, 0, 15, 14, ..., 9, 8, 23, 22, ... of our XMM register.
142
This is obviously not a useful representation to do arithmetic in.
143
The first step is to BSWAP the value so that the bits appear in pure reverse order.
144
That is at least algebraically useful.
145
146
To compute the multiplication we use the fact that GF(2)[x] multiplication has no carries and
147
thus no preference for bit order. After the BSWAP we don't have the values A and B, but rather
148
rev(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
152
where the shift operator is on the 256-bit product.
153
154
The modulo reduction remains the same, except that we change all the shifts to be the other direction.
155
156
This 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
173
Future work:
174
It might be possible to construct a faster solution by merging the leftshift of (P1,P0)
175
with 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
sdk
lib
3rdparty
symcrypt
lib
ghash_definitions.h
Generated on Tue Oct 6 2026 06:20:06 for ReactOS by
1.9.6