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