1
2
3
4
5 package cryptotest
6
7 import (
8 "bytes"
9 "crypto/cipher"
10 "crypto/subtle"
11 "fmt"
12 "strings"
13 "testing"
14 )
15
16
17 var (
18 bufLens = []int{0, 1, 3, 4, 8, 10, 15, 16, 20, 32, 50, 4096, 5000}
19 bufCap = 10000
20 )
21
22
23
24
25
26 type MakeStream func() cipher.Stream
27
28
29
30 func TestStream(t *testing.T, ms MakeStream) {
31
32 t.Run("XORSemantics", func(t *testing.T) {
33 if strings.Contains(t.Name(), "TestCFBStream") {
34
35
36 t.Skip("CFB implements cipher.Stream but does not follow XOR semantics")
37 }
38
39
40 t.Run("Roundtrip", func(t *testing.T) {
41
42 for _, length := range bufLens {
43 t.Run(fmt.Sprintf("BuffLength=%d", length), func(t *testing.T) {
44 rng := newRandReader(t)
45
46 plaintext := make([]byte, length)
47 rng.Read(plaintext)
48
49 ciphertext := make([]byte, length)
50 decrypted := make([]byte, length)
51
52 ms().XORKeyStream(ciphertext, plaintext)
53 ms().XORKeyStream(decrypted, ciphertext)
54 if !bytes.Equal(decrypted, plaintext) {
55 t.Errorf("plaintext is different after an encrypt/decrypt cycle; got %s, want %s", truncateHex(decrypted), truncateHex(plaintext))
56 }
57 })
58 }
59 })
60
61
62
63 t.Run("DirectXOR", func(t *testing.T) {
64
65 for _, length := range bufLens {
66 t.Run(fmt.Sprintf("BuffLength=%d", length), func(t *testing.T) {
67 rng := newRandReader(t)
68
69 plaintext := make([]byte, length)
70 rng.Read(plaintext)
71
72
73 stream, directXOR := make([]byte, length), make([]byte, length)
74 ms().XORKeyStream(stream, stream)
75
76 subtle.XORBytes(directXOR, stream, plaintext)
77
78
79 ciphertext := make([]byte, length)
80 ms().XORKeyStream(ciphertext, plaintext)
81 if !bytes.Equal(ciphertext, directXOR) {
82 t.Errorf("xor semantics were not preserved; got %s, want %s", truncateHex(ciphertext), truncateHex(directXOR))
83 }
84 })
85 }
86 })
87 })
88
89 t.Run("AlterInput", func(t *testing.T) {
90 rng := newRandReader(t)
91 src, dst, before := make([]byte, bufCap), make([]byte, bufCap), make([]byte, bufCap)
92 rng.Read(src)
93
94 for _, length := range bufLens {
95
96 t.Run(fmt.Sprintf("BuffLength=%d", length), func(t *testing.T) {
97 copy(before, src)
98
99 ms().XORKeyStream(dst[:length], src[:length])
100 if !bytes.Equal(src, before) {
101 t.Errorf("XORKeyStream modified src; got %s, want %s", truncateHex(src), truncateHex(before))
102 }
103 })
104 }
105 })
106
107 t.Run("Aliasing", func(t *testing.T) {
108 rng := newRandReader(t)
109
110 buff, expectedOutput := make([]byte, bufCap), make([]byte, bufCap)
111
112 for _, length := range bufLens {
113
114 rng.Read(buff)
115 ms().XORKeyStream(expectedOutput[:length], buff[:length])
116
117
118
119 ms().XORKeyStream(buff[:length], buff[:length])
120 if !bytes.Equal(buff[:length], expectedOutput[:length]) {
121 t.Errorf("block cipher produced different output when dst = src; got %x, want %x", buff[:length], expectedOutput[:length])
122 }
123 }
124 })
125
126 t.Run("OutOfBoundsWrite", func(t *testing.T) {
127 rng := newRandReader(t)
128
129 plaintext := make([]byte, bufCap)
130 rng.Read(plaintext)
131 ciphertext := make([]byte, bufCap)
132
133 for _, length := range bufLens {
134 copy(ciphertext, plaintext)
135
136 t.Run(fmt.Sprintf("BuffLength=%d", length), func(t *testing.T) {
137 mustPanic(t, "output smaller than input", func() { ms().XORKeyStream(ciphertext[:length], plaintext) })
138
139 if !bytes.Equal(ciphertext[length:], plaintext[length:]) {
140 t.Errorf("XORKeyStream did out of bounds write; got %s, want %s", truncateHex(ciphertext[length:]), truncateHex(plaintext[length:]))
141 }
142 })
143 }
144 })
145
146 t.Run("BufferOverlap", func(t *testing.T) {
147 rng := newRandReader(t)
148
149 buff := make([]byte, bufCap)
150 rng.Read(buff)
151
152 for _, length := range bufLens {
153 if length == 0 || length == 1 {
154 continue
155 }
156
157 t.Run(fmt.Sprintf("BuffLength=%d", length), func(t *testing.T) {
158
159 src := buff[:length]
160 dst := buff[1 : length+1]
161 mustPanic(t, "invalid buffer overlap", func() { ms().XORKeyStream(dst, src) })
162
163
164 src = buff[:length]
165 dst = buff[length-1 : 2*length-1]
166 mustPanic(t, "invalid buffer overlap", func() { ms().XORKeyStream(dst, src) })
167
168
169 src = buff[length-1 : 2*length-1]
170 dst = buff[:length]
171 mustPanic(t, "invalid buffer overlap", func() { ms().XORKeyStream(dst, src) })
172 })
173 }
174 })
175
176 t.Run("KeepState", func(t *testing.T) {
177 rng := newRandReader(t)
178
179 plaintext := make([]byte, bufCap)
180 rng.Read(plaintext)
181 ciphertext := make([]byte, bufCap)
182
183
184 ms().XORKeyStream(ciphertext, plaintext)
185
186 for _, step := range bufLens {
187 if step == 0 {
188 continue
189 }
190 stepMsg := fmt.Sprintf("step %d: ", step)
191
192 dst := make([]byte, bufCap)
193
194
195 stream := ms()
196 i := 0
197 for i+step < len(plaintext) {
198 stream.XORKeyStream(dst[i:], plaintext[i:i+step])
199 i += step
200 }
201 stream.XORKeyStream(dst[i:], plaintext[i:])
202
203 if !bytes.Equal(dst, ciphertext) {
204 t.Errorf(stepMsg+"successive XORKeyStream calls returned a different result than a single one; got %s, want %s", truncateHex(dst), truncateHex(ciphertext))
205 }
206 }
207 })
208 }
209
210
211
212
213
214 func TestStreamFromBlock(t *testing.T, block cipher.Block, blockMode func(b cipher.Block, iv []byte) cipher.Stream) {
215
216 t.Run("WrongIVLen", func(t *testing.T) {
217 t.Skip("see Issue 68377")
218
219 rng := newRandReader(t)
220 iv := make([]byte, block.BlockSize()+1)
221 rng.Read(iv)
222 mustPanic(t, "IV length must equal block size", func() { blockMode(block, iv) })
223 })
224
225 t.Run("BlockModeStream", func(t *testing.T) {
226 rng := newRandReader(t)
227 iv := make([]byte, block.BlockSize())
228 rng.Read(iv)
229
230 TestStream(t, func() cipher.Stream { return blockMode(block, iv) })
231 })
232 }
233
234 func truncateHex(b []byte) string {
235 numVals := 50
236
237 if len(b) <= numVals {
238 return fmt.Sprintf("%x", b)
239 }
240 return fmt.Sprintf("%x...", b[:numVals])
241 }
242
View as plain text