@@ -67,6 +67,123 @@ func newGCM(cipher Block, nonceSize, tagSize int) (AEAD, error) {
67
67
return g , nil
68
68
}
69
69
70
+ // NewGCMWithRandomNonce returns the given cipher wrapped in Galois Counter
71
+ // Mode, with randomly-generated nonces. The cipher must have been created by
72
+ // [aes.NewCipher].
73
+ //
74
+ // It generates a random 96-bit nonce, which is prepended to the ciphertext by Seal,
75
+ // and is extracted from the ciphertext by Open. The NonceSize of the AEAD is zero,
76
+ // while the Overhead is 28 bytes (the combination of nonce size and tag size).
77
+ //
78
+ // A given key MUST NOT be used to encrypt more than 2^32 messages, to limit the
79
+ // risk of a random nonce collision to negligible levels.
80
+ func NewGCMWithRandomNonce (cipher Block ) (AEAD , error ) {
81
+ c , ok := cipher .(* aes.Block )
82
+ if ! ok {
83
+ return nil , errors .New ("cipher: NewGCMWithRandomNonce requires aes.Block" )
84
+ }
85
+ g , err := gcm .New (c , gcmStandardNonceSize , gcmTagSize )
86
+ if err != nil {
87
+ return nil , err
88
+ }
89
+ return gcmWithRandomNonce {g }, nil
90
+ }
91
+
92
+ type gcmWithRandomNonce struct {
93
+ * gcm.GCM
94
+ }
95
+
96
+ func (g gcmWithRandomNonce ) NonceSize () int {
97
+ return 0
98
+ }
99
+
100
+ func (g gcmWithRandomNonce ) Overhead () int {
101
+ return gcmStandardNonceSize + gcmTagSize
102
+ }
103
+
104
+ func (g gcmWithRandomNonce ) Seal (dst , nonce , plaintext , additionalData []byte ) []byte {
105
+ if len (nonce ) != 0 {
106
+ panic ("crypto/cipher: non-empty nonce passed to GCMWithRandomNonce" )
107
+ }
108
+
109
+ ret , out := sliceForAppend (dst , gcmStandardNonceSize + len (plaintext )+ gcmTagSize )
110
+ if alias .InexactOverlap (out , plaintext ) {
111
+ panic ("crypto/cipher: invalid buffer overlap of output and input" )
112
+ }
113
+ if alias .AnyOverlap (out , additionalData ) {
114
+ panic ("crypto/cipher: invalid buffer overlap of output and additional data" )
115
+ }
116
+ nonce = out [:gcmStandardNonceSize ]
117
+ ciphertext := out [gcmStandardNonceSize :]
118
+
119
+ // The AEAD interface allows using plaintext[:0] or ciphertext[:0] as dst.
120
+ //
121
+ // This is kind of a problem when trying to prepend or trim a nonce, because the
122
+ // actual AES-GCTR blocks end up overlapping but not exactly.
123
+ //
124
+ // In Open, we write the output *before* the input, so unless we do something
125
+ // weird like working through a chunk of block backwards, it works out.
126
+ //
127
+ // In Seal, we could work through the input backwards or intentionally load
128
+ // ahead before writing.
129
+ //
130
+ // However, the crypto/internal/fips/aes/gcm APIs also check for exact overlap,
131
+ // so for now we just do a memmove if we detect overlap.
132
+ //
133
+ // ┌───────────────────────────┬ ─ ─
134
+ // │PPPPPPPPPPPPPPPPPPPPPPPPPPP│ │
135
+ // └▽─────────────────────────▲┴ ─ ─
136
+ // ╲ Seal ╲
137
+ // ╲ Open ╲
138
+ // ┌───▼─────────────────────────△──┐
139
+ // │NN|CCCCCCCCCCCCCCCCCCCCCCCCCCC|T│
140
+ // └────────────────────────────────┘
141
+ //
142
+ if alias .AnyOverlap (out , plaintext ) {
143
+ copy (ciphertext , plaintext )
144
+ plaintext = ciphertext [:len (plaintext )]
145
+ }
146
+
147
+ gcm .SealWithRandomNonce (g .GCM , nonce , ciphertext , plaintext , additionalData )
148
+ return ret
149
+ }
150
+
151
+ func (g gcmWithRandomNonce ) Open (dst , nonce , ciphertext , additionalData []byte ) ([]byte , error ) {
152
+ if len (nonce ) != 0 {
153
+ panic ("crypto/cipher: non-empty nonce passed to GCMWithRandomNonce" )
154
+ }
155
+ if len (ciphertext ) < gcmStandardNonceSize + gcmTagSize {
156
+ return nil , errOpen
157
+ }
158
+
159
+ ret , out := sliceForAppend (dst , len (ciphertext )- gcmStandardNonceSize - gcmTagSize )
160
+ if alias .InexactOverlap (out , ciphertext ) {
161
+ panic ("crypto/cipher: invalid buffer overlap of output and input" )
162
+ }
163
+ if alias .AnyOverlap (out , additionalData ) {
164
+ panic ("crypto/cipher: invalid buffer overlap of output and additional data" )
165
+ }
166
+ // See the discussion in Seal. Note that if there is any overlap at this
167
+ // point, it's because out = ciphertext, so out must have enough capacity
168
+ // even if we sliced the tag off. Also note how [AEAD] specifies that "the
169
+ // contents of dst, up to its capacity, may be overwritten".
170
+ if alias .AnyOverlap (out , ciphertext ) {
171
+ nonce = make ([]byte , gcmStandardNonceSize )
172
+ copy (nonce , ciphertext )
173
+ copy (out [:len (ciphertext )], ciphertext [gcmStandardNonceSize :])
174
+ ciphertext = out [:len (ciphertext )- gcmStandardNonceSize ]
175
+ } else {
176
+ nonce = ciphertext [:gcmStandardNonceSize ]
177
+ ciphertext = ciphertext [gcmStandardNonceSize :]
178
+ }
179
+
180
+ _ , err := g .GCM .Open (out [:0 ], nonce , ciphertext , additionalData )
181
+ if err != nil {
182
+ return nil , err
183
+ }
184
+ return ret , nil
185
+ }
186
+
70
187
// gcmAble is an interface implemented by ciphers that have a specific optimized
71
188
// implementation of GCM. crypto/aes doesn't use this anymore, and we'd like to
72
189
// eventually remove it.
0 commit comments