1
2
3
4
5 package cryptotest
6
7 import (
8 "bytes"
9 "crypto/cipher"
10 "testing"
11 )
12
13
14
15 type MakeBlockMode func(b cipher.Block, iv []byte) cipher.BlockMode
16
17
18
19 func TestBlockMode(t *testing.T, block cipher.Block, makeEncrypter, makeDecrypter MakeBlockMode) {
20 rng := newRandReader(t)
21 iv := make([]byte, block.BlockSize())
22 rng.Read(iv)
23
24 testBlockModePair(t, block, makeEncrypter, makeDecrypter, iv)
25 }
26
27 func testBlockModePair(t *testing.T, b cipher.Block, enc, dec MakeBlockMode, iv []byte) {
28 t.Run("Encryption", func(t *testing.T) {
29 testBlockMode(t, enc, b, iv)
30 })
31
32 t.Run("Decryption", func(t *testing.T) {
33 testBlockMode(t, dec, b, iv)
34 })
35
36 t.Run("Roundtrip", func(t *testing.T) {
37 rng := newRandReader(t)
38
39 blockSize := enc(b, iv).BlockSize()
40 if decBlockSize := dec(b, iv).BlockSize(); decBlockSize != blockSize {
41 t.Errorf("decryption blocksize different than encryption's; got %d, want %d", decBlockSize, blockSize)
42 }
43
44 before, dst, after := make([]byte, blockSize*2), make([]byte, blockSize*2), make([]byte, blockSize*2)
45 rng.Read(before)
46
47 enc(b, iv).CryptBlocks(dst, before)
48 dec(b, iv).CryptBlocks(after, dst)
49 if !bytes.Equal(after, before) {
50 t.Errorf("plaintext is different after an encrypt/decrypt cycle; got %x, want %x", after, before)
51 }
52 })
53 }
54
55 func testBlockMode(t *testing.T, bm MakeBlockMode, b cipher.Block, iv []byte) {
56 blockSize := bm(b, iv).BlockSize()
57
58 t.Run("WrongIVLen", func(t *testing.T) {
59 iv := make([]byte, b.BlockSize()+1)
60 mustPanic(t, "IV length must equal block size", func() { bm(b, iv) })
61 })
62
63 t.Run("EmptyInput", func(t *testing.T) {
64 rng := newRandReader(t)
65
66 src, dst := make([]byte, blockSize), make([]byte, blockSize)
67 rng.Read(dst)
68 before := bytes.Clone(dst)
69
70 bm(b, iv).CryptBlocks(dst, src[:0])
71 if !bytes.Equal(dst, before) {
72 t.Errorf("CryptBlocks modified dst on empty input; got %x, want %x", dst, before)
73 }
74 })
75
76 t.Run("AlterInput", func(t *testing.T) {
77 rng := newRandReader(t)
78
79 src, dst, before := make([]byte, blockSize*2), make([]byte, blockSize*2), make([]byte, blockSize*2)
80
81 for _, length := range []int{0, blockSize, blockSize * 2} {
82 rng.Read(src)
83 copy(before, src)
84
85 bm(b, iv).CryptBlocks(dst[:length], src[:length])
86 if !bytes.Equal(src, before) {
87 t.Errorf("CryptBlocks modified src; got %x, want %x", src, before)
88 }
89 }
90 })
91
92 t.Run("Aliasing", func(t *testing.T) {
93 rng := newRandReader(t)
94
95 buff, expectedOutput := make([]byte, blockSize*2), make([]byte, blockSize*2)
96
97 for _, length := range []int{0, blockSize, blockSize * 2} {
98
99 rng.Read(buff)
100 bm(b, iv).CryptBlocks(expectedOutput[:length], buff[:length])
101
102
103
104 bm(b, iv).CryptBlocks(buff[:length], buff[:length])
105 if !bytes.Equal(buff[:length], expectedOutput[:length]) {
106 t.Errorf("block cipher produced different output when dst = src; got %x, want %x", buff[:length], expectedOutput[:length])
107 }
108 }
109 })
110
111 t.Run("OutOfBoundsWrite", func(t *testing.T) {
112 rng := newRandReader(t)
113
114 src := make([]byte, blockSize)
115 rng.Read(src)
116
117
118 buff := make([]byte, blockSize*3)
119 endOfPrefix, startOfSuffix := blockSize, blockSize*2
120 rng.Read(buff[:endOfPrefix])
121 rng.Read(buff[startOfSuffix:])
122 dst := buff[endOfPrefix:startOfSuffix]
123
124
125 initPrefix, initSuffix := make([]byte, blockSize), make([]byte, blockSize)
126 copy(initPrefix, buff[:endOfPrefix])
127 copy(initSuffix, buff[startOfSuffix:])
128
129
130
131 bm(b, iv).CryptBlocks(dst, src)
132 if !bytes.Equal(buff[startOfSuffix:], initSuffix) {
133 t.Errorf("block cipher did out of bounds write after end of dst slice; got %x, want %x", buff[startOfSuffix:], initSuffix)
134 }
135 if !bytes.Equal(buff[:endOfPrefix], initPrefix) {
136 t.Errorf("block cipher did out of bounds write before beginning of dst slice; got %x, want %x", buff[:endOfPrefix], initPrefix)
137 }
138
139
140
141 dst = buff[endOfPrefix:]
142 bm(b, iv).CryptBlocks(dst, src)
143 if !bytes.Equal(buff[startOfSuffix:], initSuffix) {
144 t.Errorf("CryptBlocks modified dst past len(src); got %x, want %x", buff[startOfSuffix:], initSuffix)
145 }
146
147
148 src = make([]byte, blockSize*3)
149 rng.Read(src)
150
151 mustPanic(t, "output smaller than input", func() {
152 bm(b, iv).CryptBlocks(dst, src)
153 })
154
155 if !bytes.Equal(buff[startOfSuffix:], initSuffix) {
156 t.Errorf("block cipher did out of bounds write after end of dst slice; got %x, want %x", buff[startOfSuffix:], initSuffix)
157 }
158 if !bytes.Equal(buff[:endOfPrefix], initPrefix) {
159 t.Errorf("block cipher did out of bounds write before beginning of dst slice; got %x, want %x", buff[:endOfPrefix], initPrefix)
160 }
161 })
162
163
164
165 t.Run("OutOfBoundsRead", func(t *testing.T) {
166 rng := newRandReader(t)
167
168 src := make([]byte, blockSize)
169 rng.Read(src)
170 expectedDst := make([]byte, blockSize)
171 bm(b, iv).CryptBlocks(expectedDst, src)
172
173
174 buff := make([]byte, blockSize*3)
175 endOfPrefix, startOfSuffix := blockSize, blockSize*2
176
177 copy(buff[endOfPrefix:startOfSuffix], src)
178 rng.Read(buff[:endOfPrefix])
179 rng.Read(buff[startOfSuffix:])
180
181 testDst := make([]byte, blockSize)
182 bm(b, iv).CryptBlocks(testDst, buff[endOfPrefix:startOfSuffix])
183
184 if !bytes.Equal(testDst, expectedDst) {
185 t.Errorf("CryptBlocks affected by data outside of src slice bounds; got %x, want %x", testDst, expectedDst)
186 }
187 })
188
189 t.Run("BufferOverlap", func(t *testing.T) {
190 rng := newRandReader(t)
191
192 buff := make([]byte, blockSize*2)
193 rng.Read(buff)
194
195
196 src := buff[:blockSize]
197 dst := buff[1 : blockSize+1]
198 mustPanic(t, "invalid buffer overlap", func() { bm(b, iv).CryptBlocks(dst, src) })
199
200
201 src = buff[:blockSize]
202 dst = buff[blockSize-1 : 2*blockSize-1]
203 mustPanic(t, "invalid buffer overlap", func() { bm(b, iv).CryptBlocks(dst, src) })
204
205
206 src = buff[blockSize-1 : 2*blockSize-1]
207 dst = buff[:blockSize]
208 mustPanic(t, "invalid buffer overlap", func() { bm(b, iv).CryptBlocks(dst, src) })
209 })
210
211
212 t.Run("PartialBlocks", func(t *testing.T) {
213
214 for _, srcSize := range []int{blockSize - 1, blockSize + 1, 2*blockSize - 1, 2*blockSize + 1} {
215 src := make([]byte, srcSize)
216 dst := make([]byte, 3*blockSize)
217 mustPanic(t, "input not full blocks", func() { bm(b, iv).CryptBlocks(dst, src) })
218 }
219 })
220
221 t.Run("KeepState", func(t *testing.T) {
222 rng := newRandReader(t)
223
224 src, serialDst, compositeDst := make([]byte, blockSize*4), make([]byte, blockSize*4), make([]byte, blockSize*4)
225 rng.Read(src)
226
227 length, block := 2*blockSize, bm(b, iv)
228 block.CryptBlocks(serialDst, src[:length])
229 block.CryptBlocks(serialDst[length:], src[length:])
230
231 bm(b, iv).CryptBlocks(compositeDst, src)
232
233 if !bytes.Equal(serialDst, compositeDst) {
234 t.Errorf("two successive CryptBlocks calls returned a different result than a single one; got %x, want %x", serialDst, compositeDst)
235 }
236 })
237 }
238
View as plain text