1
2
3
4
5 package cipher
6
7 import (
8 subtleoverlap "crypto/internal/subtle"
9 "crypto/subtle"
10 "encoding/binary"
11 "errors"
12 )
13
14
15
16
17 type AEAD interface {
18
19
20 NonceSize() int
21
22
23
24 Overhead() int
25
26
27
28
29
30
31
32
33 Seal(dst, nonce, plaintext, additionalData []byte) []byte
34
35
36
37
38
39
40
41
42
43
44
45
46 Open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error)
47 }
48
49
50
51
52 type gcmAble interface {
53 NewGCM(nonceSize, tagSize int) (AEAD, error)
54 }
55
56
57
58
59
60
61
62
63 type gcmFieldElement struct {
64 low, high uint64
65 }
66
67
68
69 type gcm struct {
70 cipher Block
71 nonceSize int
72 tagSize int
73
74
75 productTable [16]gcmFieldElement
76 }
77
78
79
80
81
82
83
84 func NewGCM(cipher Block) (AEAD, error) {
85 return newGCMWithNonceAndTagSize(cipher, gcmStandardNonceSize, gcmTagSize)
86 }
87
88
89
90
91
92
93
94
95 func NewGCMWithNonceSize(cipher Block, size int) (AEAD, error) {
96 return newGCMWithNonceAndTagSize(cipher, size, gcmTagSize)
97 }
98
99
100
101
102
103
104
105
106
107 func NewGCMWithTagSize(cipher Block, tagSize int) (AEAD, error) {
108 return newGCMWithNonceAndTagSize(cipher, gcmStandardNonceSize, tagSize)
109 }
110
111 func newGCMWithNonceAndTagSize(cipher Block, nonceSize, tagSize int) (AEAD, error) {
112 if tagSize < gcmMinimumTagSize || tagSize > gcmBlockSize {
113 return nil, errors.New("cipher: incorrect tag size given to GCM")
114 }
115
116 if nonceSize <= 0 {
117 return nil, errors.New("cipher: the nonce can't have zero length, or the security of the key will be immediately compromised")
118 }
119
120 if cipher, ok := cipher.(gcmAble); ok {
121 return cipher.NewGCM(nonceSize, tagSize)
122 }
123
124 if cipher.BlockSize() != gcmBlockSize {
125 return nil, errors.New("cipher: NewGCM requires 128-bit block cipher")
126 }
127
128 var key [gcmBlockSize]byte
129 cipher.Encrypt(key[:], key[:])
130
131 g := &gcm{cipher: cipher, nonceSize: nonceSize, tagSize: tagSize}
132
133
134
135
136
137
138 x := gcmFieldElement{
139 binary.BigEndian.Uint64(key[:8]),
140 binary.BigEndian.Uint64(key[8:]),
141 }
142 g.productTable[reverseBits(1)] = x
143
144 for i := 2; i < 16; i += 2 {
145 g.productTable[reverseBits(i)] = gcmDouble(&g.productTable[reverseBits(i/2)])
146 g.productTable[reverseBits(i+1)] = gcmAdd(&g.productTable[reverseBits(i)], &x)
147 }
148
149 return g, nil
150 }
151
152 const (
153 gcmBlockSize = 16
154 gcmTagSize = 16
155 gcmMinimumTagSize = 12
156 gcmStandardNonceSize = 12
157 )
158
159 func (g *gcm) NonceSize() int {
160 return g.nonceSize
161 }
162
163 func (g *gcm) Overhead() int {
164 return g.tagSize
165 }
166
167 func (g *gcm) Seal(dst, nonce, plaintext, data []byte) []byte {
168 if len(nonce) != g.nonceSize {
169 panic("crypto/cipher: incorrect nonce length given to GCM")
170 }
171 if uint64(len(plaintext)) > ((1<<32)-2)*uint64(g.cipher.BlockSize()) {
172 panic("crypto/cipher: message too large for GCM")
173 }
174
175 ret, out := sliceForAppend(dst, len(plaintext)+g.tagSize)
176 if subtleoverlap.InexactOverlap(out, plaintext) {
177 panic("crypto/cipher: invalid buffer overlap")
178 }
179
180 var counter, tagMask [gcmBlockSize]byte
181 g.deriveCounter(&counter, nonce)
182
183 g.cipher.Encrypt(tagMask[:], counter[:])
184 gcmInc32(&counter)
185
186 g.counterCrypt(out, plaintext, &counter)
187
188 var tag [gcmTagSize]byte
189 g.auth(tag[:], out[:len(plaintext)], data, &tagMask)
190 copy(out[len(plaintext):], tag[:])
191
192 return ret
193 }
194
195 var errOpen = errors.New("cipher: message authentication failed")
196
197 func (g *gcm) Open(dst, nonce, ciphertext, data []byte) ([]byte, error) {
198 if len(nonce) != g.nonceSize {
199 panic("crypto/cipher: incorrect nonce length given to GCM")
200 }
201
202
203 if g.tagSize < gcmMinimumTagSize {
204 panic("crypto/cipher: incorrect GCM tag size")
205 }
206
207 if len(ciphertext) < g.tagSize {
208 return nil, errOpen
209 }
210 if uint64(len(ciphertext)) > ((1<<32)-2)*uint64(g.cipher.BlockSize())+uint64(g.tagSize) {
211 return nil, errOpen
212 }
213
214 tag := ciphertext[len(ciphertext)-g.tagSize:]
215 ciphertext = ciphertext[:len(ciphertext)-g.tagSize]
216
217 var counter, tagMask [gcmBlockSize]byte
218 g.deriveCounter(&counter, nonce)
219
220 g.cipher.Encrypt(tagMask[:], counter[:])
221 gcmInc32(&counter)
222
223 var expectedTag [gcmTagSize]byte
224 g.auth(expectedTag[:], ciphertext, data, &tagMask)
225
226 ret, out := sliceForAppend(dst, len(ciphertext))
227 if subtleoverlap.InexactOverlap(out, ciphertext) {
228 panic("crypto/cipher: invalid buffer overlap")
229 }
230
231 if subtle.ConstantTimeCompare(expectedTag[:g.tagSize], tag) != 1 {
232
233
234
235
236 for i := range out {
237 out[i] = 0
238 }
239 return nil, errOpen
240 }
241
242 g.counterCrypt(out, ciphertext, &counter)
243
244 return ret, nil
245 }
246
247
248 func reverseBits(i int) int {
249 i = ((i << 2) & 0xc) | ((i >> 2) & 0x3)
250 i = ((i << 1) & 0xa) | ((i >> 1) & 0x5)
251 return i
252 }
253
254
255 func gcmAdd(x, y *gcmFieldElement) gcmFieldElement {
256
257 return gcmFieldElement{x.low ^ y.low, x.high ^ y.high}
258 }
259
260
261 func gcmDouble(x *gcmFieldElement) (double gcmFieldElement) {
262 msbSet := x.high&1 == 1
263
264
265 double.high = x.high >> 1
266 double.high |= x.low << 63
267 double.low = x.low >> 1
268
269
270
271
272
273
274
275
276 if msbSet {
277 double.low ^= 0xe100000000000000
278 }
279
280 return
281 }
282
283 var gcmReductionTable = []uint16{
284 0x0000, 0x1c20, 0x3840, 0x2460, 0x7080, 0x6ca0, 0x48c0, 0x54e0,
285 0xe100, 0xfd20, 0xd940, 0xc560, 0x9180, 0x8da0, 0xa9c0, 0xb5e0,
286 }
287
288
289 func (g *gcm) mul(y *gcmFieldElement) {
290 var z gcmFieldElement
291
292 for i := 0; i < 2; i++ {
293 word := y.high
294 if i == 1 {
295 word = y.low
296 }
297
298
299
300 for j := 0; j < 64; j += 4 {
301 msw := z.high & 0xf
302 z.high >>= 4
303 z.high |= z.low << 60
304 z.low >>= 4
305 z.low ^= uint64(gcmReductionTable[msw]) << 48
306
307
308
309
310 t := &g.productTable[word&0xf]
311
312 z.low ^= t.low
313 z.high ^= t.high
314 word >>= 4
315 }
316 }
317
318 *y = z
319 }
320
321
322
323 func (g *gcm) updateBlocks(y *gcmFieldElement, blocks []byte) {
324 for len(blocks) > 0 {
325 y.low ^= binary.BigEndian.Uint64(blocks)
326 y.high ^= binary.BigEndian.Uint64(blocks[8:])
327 g.mul(y)
328 blocks = blocks[gcmBlockSize:]
329 }
330 }
331
332
333
334 func (g *gcm) update(y *gcmFieldElement, data []byte) {
335 fullBlocks := (len(data) >> 4) << 4
336 g.updateBlocks(y, data[:fullBlocks])
337
338 if len(data) != fullBlocks {
339 var partialBlock [gcmBlockSize]byte
340 copy(partialBlock[:], data[fullBlocks:])
341 g.updateBlocks(y, partialBlock[:])
342 }
343 }
344
345
346
347 func gcmInc32(counterBlock *[16]byte) {
348 ctr := counterBlock[len(counterBlock)-4:]
349 binary.BigEndian.PutUint32(ctr, binary.BigEndian.Uint32(ctr)+1)
350 }
351
352
353
354
355
356 func sliceForAppend(in []byte, n int) (head, tail []byte) {
357 if total := len(in) + n; cap(in) >= total {
358 head = in[:total]
359 } else {
360 head = make([]byte, total)
361 copy(head, in)
362 }
363 tail = head[len(in):]
364 return
365 }
366
367
368 func (g *gcm) counterCrypt(out, in []byte, counter *[gcmBlockSize]byte) {
369 var mask [gcmBlockSize]byte
370
371 for len(in) >= gcmBlockSize {
372 g.cipher.Encrypt(mask[:], counter[:])
373 gcmInc32(counter)
374
375 xorWords(out, in, mask[:])
376 out = out[gcmBlockSize:]
377 in = in[gcmBlockSize:]
378 }
379
380 if len(in) > 0 {
381 g.cipher.Encrypt(mask[:], counter[:])
382 gcmInc32(counter)
383 xorBytes(out, in, mask[:])
384 }
385 }
386
387
388
389
390 func (g *gcm) deriveCounter(counter *[gcmBlockSize]byte, nonce []byte) {
391
392
393
394
395
396
397 if len(nonce) == gcmStandardNonceSize {
398 copy(counter[:], nonce)
399 counter[gcmBlockSize-1] = 1
400 } else {
401 var y gcmFieldElement
402 g.update(&y, nonce)
403 y.high ^= uint64(len(nonce)) * 8
404 g.mul(&y)
405 binary.BigEndian.PutUint64(counter[:8], y.low)
406 binary.BigEndian.PutUint64(counter[8:], y.high)
407 }
408 }
409
410
411
412 func (g *gcm) auth(out, ciphertext, additionalData []byte, tagMask *[gcmTagSize]byte) {
413 var y gcmFieldElement
414 g.update(&y, additionalData)
415 g.update(&y, ciphertext)
416
417 y.low ^= uint64(len(additionalData)) * 8
418 y.high ^= uint64(len(ciphertext)) * 8
419
420 g.mul(&y)
421
422 binary.BigEndian.PutUint64(out, y.low)
423 binary.BigEndian.PutUint64(out[8:], y.high)
424
425 xorWords(out, out, tagMask[:])
426 }
427
View as plain text