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 t.Run(fmt.Sprintf("keySize=%d", keySize), func(t *testing.T) {
149 key := randBytes(t, r, keySize)
150 aesBlock, err := aes.NewCipher(key)
151 if err != nil {
152 t.Fatal(err)
153 }
154 genericCtr, _ := makeTestingCiphers(aesBlock, iv)
155
156 plaintext := randBytes(t, r, Size)
157
158
159 genericCiphertext := make([]byte, len(plaintext))
160 genericCtr.XORKeyStream(genericCiphertext, plaintext)
161
162
163
164
165 for part1 := 0; part1 <= Size; part1++ {
166 t.Run(fmt.Sprintf("part1=%d", part1), func(t *testing.T) {
167 for part2 := 0; part2 <= Size-part1; part2++ {
168 t.Run(fmt.Sprintf("part2=%d", part2), func(t *testing.T) {
169 _, multiblockCtr := makeTestingCiphers(aesBlock, iv)
170 multiblockCiphertext := make([]byte, len(plaintext))
171 multiblockCtr.XORKeyStream(multiblockCiphertext[:part1], plaintext[:part1])
172 multiblockCtr.XORKeyStream(multiblockCiphertext[part1:part1+part2], plaintext[part1:part1+part2])
173 multiblockCtr.XORKeyStream(multiblockCiphertext[part1+part2:], plaintext[part1+part2:])
174 if !bytes.Equal(genericCiphertext, multiblockCiphertext) {
175 t.Fatal("multiblock CTR's output does not match generic CTR's output")
176 }
177 })
178 }
179 })
180 }
181 })
182 }
183 }
184
185 func parseHex(str string) []byte {
186 b, err := hex.DecodeString(strings.ReplaceAll(str, " ", ""))
187 if err != nil {
188 panic(err)
189 }
190 return b
191 }
192
193
194
195
196 func TestCTR_AES_multiblock_overflow_IV(t *testing.T) {
197 r := rand.New(rand.NewSource(987654))
198
199 const Size = 4096
200 plaintext := randBytes(t, r, Size)
201
202 ivs := [][]byte{
203 parseHex("00 00 00 00 00 00 00 00 FF FF FF FF FF FF FF FF"),
204 parseHex("FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF"),
205 parseHex("FF FF FF FF FF FF FF FF 00 00 00 00 00 00 00 00"),
206 parseHex("FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF fe"),
207 parseHex("00 00 00 00 00 00 00 00 FF FF FF FF FF FF FF fe"),
208 parseHex("FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 00"),
209 parseHex("00 00 00 00 00 00 00 01 FF FF FF FF FF FF FF 00"),
210 parseHex("00 00 00 00 00 00 00 01 FF FF FF FF FF FF FF FF"),
211 parseHex("00 00 00 00 00 00 00 01 FF FF FF FF FF FF FF fe"),
212 parseHex("00 00 00 00 00 00 00 01 FF FF FF FF FF FF FF 00"),
213 }
214
215 for _, keySize := range []int{16, 24, 32} {
216 t.Run(fmt.Sprintf("keySize=%d", keySize), func(t *testing.T) {
217 for _, iv := range ivs {
218 key := randBytes(t, r, keySize)
219 aesBlock, err := aes.NewCipher(key)
220 if err != nil {
221 t.Fatal(err)
222 }
223
224 t.Run(fmt.Sprintf("iv=%s", hex.EncodeToString(iv)), func(t *testing.T) {
225 for _, offset := range []int{0, 1, 16, 1024} {
226 t.Run(fmt.Sprintf("offset=%d", offset), func(t *testing.T) {
227 genericCtr, multiblockCtr := makeTestingCiphers(aesBlock, iv)
228
229
230 genericCiphertext := make([]byte, Size)
231 genericCtr.XORKeyStream(genericCiphertext, plaintext)
232
233 multiblockCiphertext := make([]byte, Size)
234 multiblockCtr.XORKeyStream(multiblockCiphertext, plaintext[:offset])
235 multiblockCtr.XORKeyStream(multiblockCiphertext[offset:], plaintext[offset:])
236 if !bytes.Equal(genericCiphertext, multiblockCiphertext) {
237 t.Fatal("multiblock CTR's output does not match generic CTR's output")
238 }
239 })
240 }
241 })
242 }
243 })
244 }
245 }
246
247
248 func TestCTR_AES_multiblock_XORKeyStreamAt(t *testing.T) {
249 if boring.Enabled {
250 t.Skip("XORKeyStreamAt is not available in boring mode")
251 }
252
253 r := rand.New(rand.NewSource(12345))
254 const Size = 32 * 1024 * 1024
255 plaintext := randBytes(t, r, Size)
256
257 for _, keySize := range []int{16, 24, 32} {
258 t.Run(fmt.Sprintf("keySize=%d", keySize), func(t *testing.T) {
259 key := randBytes(t, r, keySize)
260 iv := randBytes(t, r, aesBlockSize)
261
262 aesBlock, err := aes.NewCipher(key)
263 if err != nil {
264 t.Fatal(err)
265 }
266 genericCtr, _ := makeTestingCiphers(aesBlock, iv)
267 ctrAt := fipsaes.NewCTR(aesBlock.(*fipsaes.Block), iv)
268
269
270 genericCiphertext := make([]byte, Size)
271 genericCtr.XORKeyStream(genericCiphertext, plaintext)
272
273 multiblockCiphertext := make([]byte, Size)
274
275 const N = 1000
276 boundaries := make([]int, 0, N+2)
277 for i := 0; i < N; i++ {
278 boundaries = append(boundaries, r.Intn(Size))
279 }
280 boundaries = append(boundaries, 0)
281 boundaries = append(boundaries, Size)
282 sort.Ints(boundaries)
283
284 for _, i := range r.Perm(N + 1) {
285 begin := boundaries[i]
286 end := boundaries[i+1]
287 ctrAt.XORKeyStreamAt(
288 multiblockCiphertext[begin:end],
289 plaintext[begin:end],
290 uint64(begin),
291 )
292 }
293
294 if !bytes.Equal(genericCiphertext, multiblockCiphertext) {
295 t.Fatal("multiblock CTR's output does not match generic CTR's output")
296 }
297 })
298 }
299 }
300
View as plain text