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