1 // Copyright 2024 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4 5 package cipher
6 7 import (
8 "crypto/internal/fips140/aes"
9 "crypto/internal/fips140/aes/gcm"
10 "crypto/internal/fips140/alias"
11 "crypto/internal/fips140only"
12 "crypto/subtle"
13 "errors"
14 "internal/byteorder"
15 )
16 17 const (
18 gcmBlockSize = 16
19 gcmStandardNonceSize = 12
20 gcmTagSize = 16
21 gcmMinimumTagSize = 12 // NIST SP 800-38D recommends tags with 12 or more bytes.
22 )
23 24 // NewGCM returns the given 128-bit, block cipher wrapped in Galois Counter Mode
25 // with the standard nonce length.
26 //
27 // In general, the GHASH operation performed by this implementation of GCM is not constant-time.
28 // An exception is when the underlying [Block] was created by aes.NewCipher
29 // on systems with hardware support for AES. See the [crypto/aes] package documentation for details.
30 func NewGCM(cipher Block) (AEAD, error) {
31 if fips140only.Enabled {
32 return nil, errors.New("crypto/cipher: use of GCM with arbitrary IVs is not allowed in FIPS 140-only mode, use NewGCMWithRandomNonce")
33 }
34 return newGCM(cipher, gcmStandardNonceSize, gcmTagSize)
35 }
36 37 // NewGCMWithNonceSize returns the given 128-bit, block cipher wrapped in Galois
38 // Counter Mode, which accepts nonces of the given length. The length must not
39 // be zero.
40 //
41 // Only use this function if you require compatibility with an existing
42 // cryptosystem that uses non-standard nonce lengths. All other users should use
43 // [NewGCM], which is faster and more resistant to misuse.
44 func NewGCMWithNonceSize(cipher Block, size int) (AEAD, error) {
45 if fips140only.Enabled {
46 return nil, errors.New("crypto/cipher: use of GCM with arbitrary IVs is not allowed in FIPS 140-only mode, use NewGCMWithRandomNonce")
47 }
48 return newGCM(cipher, size, gcmTagSize)
49 }
50 51 // NewGCMWithTagSize returns the given 128-bit, block cipher wrapped in Galois
52 // Counter Mode, which generates tags with the given length.
53 //
54 // Tag sizes between 12 and 16 bytes are allowed.
55 //
56 // Only use this function if you require compatibility with an existing
57 // cryptosystem that uses non-standard tag lengths. All other users should use
58 // [NewGCM], which is more resistant to misuse.
59 func NewGCMWithTagSize(cipher Block, tagSize int) (AEAD, error) {
60 if fips140only.Enabled {
61 return nil, errors.New("crypto/cipher: use of GCM with arbitrary IVs is not allowed in FIPS 140-only mode, use NewGCMWithRandomNonce")
62 }
63 return newGCM(cipher, gcmStandardNonceSize, tagSize)
64 }
65 66 func newGCM(cipher Block, nonceSize, tagSize int) (AEAD, error) {
67 c, ok := cipher.(*aes.Block)
68 if !ok {
69 if fips140only.Enabled {
70 return nil, errors.New("crypto/cipher: use of GCM with non-AES ciphers is not allowed in FIPS 140-only mode")
71 }
72 return newGCMFallback(cipher, nonceSize, tagSize)
73 }
74 // We don't return gcm.New directly, because it would always return a non-nil
75 // AEAD interface value with type *gcm.GCM even if the *gcm.GCM is nil.
76 g, err := gcm.New(c, nonceSize, tagSize)
77 if err != nil {
78 return nil, err
79 }
80 return g, nil
81 }
82 83 // NewGCMWithRandomNonce returns the given cipher wrapped in Galois Counter
84 // Mode, with randomly-generated nonces. The cipher must have been created by
85 // [crypto/aes.NewCipher].
86 //
87 // It generates a random 96-bit nonce, which is prepended to the ciphertext by Seal,
88 // and is extracted from the ciphertext by Open. The NonceSize of the AEAD is zero,
89 // while the Overhead is 28 bytes (the combination of nonce size and tag size).
90 //
91 // A given key MUST NOT be used to encrypt more than 2^32 messages, to limit the
92 // risk of a random nonce collision to negligible levels.
93 func NewGCMWithRandomNonce(cipher Block) (AEAD, error) {
94 c, ok := cipher.(*aes.Block)
95 if !ok {
96 return nil, errors.New("cipher: NewGCMWithRandomNonce requires aes.Block")
97 }
98 g, err := gcm.New(c, gcmStandardNonceSize, gcmTagSize)
99 if err != nil {
100 return nil, err
101 }
102 return gcmWithRandomNonce{g}, nil
103 }
104 105 type gcmWithRandomNonce struct {
106 *gcm.GCM
107 }
108 109 func (g gcmWithRandomNonce) NonceSize() int {
110 return 0
111 }
112 113 func (g gcmWithRandomNonce) Overhead() int {
114 return gcmStandardNonceSize + gcmTagSize
115 }
116 117 func (g gcmWithRandomNonce) Seal(dst, nonce, plaintext, additionalData []byte) []byte {
118 if len(nonce) != 0 {
119 panic("crypto/cipher: non-empty nonce passed to GCMWithRandomNonce")
120 }
121 122 ret, out := sliceForAppend(dst, gcmStandardNonceSize+len(plaintext)+gcmTagSize)
123 if alias.InexactOverlap(out, plaintext) {
124 panic("crypto/cipher: invalid buffer overlap of output and input")
125 }
126 if alias.AnyOverlap(out, additionalData) {
127 panic("crypto/cipher: invalid buffer overlap of output and additional data")
128 }
129 nonce = out[:gcmStandardNonceSize]
130 ciphertext := out[gcmStandardNonceSize:]
131 132 // The AEAD interface allows using plaintext[:0] or ciphertext[:0] as dst.
133 //
134 // This is kind of a problem when trying to prepend or trim a nonce, because the
135 // actual AES-GCTR blocks end up overlapping but not exactly.
136 //
137 // In Open, we write the output *before* the input, so unless we do something
138 // weird like working through a chunk of block backwards, it works out.
139 //
140 // In Seal, we could work through the input backwards or intentionally load
141 // ahead before writing.
142 //
143 // However, the crypto/internal/fips140/aes/gcm APIs also check for exact overlap,
144 // so for now we just do a memmove if we detect overlap.
145 //
146 // ┌───────────────────────────┬ ─ ─
147 // │PPPPPPPPPPPPPPPPPPPPPPPPPPP│ │
148 // └▽─────────────────────────▲┴ ─ ─
149 // ╲ Seal ╲
150 // ╲ Open ╲
151 // ┌───▼─────────────────────────△──┐
152 // │NN|CCCCCCCCCCCCCCCCCCCCCCCCCCC|T│
153 // └────────────────────────────────┘
154 //
155 if alias.AnyOverlap(out, plaintext) {
156 copy(ciphertext, plaintext)
157 plaintext = ciphertext[:len(plaintext)]
158 }
159 160 gcm.SealWithRandomNonce(g.GCM, nonce, ciphertext, plaintext, additionalData)
161 return ret
162 }
163 164 func (g gcmWithRandomNonce) Open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) {
165 if len(nonce) != 0 {
166 panic("crypto/cipher: non-empty nonce passed to GCMWithRandomNonce")
167 }
168 if len(ciphertext) < gcmStandardNonceSize+gcmTagSize {
169 return nil, errOpen
170 }
171 172 ret, out := sliceForAppend(dst, len(ciphertext)-gcmStandardNonceSize-gcmTagSize)
173 if alias.InexactOverlap(out, ciphertext) {
174 panic("crypto/cipher: invalid buffer overlap of output and input")
175 }
176 if alias.AnyOverlap(out, additionalData) {
177 panic("crypto/cipher: invalid buffer overlap of output and additional data")
178 }
179 // See the discussion in Seal. Note that if there is any overlap at this
180 // point, it's because out = ciphertext, so out must have enough capacity
181 // even if we sliced the tag off. Also note how [AEAD] specifies that "the
182 // contents of dst, up to its capacity, may be overwritten".
183 if alias.AnyOverlap(out, ciphertext) {
184 nonce = []byte{:gcmStandardNonceSize}
185 copy(nonce, ciphertext)
186 copy(out[:len(ciphertext)], ciphertext[gcmStandardNonceSize:])
187 ciphertext = out[:len(ciphertext)-gcmStandardNonceSize]
188 } else {
189 nonce = ciphertext[:gcmStandardNonceSize]
190 ciphertext = ciphertext[gcmStandardNonceSize:]
191 }
192 193 _, err := g.GCM.Open(out[:0], nonce, ciphertext, additionalData)
194 if err != nil {
195 return nil, err
196 }
197 return ret, nil
198 }
199 200 // gcmAble is an interface implemented by ciphers that have a specific optimized
201 // implementation of GCM. crypto/aes doesn't use this anymore, and we'd like to
202 // eventually remove it.
203 type gcmAble interface {
204 NewGCM(nonceSize, tagSize int) (AEAD, error)
205 }
206 207 func newGCMFallback(cipher Block, nonceSize, tagSize int) (AEAD, error) {
208 if tagSize < gcmMinimumTagSize || tagSize > gcmBlockSize {
209 return nil, errors.New("cipher: incorrect tag size given to GCM")
210 }
211 if nonceSize <= 0 {
212 return nil, errors.New("cipher: the nonce can't have zero length")
213 }
214 if cipher, ok := cipher.(gcmAble); ok {
215 return cipher.NewGCM(nonceSize, tagSize)
216 }
217 if cipher.BlockSize() != gcmBlockSize {
218 return nil, errors.New("cipher: NewGCM requires 128-bit block cipher")
219 }
220 return &gcmFallback{cipher: cipher, nonceSize: nonceSize, tagSize: tagSize}, nil
221 }
222 223 // gcmFallback is only used for non-AES ciphers, which regrettably we
224 // theoretically support. It's a copy of the generic implementation from
225 // crypto/internal/fips140/aes/gcm/gcm_generic.go, refer to that file for more details.
226 type gcmFallback struct {
227 cipher Block
228 nonceSize int
229 tagSize int
230 }
231 232 func (g *gcmFallback) NonceSize() int {
233 return g.nonceSize
234 }
235 236 func (g *gcmFallback) Overhead() int {
237 return g.tagSize
238 }
239 240 func (g *gcmFallback) Seal(dst, nonce, plaintext, additionalData []byte) []byte {
241 if len(nonce) != g.nonceSize {
242 panic("crypto/cipher: incorrect nonce length given to GCM")
243 }
244 if g.nonceSize == 0 {
245 panic("crypto/cipher: incorrect GCM nonce size")
246 }
247 if uint64(len(plaintext)) > uint64((1<<32)-2)*gcmBlockSize {
248 panic("crypto/cipher: message too large for GCM")
249 }
250 251 ret, out := sliceForAppend(dst, len(plaintext)+g.tagSize)
252 if alias.InexactOverlap(out, plaintext) {
253 panic("crypto/cipher: invalid buffer overlap of output and input")
254 }
255 if alias.AnyOverlap(out, additionalData) {
256 panic("crypto/cipher: invalid buffer overlap of output and additional data")
257 }
258 259 var H, counter, tagMask [gcmBlockSize]byte
260 g.cipher.Encrypt(H[:], H[:])
261 deriveCounter(&H, &counter, nonce)
262 gcmCounterCryptGeneric(g.cipher, tagMask[:], tagMask[:], &counter)
263 264 gcmCounterCryptGeneric(g.cipher, out, plaintext, &counter)
265 266 var tag [gcmTagSize]byte
267 gcmAuth(tag[:], &H, &tagMask, out[:len(plaintext)], additionalData)
268 copy(out[len(plaintext):], tag[:])
269 270 return ret
271 }
272 273 var errOpen = errors.New("cipher: message authentication failed")
274 275 func (g *gcmFallback) Open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) {
276 if len(nonce) != g.nonceSize {
277 panic("crypto/cipher: incorrect nonce length given to GCM")
278 }
279 if g.tagSize < gcmMinimumTagSize {
280 panic("crypto/cipher: incorrect GCM tag size")
281 }
282 283 if len(ciphertext) < g.tagSize {
284 return nil, errOpen
285 }
286 if uint64(len(ciphertext)) > uint64((1<<32)-2)*gcmBlockSize+uint64(g.tagSize) {
287 return nil, errOpen
288 }
289 290 ret, out := sliceForAppend(dst, len(ciphertext)-g.tagSize)
291 if alias.InexactOverlap(out, ciphertext) {
292 panic("crypto/cipher: invalid buffer overlap of output and input")
293 }
294 if alias.AnyOverlap(out, additionalData) {
295 panic("crypto/cipher: invalid buffer overlap of output and additional data")
296 }
297 298 var H, counter, tagMask [gcmBlockSize]byte
299 g.cipher.Encrypt(H[:], H[:])
300 deriveCounter(&H, &counter, nonce)
301 gcmCounterCryptGeneric(g.cipher, tagMask[:], tagMask[:], &counter)
302 303 tag := ciphertext[len(ciphertext)-g.tagSize:]
304 ciphertext = ciphertext[:len(ciphertext)-g.tagSize]
305 306 var expectedTag [gcmTagSize]byte
307 gcmAuth(expectedTag[:], &H, &tagMask, ciphertext, additionalData)
308 if subtle.ConstantTimeCompare(expectedTag[:g.tagSize], tag) != 1 {
309 // We sometimes decrypt and authenticate concurrently, so we overwrite
310 // dst in the event of a tag mismatch. To be consistent across platforms
311 // and to avoid releasing unauthenticated plaintext, we clear the buffer
312 // in the event of an error.
313 clear(out)
314 return nil, errOpen
315 }
316 317 gcmCounterCryptGeneric(g.cipher, out, ciphertext, &counter)
318 319 return ret, nil
320 }
321 322 func deriveCounter(H, counter *[gcmBlockSize]byte, nonce []byte) {
323 if len(nonce) == gcmStandardNonceSize {
324 copy(counter[:], nonce)
325 counter[gcmBlockSize-1] = 1
326 } else {
327 lenBlock := []byte{:16}
328 byteorder.BEPutUint64(lenBlock[8:], uint64(len(nonce))*8)
329 J := gcm.GHASH(H, nonce, lenBlock)
330 copy(counter[:], J)
331 }
332 }
333 334 func gcmCounterCryptGeneric(b Block, out, src []byte, counter *[gcmBlockSize]byte) {
335 var mask [gcmBlockSize]byte
336 for len(src) >= gcmBlockSize {
337 b.Encrypt(mask[:], counter[:])
338 gcmInc32(counter)
339 340 subtle.XORBytes(out, src, mask[:])
341 out = out[gcmBlockSize:]
342 src = src[gcmBlockSize:]
343 }
344 if len(src) > 0 {
345 b.Encrypt(mask[:], counter[:])
346 gcmInc32(counter)
347 subtle.XORBytes(out, src, mask[:])
348 }
349 }
350 351 func gcmInc32(counterBlock *[gcmBlockSize]byte) {
352 ctr := counterBlock[len(counterBlock)-4:]
353 byteorder.BEPutUint32(ctr, byteorder.BEUint32(ctr)+1)
354 }
355 356 func gcmAuth(out []byte, H, tagMask *[gcmBlockSize]byte, ciphertext, additionalData []byte) {
357 lenBlock := []byte{:16}
358 byteorder.BEPutUint64(lenBlock[:8], uint64(len(additionalData))*8)
359 byteorder.BEPutUint64(lenBlock[8:], uint64(len(ciphertext))*8)
360 S := gcm.GHASH(H, additionalData, ciphertext, lenBlock)
361 subtle.XORBytes(out, S, tagMask[:])
362 }
363 364 // sliceForAppend takes a slice and a requested number of bytes. It returns a
365 // slice with the contents of the given slice followed by that many bytes and a
366 // second slice that aliases into it and contains only the extra bytes. If the
367 // original slice has sufficient capacity then no allocation is performed.
368 func sliceForAppend(in []byte, n int) (head, tail []byte) {
369 if total := len(in) + n; cap(in) >= total {
370 head = in[:total]
371 } else {
372 head = []byte{:total}
373 copy(head, in)
374 }
375 tail = head[len(in):]
376 return
377 }
378