1
2
3
4
5 package testhash
6
7 import (
8 "bytes"
9 "hash"
10 "io"
11 "math/rand"
12 "testing"
13 "time"
14 )
15
16 type MakeHash func() hash.Hash
17
18
19
20 func TestHash(t *testing.T, mh MakeHash) {
21 TestHashWithoutClone(t, mh)
22
23
24 t.Run("Clone", func(t *testing.T) {
25 h, ok := mh().(hash.Cloner)
26 if !ok {
27 t.Fatalf("%T does not implement hash.Cloner", mh)
28 }
29 h3, err := h.Clone()
30 if err != nil {
31 t.Fatalf("Clone failed: %v", err)
32 }
33 prefix := []byte("tmp")
34 writeToHash(t, h, prefix)
35 h2, err := h.Clone()
36 if err != nil {
37 t.Fatalf("Clone failed: %v", err)
38 }
39 prefixSum := h.Sum(nil)
40 if !bytes.Equal(prefixSum, h2.Sum(nil)) {
41 t.Fatalf("%T Clone results are inconsistent", h)
42 }
43 suffix := []byte("tmp2")
44 writeToHash(t, h, suffix)
45 writeToHash(t, h3, append(prefix, suffix...))
46 compositeSum := h3.Sum(nil)
47 if !bytes.Equal(h.Sum(nil), compositeSum) {
48 t.Fatalf("%T Clone results are inconsistent", h)
49 }
50 if !bytes.Equal(h2.Sum(nil), prefixSum) {
51 t.Fatalf("%T Clone results are inconsistent", h)
52 }
53 writeToHash(t, h2, suffix)
54 if !bytes.Equal(h.Sum(nil), compositeSum) {
55 t.Fatalf("%T Clone results are inconsistent", h)
56 }
57 if !bytes.Equal(h2.Sum(nil), compositeSum) {
58 t.Fatalf("%T Clone results are inconsistent", h)
59 }
60 })
61 }
62
63 func TestHashWithoutClone(t *testing.T, mh MakeHash) {
64
65 t.Run("SumAppend", func(t *testing.T) {
66 h := mh()
67 rng := newRandReader(t)
68
69 emptyBuff := []byte("")
70 shortBuff := []byte("a")
71 longBuff := make([]byte, h.BlockSize()+1)
72 rng.Read(longBuff)
73
74
75 prefixes := [][]byte{nil, emptyBuff, shortBuff, longBuff}
76
77
78 for _, prefix := range prefixes {
79 h.Reset()
80
81 sum := getSum(t, h, prefix)
82
83
84 if !bytes.Equal(sum[:len(prefix)], prefix) {
85 t.Errorf("Sum alters passed buffer instead of appending; got %x, want %x", sum[:len(prefix)], prefix)
86 }
87
88
89 if expectedSum := getSum(t, h, nil); !bytes.Equal(sum[len(prefix):], expectedSum) {
90 t.Errorf("Sum behavior affected by data in the input buffer; got %x, want %x", sum[len(prefix):], expectedSum)
91 }
92
93
94 if got, want := len(sum)-len(prefix), h.Size(); got != want {
95 t.Errorf("Sum appends number of bytes != Size; got %v , want %v", got, want)
96 }
97 }
98 })
99
100
101 t.Run("WriteWithoutError", func(t *testing.T) {
102 h := mh()
103 rng := newRandReader(t)
104
105 emptySlice := []byte("")
106 shortSlice := []byte("a")
107 longSlice := make([]byte, h.BlockSize()+1)
108 rng.Read(longSlice)
109
110
111 slices := [][]byte{emptySlice, shortSlice, longSlice}
112
113 for _, slice := range slices {
114 writeToHash(t, h, slice)
115 }
116 })
117
118 t.Run("ResetState", func(t *testing.T) {
119 h := mh()
120 rng := newRandReader(t)
121
122 emptySum := getSum(t, h, nil)
123
124
125 writeEx := make([]byte, h.BlockSize())
126 rng.Read(writeEx)
127 writeToHash(t, h, writeEx)
128 h.Reset()
129 resetSum := getSum(t, h, nil)
130
131 if !bytes.Equal(emptySum, resetSum) {
132 t.Errorf("Reset hash yields different Sum than new hash; got %x, want %x", emptySum, resetSum)
133 }
134 })
135
136
137 t.Run("OutOfBoundsRead", func(t *testing.T) {
138 h := mh()
139 blockSize := h.BlockSize()
140 rng := newRandReader(t)
141
142 msg := make([]byte, blockSize)
143 rng.Read(msg)
144 writeToHash(t, h, msg)
145 expectedDigest := getSum(t, h, nil)
146
147 h.Reset()
148
149
150 buff := make([]byte, blockSize*3)
151 endOfPrefix, startOfSuffix := blockSize, blockSize*2
152
153 copy(buff[endOfPrefix:startOfSuffix], msg)
154 rng.Read(buff[:endOfPrefix])
155 rng.Read(buff[startOfSuffix:])
156
157 writeToHash(t, h, buff[endOfPrefix:startOfSuffix])
158 testDigest := getSum(t, h, nil)
159
160 if !bytes.Equal(testDigest, expectedDigest) {
161 t.Errorf("Write affected by data outside of input slice bounds; got %x, want %x", testDigest, expectedDigest)
162 }
163 })
164
165
166 t.Run("StatefulWrite", func(t *testing.T) {
167 h := mh()
168 rng := newRandReader(t)
169
170 prefix, suffix := make([]byte, h.BlockSize()), make([]byte, h.BlockSize())
171 rng.Read(prefix)
172 rng.Read(suffix)
173
174
175 writeToHash(t, h, prefix)
176 writeToHash(t, h, suffix)
177 serialSum := getSum(t, h, nil)
178
179 h.Reset()
180
181
182 writeToHash(t, h, append(prefix, suffix...))
183 compositeSum := getSum(t, h, nil)
184
185
186 if !bytes.Equal(compositeSum, serialSum) {
187 t.Errorf("two successive Write calls resulted in a different Sum than a single one; got %x, want %x", compositeSum, serialSum)
188 }
189 })
190 }
191
192
193 func writeToHash(t *testing.T, h hash.Hash, p []byte) {
194 t.Helper()
195
196 before := make([]byte, len(p))
197 copy(before, p)
198
199 n, err := h.Write(p)
200 if err != nil || n != len(p) {
201 t.Errorf("Write returned error; got (%v, %v), want (nil, %v)", err, n, len(p))
202 }
203
204 if !bytes.Equal(p, before) {
205 t.Errorf("Write modified input slice; got %x, want %x", p, before)
206 }
207 }
208
209
210 func getSum(t *testing.T, h hash.Hash, buff []byte) []byte {
211 t.Helper()
212
213 testBuff := make([]byte, len(buff))
214 copy(testBuff, buff)
215
216 sum := h.Sum(buff)
217 testSum := h.Sum(testBuff)
218
219
220 if !bytes.Equal(sum, testSum) {
221 t.Errorf("successive calls to Sum yield different results; got %x, want %x", sum, testSum)
222 }
223
224 return sum
225 }
226
227 func newRandReader(t *testing.T) io.Reader {
228 seed := time.Now().UnixNano()
229 t.Logf("Deterministic RNG seed: 0x%x", seed)
230 return rand.New(rand.NewSource(seed))
231 }
232
View as plain text