1
2
3
4
5
6
7 package gcm
8
9 import (
10 "crypto/internal/fips140/aes"
11 "crypto/internal/fips140/subtle"
12 "crypto/internal/fips140deps/byteorder"
13 "crypto/internal/fips140deps/cpu"
14 "crypto/internal/impl"
15 )
16
17
18
19
20
21
22
23
24 var useGHASH = cpu.S390XHasAES && cpu.S390XHasAESCTR && cpu.S390XHasGHASH
25 var useGCM = useGHASH && cpu.S390XHasAESGCM
26
27 func init() {
28 impl.Register("gcm", "CPACF/KIMD", &useGHASH)
29 impl.Register("gcm", "CPACF/KMA", &useGCM)
30 }
31
32 func checkGenericIsExpected() {
33 if useGHASH || useGCM {
34 panic("gcm: internal error: using generic implementation despite hardware support")
35 }
36 }
37
38
39 func gcmLengths(len0, len1 uint64) [16]byte {
40 v := [16]byte{}
41 byteorder.BEPutUint64(v[0:], len0)
42 byteorder.BEPutUint64(v[8:], len1)
43 return v
44 }
45
46
47 type gcmHashKey [16]byte
48
49 type gcmPlatformData struct {
50 hashKey gcmHashKey
51 }
52
53 func initGCM(g *GCM) {
54 if !useGCM && !useGHASH {
55 return
56 }
57
58 aes.EncryptBlockInternal(&g.cipher, g.hashKey[:], g.hashKey[:])
59 }
60
61
62
63
64
65
66 func ghashAsm(key *gcmHashKey, hash *[16]byte, data []byte)
67
68
69
70 func paddedGHASH(hashKey *gcmHashKey, hash *[16]byte, data []byte) {
71 siz := len(data) &^ 0xf
72 if siz > 0 {
73 ghashAsm(hashKey, hash, data[:siz])
74 data = data[siz:]
75 }
76 if len(data) > 0 {
77 var s [16]byte
78 copy(s[:], data)
79 ghashAsm(hashKey, hash, s[:])
80 }
81 }
82
83
84
85
86
87
88
89
90
91
92
93
94 func cryptBlocksGCM(fn int, key, dst, src, buf []byte, cnt *[gcmBlockSize]byte)
95
96
97
98
99
100 func counterCrypt(g *GCM, dst, src []byte, cnt *[gcmBlockSize]byte) {
101
102
103
104 var ctrbuf, srcbuf [2048]byte
105 for len(src) >= 16 {
106 siz := len(src)
107 if len(src) > len(ctrbuf) {
108 siz = len(ctrbuf)
109 }
110 siz &^= 0xf
111 copy(srcbuf[:], src[:siz])
112 cryptBlocksGCM(aes.BlockFunction(&g.cipher), aes.BlockKey(&g.cipher), dst[:siz], srcbuf[:siz], ctrbuf[:], cnt)
113 src = src[siz:]
114 dst = dst[siz:]
115 }
116 if len(src) > 0 {
117 var x [16]byte
118 aes.EncryptBlockInternal(&g.cipher, x[:], cnt[:])
119 for i := range src {
120 dst[i] = src[i] ^ x[i]
121 }
122 gcmInc32(cnt)
123 }
124 }
125
126
127
128 func deriveCounter(H *gcmHashKey, counter *[gcmBlockSize]byte, nonce []byte) {
129 if len(nonce) == gcmStandardNonceSize {
130 copy(counter[:], nonce)
131 counter[gcmBlockSize-1] = 1
132 } else {
133 var hash [16]byte
134 paddedGHASH(H, &hash, nonce)
135 lens := gcmLengths(0, uint64(len(nonce))*8)
136 paddedGHASH(H, &hash, lens[:])
137 copy(counter[:], hash[:])
138 }
139 }
140
141
142
143 func gcmAuth(out []byte, H *gcmHashKey, tagMask *[gcmBlockSize]byte, ciphertext, additionalData []byte) {
144 var hash [16]byte
145 paddedGHASH(H, &hash, additionalData)
146 paddedGHASH(H, &hash, ciphertext)
147 lens := gcmLengths(uint64(len(additionalData))*8, uint64(len(ciphertext))*8)
148 paddedGHASH(H, &hash, lens[:])
149
150 copy(out, hash[:])
151 for i := range out {
152 out[i] ^= tagMask[i]
153 }
154 }
155
156 func seal(out []byte, g *GCM, nonce, plaintext, data []byte) {
157 switch {
158 case useGCM:
159 sealKMA(out, g, nonce, plaintext, data)
160 case useGHASH:
161 sealAsm(out, g, nonce, plaintext, data)
162 default:
163 sealGeneric(out, g, nonce, plaintext, data)
164 }
165 }
166
167 func sealAsm(out []byte, g *GCM, nonce, plaintext, additionalData []byte) {
168 var counter, tagMask [gcmBlockSize]byte
169 deriveCounter(&g.hashKey, &counter, nonce)
170 counterCrypt(g, tagMask[:], tagMask[:], &counter)
171
172 counterCrypt(g, out, plaintext, &counter)
173
174 var tag [gcmTagSize]byte
175 gcmAuth(tag[:], &g.hashKey, &tagMask, out[:len(plaintext)], additionalData)
176 copy(out[len(plaintext):], tag[:])
177 }
178
179 func open(out []byte, g *GCM, nonce, ciphertext, data []byte) error {
180 switch {
181 case useGCM:
182 return openKMA(out, g, nonce, ciphertext, data)
183 case useGHASH:
184 return openAsm(out, g, nonce, ciphertext, data)
185 default:
186 return openGeneric(out, g, nonce, ciphertext, data)
187 }
188 }
189
190 func openAsm(out []byte, g *GCM, nonce, ciphertext, additionalData []byte) error {
191 var counter, tagMask [gcmBlockSize]byte
192 deriveCounter(&g.hashKey, &counter, nonce)
193 counterCrypt(g, tagMask[:], tagMask[:], &counter)
194
195 tag := ciphertext[len(ciphertext)-g.tagSize:]
196 ciphertext = ciphertext[:len(ciphertext)-g.tagSize]
197
198 var expectedTag [gcmTagSize]byte
199 gcmAuth(expectedTag[:], &g.hashKey, &tagMask, ciphertext, additionalData)
200 if subtle.ConstantTimeCompare(expectedTag[:g.tagSize], tag) != 1 {
201 return errOpen
202 }
203
204 counterCrypt(g, out, ciphertext, &counter)
205
206 return nil
207 }
208
209
210 const (
211 kmaHS = 1 << 10
212 kmaLAAD = 1 << 9
213 kmaLPC = 1 << 8
214 kmaDecrypt = 1 << 7
215 )
216
217
218
219
220
221
222
223 func kmaGCM(fn int, key, dst, src, aad []byte, tag *[16]byte, cnt *[gcmBlockSize]byte)
224
225 func sealKMA(out []byte, g *GCM, nonce, plaintext, data []byte) {
226 var counter [gcmBlockSize]byte
227 deriveCounter(&g.hashKey, &counter, nonce)
228 fc := aes.BlockFunction(&g.cipher) | kmaLAAD | kmaLPC
229
230 var tag [gcmTagSize]byte
231 kmaGCM(fc, aes.BlockKey(&g.cipher), out[:len(plaintext)], plaintext, data, &tag, &counter)
232 copy(out[len(plaintext):], tag[:])
233 }
234
235 func openKMA(out []byte, g *GCM, nonce, ciphertext, data []byte) error {
236 tag := ciphertext[len(ciphertext)-g.tagSize:]
237 ciphertext = ciphertext[:len(ciphertext)-g.tagSize]
238
239 var counter [gcmBlockSize]byte
240 deriveCounter(&g.hashKey, &counter, nonce)
241 fc := aes.BlockFunction(&g.cipher) | kmaLAAD | kmaLPC | kmaDecrypt
242
243 var expectedTag [gcmTagSize]byte
244 kmaGCM(fc, aes.BlockKey(&g.cipher), out[:len(ciphertext)], ciphertext, data, &expectedTag, &counter)
245
246 if subtle.ConstantTimeCompare(expectedTag[:g.tagSize], tag) != 1 {
247 return errOpen
248 }
249
250 return nil
251 }
252
View as plain text