5
5
package cipher
6
6
7
7
import (
8
- "crypto/internal/alias"
8
+ "crypto/internal/fips140/aes"
9
+ "crypto/internal/fips140/aes/gcm"
10
+ "crypto/internal/fips140/alias"
11
+ "crypto/internal/fips140only"
9
12
"crypto/subtle"
10
13
"errors"
11
14
"internal/byteorder"
@@ -83,7 +86,10 @@ type gcm struct {
83
86
// An exception is when the underlying [Block] was created by aes.NewCipher
84
87
// on systems with hardware support for AES. See the [crypto/aes] package documentation for details.
85
88
func NewGCM (cipher Block ) (AEAD , error ) {
86
- return newGCMWithNonceAndTagSize (cipher , gcmStandardNonceSize , gcmTagSize )
89
+ if fips140only .Enabled {
90
+ return nil , errors .New ("crypto/cipher: use of GCM with arbitrary IVs is not allowed in FIPS 140-only mode, use NewGCMWithRandomNonce" )
91
+ }
92
+ return newGCM (cipher , gcmStandardNonceSize , gcmTagSize )
87
93
}
88
94
89
95
// NewGCMWithNonceSize returns the given 128-bit, block cipher wrapped in Galois
@@ -94,7 +100,10 @@ func NewGCM(cipher Block) (AEAD, error) {
94
100
// cryptosystem that uses non-standard nonce lengths. All other users should use
95
101
// [NewGCM], which is faster and more resistant to misuse.
96
102
func NewGCMWithNonceSize (cipher Block , size int ) (AEAD , error ) {
97
- return newGCMWithNonceAndTagSize (cipher , size , gcmTagSize )
103
+ if fips140only .Enabled {
104
+ return nil , errors .New ("crypto/cipher: use of GCM with arbitrary IVs is not allowed in FIPS 140-only mode, use NewGCMWithRandomNonce" )
105
+ }
106
+ return newGCM (cipher , size , gcmTagSize )
98
107
}
99
108
100
109
// NewGCMWithTagSize returns the given 128-bit, block cipher wrapped in Galois
@@ -106,10 +115,154 @@ func NewGCMWithNonceSize(cipher Block, size int) (AEAD, error) {
106
115
// cryptosystem that uses non-standard tag lengths. All other users should use
107
116
// [NewGCM], which is more resistant to misuse.
108
117
func NewGCMWithTagSize (cipher Block , tagSize int ) (AEAD , error ) {
109
- return newGCMWithNonceAndTagSize (cipher , gcmStandardNonceSize , tagSize )
118
+ if fips140only .Enabled {
119
+ return nil , errors .New ("crypto/cipher: use of GCM with arbitrary IVs is not allowed in FIPS 140-only mode, use NewGCMWithRandomNonce" )
120
+ }
121
+ return newGCM (cipher , gcmStandardNonceSize , tagSize )
122
+ }
123
+
124
+ func newGCM (cipher Block , nonceSize , tagSize int ) (AEAD , error ) {
125
+ c , ok := cipher .(* aes.Block )
126
+ if ! ok {
127
+ if fips140only .Enabled {
128
+ return nil , errors .New ("crypto/cipher: use of GCM with non-AES ciphers is not allowed in FIPS 140-only mode" )
129
+ }
130
+ return newGCMFallback (cipher , nonceSize , tagSize )
131
+ }
132
+ // We don't return gcm.New directly, because it would always return a non-nil
133
+ // AEAD interface value with type *gcm.GCM even if the *gcm.GCM is nil.
134
+ g , err := gcm .New (c , nonceSize , tagSize )
135
+ if err != nil {
136
+ return nil , err
137
+ }
138
+ return g , nil
139
+ }
140
+
141
+ // NewGCMWithRandomNonce returns the given cipher wrapped in Galois Counter
142
+ // Mode, with randomly-generated nonces. The cipher must have been created by
143
+ // [aes.NewCipher].
144
+ //
145
+ // It generates a random 96-bit nonce, which is prepended to the ciphertext by Seal,
146
+ // and is extracted from the ciphertext by Open. The NonceSize of the AEAD is zero,
147
+ // while the Overhead is 28 bytes (the combination of nonce size and tag size).
148
+ //
149
+ // A given key MUST NOT be used to encrypt more than 2^32 messages, to limit the
150
+ // risk of a random nonce collision to negligible levels.
151
+ func NewGCMWithRandomNonce (cipher Block ) (AEAD , error ) {
152
+ c , ok := cipher .(* aes.Block )
153
+ if ! ok {
154
+ return nil , errors .New ("cipher: NewGCMWithRandomNonce requires aes.Block" )
155
+ }
156
+ g , err := gcm .New (c , gcmStandardNonceSize , gcmTagSize )
157
+ if err != nil {
158
+ return nil , err
159
+ }
160
+ return gcmWithRandomNonce {g }, nil
161
+ }
162
+
163
+ type gcmWithRandomNonce struct {
164
+ * gcm.GCM
165
+ }
166
+
167
+ func (g gcmWithRandomNonce ) NonceSize () int {
168
+ return 0
169
+ }
170
+
171
+ func (g gcmWithRandomNonce ) Overhead () int {
172
+ return gcmStandardNonceSize + gcmTagSize
173
+ }
174
+
175
+ func (g gcmWithRandomNonce ) Seal (dst , nonce , plaintext , additionalData []byte ) []byte {
176
+ if len (nonce ) != 0 {
177
+ panic ("crypto/cipher: non-empty nonce passed to GCMWithRandomNonce" )
178
+ }
179
+
180
+ ret , out := sliceForAppend (dst , gcmStandardNonceSize + len (plaintext )+ gcmTagSize )
181
+ if alias .InexactOverlap (out , plaintext ) {
182
+ panic ("crypto/cipher: invalid buffer overlap of output and input" )
183
+ }
184
+ if alias .AnyOverlap (out , additionalData ) {
185
+ panic ("crypto/cipher: invalid buffer overlap of output and additional data" )
186
+ }
187
+ nonce = out [:gcmStandardNonceSize ]
188
+ ciphertext := out [gcmStandardNonceSize :]
189
+
190
+ // The AEAD interface allows using plaintext[:0] or ciphertext[:0] as dst.
191
+ //
192
+ // This is kind of a problem when trying to prepend or trim a nonce, because the
193
+ // actual AES-GCTR blocks end up overlapping but not exactly.
194
+ //
195
+ // In Open, we write the output *before* the input, so unless we do something
196
+ // weird like working through a chunk of block backwards, it works out.
197
+ //
198
+ // In Seal, we could work through the input backwards or intentionally load
199
+ // ahead before writing.
200
+ //
201
+ // However, the crypto/internal/fips140/aes/gcm APIs also check for exact overlap,
202
+ // so for now we just do a memmove if we detect overlap.
203
+ //
204
+ // ┌───────────────────────────┬ ─ ─
205
+ // │PPPPPPPPPPPPPPPPPPPPPPPPPPP│ │
206
+ // └▽─────────────────────────▲┴ ─ ─
207
+ // ╲ Seal ╲
208
+ // ╲ Open ╲
209
+ // ┌───▼─────────────────────────△──┐
210
+ // │NN|CCCCCCCCCCCCCCCCCCCCCCCCCCC|T│
211
+ // └────────────────────────────────┘
212
+ //
213
+ if alias .AnyOverlap (out , plaintext ) {
214
+ copy (ciphertext , plaintext )
215
+ plaintext = ciphertext [:len (plaintext )]
216
+ }
217
+
218
+ gcm .SealWithRandomNonce (g .GCM , nonce , ciphertext , plaintext , additionalData )
219
+ return ret
220
+ }
221
+
222
+ func (g gcmWithRandomNonce ) Open (dst , nonce , ciphertext , additionalData []byte ) ([]byte , error ) {
223
+ if len (nonce ) != 0 {
224
+ panic ("crypto/cipher: non-empty nonce passed to GCMWithRandomNonce" )
225
+ }
226
+ if len (ciphertext ) < gcmStandardNonceSize + gcmTagSize {
227
+ return nil , errOpen
228
+ }
229
+
230
+ ret , out := sliceForAppend (dst , len (ciphertext )- gcmStandardNonceSize - gcmTagSize )
231
+ if alias .InexactOverlap (out , ciphertext ) {
232
+ panic ("crypto/cipher: invalid buffer overlap of output and input" )
233
+ }
234
+ if alias .AnyOverlap (out , additionalData ) {
235
+ panic ("crypto/cipher: invalid buffer overlap of output and additional data" )
236
+ }
237
+ // See the discussion in Seal. Note that if there is any overlap at this
238
+ // point, it's because out = ciphertext, so out must have enough capacity
239
+ // even if we sliced the tag off. Also note how [AEAD] specifies that "the
240
+ // contents of dst, up to its capacity, may be overwritten".
241
+ if alias .AnyOverlap (out , ciphertext ) {
242
+ nonce = make ([]byte , gcmStandardNonceSize )
243
+ copy (nonce , ciphertext )
244
+ copy (out [:len (ciphertext )], ciphertext [gcmStandardNonceSize :])
245
+ ciphertext = out [:len (ciphertext )- gcmStandardNonceSize ]
246
+ } else {
247
+ nonce = ciphertext [:gcmStandardNonceSize ]
248
+ ciphertext = ciphertext [gcmStandardNonceSize :]
249
+ }
250
+
251
+ _ , err := g .GCM .Open (out [:0 ], nonce , ciphertext , additionalData )
252
+ if err != nil {
253
+ return nil , err
254
+ }
255
+ return ret , nil
256
+ }
257
+
258
+ // gcmAble is an interface implemented by ciphers that have a specific optimized
259
+ // implementation of GCM. crypto/aes doesn't use this anymore, and we'd like to
260
+ // eventually remove it.
261
+ type gcmAble interface {
262
+ NewGCM (nonceSize , tagSize int ) (AEAD , error )
110
263
}
111
264
112
- func newGCMWithNonceAndTagSize (cipher Block , nonceSize , tagSize int ) (AEAD , error ) {
265
+ func newGCMFallback (cipher Block , nonceSize , tagSize int ) (AEAD , error ) {
113
266
if tagSize < gcmMinimumTagSize || tagSize > gcmBlockSize {
114
267
return nil , errors .New ("cipher: incorrect tag size given to GCM" )
115
268
}
0 commit comments