1
2
3
4
5 package gcm
6
7 import (
8 "crypto/internal/fips140"
9 "crypto/internal/fips140deps/byteorder"
10 )
11
12
13
14
15
16
17
18
19
20 type gcmFieldElement struct {
21 low, high uint64
22 }
23
24
25
26
27 func GHASH(key *[16]byte, inputs ...[]byte) []byte {
28 fips140.RecordNonApproved()
29 var out [gcmBlockSize]byte
30 ghash(&out, key, inputs...)
31 return out[:]
32 }
33
34
35
36
37
38 func ghash(out, H *[gcmBlockSize]byte, inputs ...[]byte) {
39
40
41 var productTable [16]gcmFieldElement
42
43
44
45
46
47
48 x := gcmFieldElement{
49 byteorder.BEUint64(H[:8]),
50 byteorder.BEUint64(H[8:]),
51 }
52 productTable[reverseBits(1)] = x
53
54 for i := 2; i < 16; i += 2 {
55 productTable[reverseBits(i)] = ghashDouble(&productTable[reverseBits(i/2)])
56 productTable[reverseBits(i+1)] = ghashAdd(&productTable[reverseBits(i)], &x)
57 }
58
59 var y gcmFieldElement
60 for _, input := range inputs {
61 ghashUpdate(&productTable, &y, input)
62 }
63
64 byteorder.BEPutUint64(out[:], y.low)
65 byteorder.BEPutUint64(out[8:], y.high)
66 }
67
68
69 func reverseBits(i int) int {
70 i = ((i << 2) & 0xc) | ((i >> 2) & 0x3)
71 i = ((i << 1) & 0xa) | ((i >> 1) & 0x5)
72 return i
73 }
74
75
76 func ghashAdd(x, y *gcmFieldElement) gcmFieldElement {
77
78 return gcmFieldElement{x.low ^ y.low, x.high ^ y.high}
79 }
80
81
82 func ghashDouble(x *gcmFieldElement) (double gcmFieldElement) {
83 msbSet := x.high&1 == 1
84
85
86 double.high = x.high >> 1
87 double.high |= x.low << 63
88 double.low = x.low >> 1
89
90
91
92
93
94
95
96
97 if msbSet {
98 double.low ^= 0xe100000000000000
99 }
100
101 return
102 }
103
104 var ghashReductionTable = []uint16{
105 0x0000, 0x1c20, 0x3840, 0x2460, 0x7080, 0x6ca0, 0x48c0, 0x54e0,
106 0xe100, 0xfd20, 0xd940, 0xc560, 0x9180, 0x8da0, 0xa9c0, 0xb5e0,
107 }
108
109
110 func ghashMul(productTable *[16]gcmFieldElement, y *gcmFieldElement) {
111 var z gcmFieldElement
112
113 for i := 0; i < 2; i++ {
114 word := y.high
115 if i == 1 {
116 word = y.low
117 }
118
119
120
121 for j := 0; j < 64; j += 4 {
122 msw := z.high & 0xf
123 z.high >>= 4
124 z.high |= z.low << 60
125 z.low >>= 4
126 z.low ^= uint64(ghashReductionTable[msw]) << 48
127
128
129
130 t := productTable[word&0xf]
131
132 z.low ^= t.low
133 z.high ^= t.high
134 word >>= 4
135 }
136 }
137
138 *y = z
139 }
140
141
142
143 func updateBlocks(productTable *[16]gcmFieldElement, y *gcmFieldElement, blocks []byte) {
144 for len(blocks) > 0 {
145 y.low ^= byteorder.BEUint64(blocks)
146 y.high ^= byteorder.BEUint64(blocks[8:])
147 ghashMul(productTable, y)
148 blocks = blocks[gcmBlockSize:]
149 }
150 }
151
152
153
154 func ghashUpdate(productTable *[16]gcmFieldElement, y *gcmFieldElement, data []byte) {
155 fullBlocks := (len(data) >> 4) << 4
156 updateBlocks(productTable, y, data[:fullBlocks])
157
158 if len(data) != fullBlocks {
159 var partialBlock [gcmBlockSize]byte
160 copy(partialBlock[:], data[fullBlocks:])
161 updateBlocks(productTable, y, partialBlock[:])
162 }
163 }
164
View as plain text