1
2
3
4
5
6
7
8
9
10
11 package cipher_test
12
13 import (
14 "bytes"
15 "crypto/aes"
16 "crypto/cipher"
17 "crypto/internal/boring"
18 "crypto/internal/cryptotest"
19 fipsaes "crypto/internal/fips140/aes"
20 "encoding/binary"
21 "encoding/hex"
22 "fmt"
23 "math/rand"
24 "sort"
25 "strings"
26 "testing"
27 )
28
29 var commonCounter = []byte{0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff}
30
31 var ctrAESTests = []struct {
32 name string
33 key []byte
34 iv []byte
35 in []byte
36 out []byte
37 }{
38
39 {
40 "CTR-AES128",
41 commonKey128,
42 commonCounter,
43 commonInput,
44 []byte{
45 0x87, 0x4d, 0x61, 0x91, 0xb6, 0x20, 0xe3, 0x26, 0x1b, 0xef, 0x68, 0x64, 0x99, 0x0d, 0xb6, 0xce,
46 0x98, 0x06, 0xf6, 0x6b, 0x79, 0x70, 0xfd, 0xff, 0x86, 0x17, 0x18, 0x7b, 0xb9, 0xff, 0xfd, 0xff,
47 0x5a, 0xe4, 0xdf, 0x3e, 0xdb, 0xd5, 0xd3, 0x5e, 0x5b, 0x4f, 0x09, 0x02, 0x0d, 0xb0, 0x3e, 0xab,
48 0x1e, 0x03, 0x1d, 0xda, 0x2f, 0xbe, 0x03, 0xd1, 0x79, 0x21, 0x70, 0xa0, 0xf3, 0x00, 0x9c, 0xee,
49 },
50 },
51 {
52 "CTR-AES192",
53 commonKey192,
54 commonCounter,
55 commonInput,
56 []byte{
57 0x1a, 0xbc, 0x93, 0x24, 0x17, 0x52, 0x1c, 0xa2, 0x4f, 0x2b, 0x04, 0x59, 0xfe, 0x7e, 0x6e, 0x0b,
58 0x09, 0x03, 0x39, 0xec, 0x0a, 0xa6, 0xfa, 0xef, 0xd5, 0xcc, 0xc2, 0xc6, 0xf4, 0xce, 0x8e, 0x94,
59 0x1e, 0x36, 0xb2, 0x6b, 0xd1, 0xeb, 0xc6, 0x70, 0xd1, 0xbd, 0x1d, 0x66, 0x56, 0x20, 0xab, 0xf7,
60 0x4f, 0x78, 0xa7, 0xf6, 0xd2, 0x98, 0x09, 0x58, 0x5a, 0x97, 0xda, 0xec, 0x58, 0xc6, 0xb0, 0x50,
61 },
62 },
63 {
64 "CTR-AES256",
65 commonKey256,
66 commonCounter,
67 commonInput,
68 []byte{
69 0x60, 0x1e, 0xc3, 0x13, 0x77, 0x57, 0x89, 0xa5, 0xb7, 0xa7, 0xf5, 0x04, 0xbb, 0xf3, 0xd2, 0x28,
70 0xf4, 0x43, 0xe3, 0xca, 0x4d, 0x62, 0xb5, 0x9a, 0xca, 0x84, 0xe9, 0x90, 0xca, 0xca, 0xf5, 0xc5,
71 0x2b, 0x09, 0x30, 0xda, 0xa2, 0x3d, 0xe9, 0x4c, 0xe8, 0x70, 0x17, 0xba, 0x2d, 0x84, 0x98, 0x8d,
72 0xdf, 0xc9, 0xc5, 0x8d, 0xb6, 0x7a, 0xad, 0xa6, 0x13, 0xc2, 0xdd, 0x08, 0x45, 0x79, 0x41, 0xa6,
73 },
74 },
75 }
76
77 func TestCTR_AES(t *testing.T) {
78 cryptotest.TestAllImplementations(t, "aes", testCTR_AES)
79 }
80
81 func testCTR_AES(t *testing.T) {
82 for _, tt := range ctrAESTests {
83 test := tt.name
84
85 c, err := aes.NewCipher(tt.key)
86 if err != nil {
87 t.Errorf("%s: NewCipher(%d bytes) = %s", test, len(tt.key), err)
88 continue
89 }
90
91 for j := 0; j <= 5; j += 5 {
92 in := tt.in[0 : len(tt.in)-j]
93 ctr := cipher.NewCTR(c, tt.iv)
94 encrypted := make([]byte, len(in))
95 ctr.XORKeyStream(encrypted, in)
96 if out := tt.out[:len(in)]; !bytes.Equal(out, encrypted) {
97 t.Errorf("%s/%d: CTR\ninpt %x\nhave %x\nwant %x", test, len(in), in, encrypted, out)
98 }
99 }
100
101 for j := 0; j <= 7; j += 7 {
102 in := tt.out[0 : len(tt.out)-j]
103 ctr := cipher.NewCTR(c, tt.iv)
104 plain := make([]byte, len(in))
105 ctr.XORKeyStream(plain, in)
106 if out := tt.in[:len(in)]; !bytes.Equal(out, plain) {
107 t.Errorf("%s/%d: CTRReader\nhave %x\nwant %x", test, len(out), plain, out)
108 }
109 }
110
111 if t.Failed() {
112 break
113 }
114 }
115 }
116
117 func makeTestingCiphers(aesBlock cipher.Block, iv []byte) (genericCtr, multiblockCtr cipher.Stream) {
118 return cipher.NewCTR(wrap(aesBlock), iv), cipher.NewCTR(aesBlock, iv)
119 }
120
121
122
123
124 func TestCTR_AES_blocks8FastPathMatchesGeneric(t *testing.T) {
125 key := make([]byte, aes.BlockSize)
126 block, err := aes.NewCipher(key)
127 if err != nil {
128 t.Fatal(err)
129 }
130 if _, ok := block.(*fipsaes.Block); !ok {
131 t.Skip("requires crypto/internal/fips140/aes")
132 }
133
134 keystream := make([]byte, 8*aes.BlockSize)
135
136 testCases := []struct {
137 name string
138 hi uint64
139 lo uint64
140 }{
141 {"Zero", 0, 0},
142 {"NearOverflowMinus7", 1, ^uint64(0) - 7},
143 {"NearOverflowMinus6", 2, ^uint64(0) - 6},
144 {"Overflow", 0, ^uint64(0)},
145 }
146
147 for _, tc := range testCases {
148 t.Run(tc.name, func(t *testing.T) {
149 var iv [aes.BlockSize]byte
150 binary.BigEndian.PutUint64(iv[0:8], tc.hi)
151 binary.BigEndian.PutUint64(iv[8:], tc.lo)
152
153 generic, multiblock := makeTestingCiphers(block, iv[:])
154
155 genericOut := make([]byte, len(keystream))
156 multiblockOut := make([]byte, len(keystream))
157
158 generic.XORKeyStream(genericOut, keystream)
159 multiblock.XORKeyStream(multiblockOut, keystream)
160
161 if !bytes.Equal(multiblockOut, genericOut) {
162 t.Fatalf("mismatch for iv %#x:%#x\n"+
163 "asm keystream: %x\n"+
164 "gen keystream: %x\n"+
165 "asm counters: %x\n"+
166 "gen counters: %x",
167 tc.hi, tc.lo, multiblockOut, genericOut,
168 extractCounters(block, multiblockOut),
169 extractCounters(block, genericOut))
170 }
171 })
172 }
173 }
174
175 func randBytes(t *testing.T, r *rand.Rand, count int) []byte {
176 t.Helper()
177 buf := make([]byte, count)
178 n, err := r.Read(buf)
179 if err != nil {
180 t.Fatal(err)
181 }
182 if n != count {
183 t.Fatal("short read from Rand")
184 }
185 return buf
186 }
187
188 const aesBlockSize = 16
189
190 type ctrAble interface {
191 NewCTR(iv []byte) cipher.Stream
192 }
193
194
195
196
197 func TestCTR_AES_multiblock_random_IV(t *testing.T) {
198 r := rand.New(rand.NewSource(54321))
199 iv := randBytes(t, r, aesBlockSize)
200 const Size = 100
201
202 for _, keySize := range []int{16, 24, 32} {
203 t.Run(fmt.Sprintf("keySize=%d", keySize), func(t *testing.T) {
204 key := randBytes(t, r, keySize)
205 aesBlock, err := aes.NewCipher(key)
206 if err != nil {
207 t.Fatal(err)
208 }
209 genericCtr, _ := makeTestingCiphers(aesBlock, iv)
210
211 plaintext := randBytes(t, r, Size)
212
213
214 genericCiphertext := make([]byte, len(plaintext))
215 genericCtr.XORKeyStream(genericCiphertext, plaintext)
216
217
218
219
220 for part1 := 0; part1 <= Size; part1++ {
221 t.Run(fmt.Sprintf("part1=%d", part1), func(t *testing.T) {
222 for part2 := 0; part2 <= Size-part1; part2++ {
223 t.Run(fmt.Sprintf("part2=%d", part2), func(t *testing.T) {
224 _, multiblockCtr := makeTestingCiphers(aesBlock, iv)
225 multiblockCiphertext := make([]byte, len(plaintext))
226 multiblockCtr.XORKeyStream(multiblockCiphertext[:part1], plaintext[:part1])
227 multiblockCtr.XORKeyStream(multiblockCiphertext[part1:part1+part2], plaintext[part1:part1+part2])
228 multiblockCtr.XORKeyStream(multiblockCiphertext[part1+part2:], plaintext[part1+part2:])
229 if !bytes.Equal(genericCiphertext, multiblockCiphertext) {
230 t.Fatal("multiblock CTR's output does not match generic CTR's output")
231 }
232 })
233 }
234 })
235 }
236 })
237 }
238 }
239
240 func parseHex(str string) []byte {
241 b, err := hex.DecodeString(strings.ReplaceAll(str, " ", ""))
242 if err != nil {
243 panic(err)
244 }
245 return b
246 }
247
248
249
250
251 func TestCTR_AES_multiblock_overflow_IV(t *testing.T) {
252 r := rand.New(rand.NewSource(987654))
253
254 const Size = 4096
255 plaintext := randBytes(t, r, Size)
256
257 ivs := [][]byte{
258 parseHex("00 00 00 00 00 00 00 00 FF FF FF FF FF FF FF FF"),
259 parseHex("FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF"),
260 parseHex("FF FF FF FF FF FF FF FF 00 00 00 00 00 00 00 00"),
261 parseHex("FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF fe"),
262 parseHex("00 00 00 00 00 00 00 00 FF FF FF FF FF FF FF fe"),
263 parseHex("FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 00"),
264 parseHex("00 00 00 00 00 00 00 01 FF FF FF FF FF FF FF 00"),
265 parseHex("00 00 00 00 00 00 00 01 FF FF FF FF FF FF FF FF"),
266 parseHex("00 00 00 00 00 00 00 01 FF FF FF FF FF FF FF fe"),
267 parseHex("00 00 00 00 00 00 00 01 FF FF FF FF FF FF FF 00"),
268 }
269
270 for _, keySize := range []int{16, 24, 32} {
271 t.Run(fmt.Sprintf("keySize=%d", keySize), func(t *testing.T) {
272 for _, iv := range ivs {
273 key := randBytes(t, r, keySize)
274 aesBlock, err := aes.NewCipher(key)
275 if err != nil {
276 t.Fatal(err)
277 }
278
279 t.Run(fmt.Sprintf("iv=%s", hex.EncodeToString(iv)), func(t *testing.T) {
280 for _, offset := range []int{0, 1, 16, 1024} {
281 t.Run(fmt.Sprintf("offset=%d", offset), func(t *testing.T) {
282 genericCtr, multiblockCtr := makeTestingCiphers(aesBlock, iv)
283
284
285 genericCiphertext := make([]byte, Size)
286 genericCtr.XORKeyStream(genericCiphertext, plaintext)
287
288 multiblockCiphertext := make([]byte, Size)
289 multiblockCtr.XORKeyStream(multiblockCiphertext, plaintext[:offset])
290 multiblockCtr.XORKeyStream(multiblockCiphertext[offset:], plaintext[offset:])
291 if !bytes.Equal(genericCiphertext, multiblockCiphertext) {
292 t.Fatal("multiblock CTR's output does not match generic CTR's output")
293 }
294 })
295 }
296 })
297 }
298 })
299 }
300 }
301
302
303 func TestCTR_AES_multiblock_XORKeyStreamAt(t *testing.T) {
304 if boring.Enabled {
305 t.Skip("XORKeyStreamAt is not available in boring mode")
306 }
307
308 r := rand.New(rand.NewSource(12345))
309 const Size = 32 * 1024 * 1024
310 plaintext := randBytes(t, r, Size)
311
312 for _, keySize := range []int{16, 24, 32} {
313 t.Run(fmt.Sprintf("keySize=%d", keySize), func(t *testing.T) {
314 key := randBytes(t, r, keySize)
315 iv := randBytes(t, r, aesBlockSize)
316
317 aesBlock, err := aes.NewCipher(key)
318 if err != nil {
319 t.Fatal(err)
320 }
321 genericCtr, _ := makeTestingCiphers(aesBlock, iv)
322 ctrAt := fipsaes.NewCTR(aesBlock.(*fipsaes.Block), iv)
323
324
325 genericCiphertext := make([]byte, Size)
326 genericCtr.XORKeyStream(genericCiphertext, plaintext)
327
328 multiblockCiphertext := make([]byte, Size)
329
330 const N = 1000
331 boundaries := make([]int, 0, N+2)
332 for i := 0; i < N; i++ {
333 boundaries = append(boundaries, r.Intn(Size))
334 }
335 boundaries = append(boundaries, 0)
336 boundaries = append(boundaries, Size)
337 sort.Ints(boundaries)
338
339 for _, i := range r.Perm(N + 1) {
340 begin := boundaries[i]
341 end := boundaries[i+1]
342 ctrAt.XORKeyStreamAt(
343 multiblockCiphertext[begin:end],
344 plaintext[begin:end],
345 uint64(begin),
346 )
347 }
348
349 if !bytes.Equal(genericCiphertext, multiblockCiphertext) {
350 t.Fatal("multiblock CTR's output does not match generic CTR's output")
351 }
352 })
353 }
354 }
355
356 func extractCounters(block cipher.Block, keystream []byte) []byte {
357 blockSize := block.BlockSize()
358 res := make([]byte, len(keystream))
359 for i := 0; i < len(keystream); i += blockSize {
360 block.Decrypt(res[i:i+blockSize], keystream[i:i+blockSize])
361 }
362 return res
363 }
364
View as plain text