Skip to content

Commit 3f7c0cc

Browse files
aclementsgopherbot
authored andcommitted
internal/testhash: move cryptotest.TestHash to shared package
This test helper can test any hash.Hash, not just crypto hashes. Move it to $GOROOT/src/internal/testhash so both crypto and hash can use it. For #69521 Change-Id: Iac086cca513d5c03936e35d1ab55b8636f4652f7 Reviewed-on: https://go-review.googlesource.com/c/go/+/670178 Reviewed-by: qiu laidongfeng2 <[email protected]> Auto-Submit: Austin Clements <[email protected]> Reviewed-by: Roland Shoemaker <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]>
1 parent f4e37b8 commit 3f7c0cc

File tree

3 files changed

+196
-165
lines changed

3 files changed

+196
-165
lines changed

src/crypto/internal/cryptotest/hash.go

Lines changed: 3 additions & 164 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
// Copyright 2024 The Go Authors. All rights reserved.
1+
// Copyright 2025 The Go Authors. All rights reserved.
22
// Use of this source code is governed by a BSD-style
33
// license that can be found in the LICENSE file.
44

55
package cryptotest
66

77
import (
8-
"bytes"
98
"hash"
9+
"internal/testhash"
1010
"io"
1111
"math/rand"
1212
"testing"
@@ -18,168 +18,7 @@ type MakeHash func() hash.Hash
1818
// TestHash performs a set of tests on hash.Hash implementations, checking the
1919
// documented requirements of Write, Sum, Reset, Size, and BlockSize.
2020
func TestHash(t *testing.T, mh MakeHash) {
21-
22-
// Test that Sum returns an appended digest matching output of Size
23-
t.Run("SumAppend", func(t *testing.T) {
24-
h := mh()
25-
rng := newRandReader(t)
26-
27-
emptyBuff := []byte("")
28-
shortBuff := []byte("a")
29-
longBuff := make([]byte, h.BlockSize()+1)
30-
rng.Read(longBuff)
31-
32-
// Set of example strings to append digest to
33-
prefixes := [][]byte{nil, emptyBuff, shortBuff, longBuff}
34-
35-
// Go to each string and check digest gets appended to and is correct size.
36-
for _, prefix := range prefixes {
37-
h.Reset()
38-
39-
sum := getSum(t, h, prefix) // Append new digest to prefix
40-
41-
// Check that Sum didn't alter the prefix
42-
if !bytes.Equal(sum[:len(prefix)], prefix) {
43-
t.Errorf("Sum alters passed buffer instead of appending; got %x, want %x", sum[:len(prefix)], prefix)
44-
}
45-
46-
// Check that the appended sum wasn't affected by the prefix
47-
if expectedSum := getSum(t, h, nil); !bytes.Equal(sum[len(prefix):], expectedSum) {
48-
t.Errorf("Sum behavior affected by data in the input buffer; got %x, want %x", sum[len(prefix):], expectedSum)
49-
}
50-
51-
// Check size of append
52-
if got, want := len(sum)-len(prefix), h.Size(); got != want {
53-
t.Errorf("Sum appends number of bytes != Size; got %v , want %v", got, want)
54-
}
55-
}
56-
})
57-
58-
// Test that Hash.Write never returns error.
59-
t.Run("WriteWithoutError", func(t *testing.T) {
60-
h := mh()
61-
rng := newRandReader(t)
62-
63-
emptySlice := []byte("")
64-
shortSlice := []byte("a")
65-
longSlice := make([]byte, h.BlockSize()+1)
66-
rng.Read(longSlice)
67-
68-
// Set of example strings to append digest to
69-
slices := [][]byte{emptySlice, shortSlice, longSlice}
70-
71-
for _, slice := range slices {
72-
writeToHash(t, h, slice) // Writes and checks Write doesn't error
73-
}
74-
})
75-
76-
t.Run("ResetState", func(t *testing.T) {
77-
h := mh()
78-
rng := newRandReader(t)
79-
80-
emptySum := getSum(t, h, nil)
81-
82-
// Write to hash and then Reset it and see if Sum is same as emptySum
83-
writeEx := make([]byte, h.BlockSize())
84-
rng.Read(writeEx)
85-
writeToHash(t, h, writeEx)
86-
h.Reset()
87-
resetSum := getSum(t, h, nil)
88-
89-
if !bytes.Equal(emptySum, resetSum) {
90-
t.Errorf("Reset hash yields different Sum than new hash; got %x, want %x", emptySum, resetSum)
91-
}
92-
})
93-
94-
// Check that Write isn't reading from beyond input slice's bounds
95-
t.Run("OutOfBoundsRead", func(t *testing.T) {
96-
h := mh()
97-
blockSize := h.BlockSize()
98-
rng := newRandReader(t)
99-
100-
msg := make([]byte, blockSize)
101-
rng.Read(msg)
102-
writeToHash(t, h, msg)
103-
expectedDigest := getSum(t, h, nil) // Record control digest
104-
105-
h.Reset()
106-
107-
// Make a buffer with msg in the middle and data on either end
108-
buff := make([]byte, blockSize*3)
109-
endOfPrefix, startOfSuffix := blockSize, blockSize*2
110-
111-
copy(buff[endOfPrefix:startOfSuffix], msg)
112-
rng.Read(buff[:endOfPrefix])
113-
rng.Read(buff[startOfSuffix:])
114-
115-
writeToHash(t, h, buff[endOfPrefix:startOfSuffix])
116-
testDigest := getSum(t, h, nil)
117-
118-
if !bytes.Equal(testDigest, expectedDigest) {
119-
t.Errorf("Write affected by data outside of input slice bounds; got %x, want %x", testDigest, expectedDigest)
120-
}
121-
})
122-
123-
// Test that multiple calls to Write is stateful
124-
t.Run("StatefulWrite", func(t *testing.T) {
125-
h := mh()
126-
rng := newRandReader(t)
127-
128-
prefix, suffix := make([]byte, h.BlockSize()), make([]byte, h.BlockSize())
129-
rng.Read(prefix)
130-
rng.Read(suffix)
131-
132-
// Write prefix then suffix sequentially and record resulting hash
133-
writeToHash(t, h, prefix)
134-
writeToHash(t, h, suffix)
135-
serialSum := getSum(t, h, nil)
136-
137-
h.Reset()
138-
139-
// Write prefix and suffix at the same time and record resulting hash
140-
writeToHash(t, h, append(prefix, suffix...))
141-
compositeSum := getSum(t, h, nil)
142-
143-
// Check that sequential writing results in the same as writing all at once
144-
if !bytes.Equal(compositeSum, serialSum) {
145-
t.Errorf("two successive Write calls resulted in a different Sum than a single one; got %x, want %x", compositeSum, serialSum)
146-
}
147-
})
148-
}
149-
150-
// Helper function for writing. Verifies that Write does not error.
151-
func writeToHash(t *testing.T, h hash.Hash, p []byte) {
152-
t.Helper()
153-
154-
before := make([]byte, len(p))
155-
copy(before, p)
156-
157-
n, err := h.Write(p)
158-
if err != nil || n != len(p) {
159-
t.Errorf("Write returned error; got (%v, %v), want (nil, %v)", err, n, len(p))
160-
}
161-
162-
if !bytes.Equal(p, before) {
163-
t.Errorf("Write modified input slice; got %x, want %x", p, before)
164-
}
165-
}
166-
167-
// Helper function for getting Sum. Checks that Sum doesn't change hash state.
168-
func getSum(t *testing.T, h hash.Hash, buff []byte) []byte {
169-
t.Helper()
170-
171-
testBuff := make([]byte, len(buff))
172-
copy(testBuff, buff)
173-
174-
sum := h.Sum(buff)
175-
testSum := h.Sum(testBuff)
176-
177-
// Check that Sum doesn't change underlying hash state
178-
if !bytes.Equal(sum, testSum) {
179-
t.Errorf("successive calls to Sum yield different results; got %x, want %x", sum, testSum)
180-
}
181-
182-
return sum
21+
testhash.TestHash(t, testhash.MakeHash(mh))
18322
}
18423

18524
func newRandReader(t *testing.T) io.Reader {

src/go/build/deps_test.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -712,7 +712,10 @@ var depsRules = `
712712
FMT
713713
< internal/txtar;
714714
715-
CRYPTO-MATH, testing, internal/testenv, encoding/json
715+
testing
716+
< internal/testhash;
717+
718+
CRYPTO-MATH, testing, internal/testenv, internal/testhash, encoding/json
716719
< crypto/internal/cryptotest;
717720
718721
CGO, FMT

src/internal/testhash/hash.go

Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
// Copyright 2024 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
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+
// TestHash performs a set of tests on hash.Hash implementations, checking the
19+
// documented requirements of Write, Sum, Reset, Size, and BlockSize.
20+
func TestHash(t *testing.T, mh MakeHash) {
21+
22+
// Test that Sum returns an appended digest matching output of Size
23+
t.Run("SumAppend", func(t *testing.T) {
24+
h := mh()
25+
rng := newRandReader(t)
26+
27+
emptyBuff := []byte("")
28+
shortBuff := []byte("a")
29+
longBuff := make([]byte, h.BlockSize()+1)
30+
rng.Read(longBuff)
31+
32+
// Set of example strings to append digest to
33+
prefixes := [][]byte{nil, emptyBuff, shortBuff, longBuff}
34+
35+
// Go to each string and check digest gets appended to and is correct size.
36+
for _, prefix := range prefixes {
37+
h.Reset()
38+
39+
sum := getSum(t, h, prefix) // Append new digest to prefix
40+
41+
// Check that Sum didn't alter the prefix
42+
if !bytes.Equal(sum[:len(prefix)], prefix) {
43+
t.Errorf("Sum alters passed buffer instead of appending; got %x, want %x", sum[:len(prefix)], prefix)
44+
}
45+
46+
// Check that the appended sum wasn't affected by the prefix
47+
if expectedSum := getSum(t, h, nil); !bytes.Equal(sum[len(prefix):], expectedSum) {
48+
t.Errorf("Sum behavior affected by data in the input buffer; got %x, want %x", sum[len(prefix):], expectedSum)
49+
}
50+
51+
// Check size of append
52+
if got, want := len(sum)-len(prefix), h.Size(); got != want {
53+
t.Errorf("Sum appends number of bytes != Size; got %v , want %v", got, want)
54+
}
55+
}
56+
})
57+
58+
// Test that Hash.Write never returns error.
59+
t.Run("WriteWithoutError", func(t *testing.T) {
60+
h := mh()
61+
rng := newRandReader(t)
62+
63+
emptySlice := []byte("")
64+
shortSlice := []byte("a")
65+
longSlice := make([]byte, h.BlockSize()+1)
66+
rng.Read(longSlice)
67+
68+
// Set of example strings to append digest to
69+
slices := [][]byte{emptySlice, shortSlice, longSlice}
70+
71+
for _, slice := range slices {
72+
writeToHash(t, h, slice) // Writes and checks Write doesn't error
73+
}
74+
})
75+
76+
t.Run("ResetState", func(t *testing.T) {
77+
h := mh()
78+
rng := newRandReader(t)
79+
80+
emptySum := getSum(t, h, nil)
81+
82+
// Write to hash and then Reset it and see if Sum is same as emptySum
83+
writeEx := make([]byte, h.BlockSize())
84+
rng.Read(writeEx)
85+
writeToHash(t, h, writeEx)
86+
h.Reset()
87+
resetSum := getSum(t, h, nil)
88+
89+
if !bytes.Equal(emptySum, resetSum) {
90+
t.Errorf("Reset hash yields different Sum than new hash; got %x, want %x", emptySum, resetSum)
91+
}
92+
})
93+
94+
// Check that Write isn't reading from beyond input slice's bounds
95+
t.Run("OutOfBoundsRead", func(t *testing.T) {
96+
h := mh()
97+
blockSize := h.BlockSize()
98+
rng := newRandReader(t)
99+
100+
msg := make([]byte, blockSize)
101+
rng.Read(msg)
102+
writeToHash(t, h, msg)
103+
expectedDigest := getSum(t, h, nil) // Record control digest
104+
105+
h.Reset()
106+
107+
// Make a buffer with msg in the middle and data on either end
108+
buff := make([]byte, blockSize*3)
109+
endOfPrefix, startOfSuffix := blockSize, blockSize*2
110+
111+
copy(buff[endOfPrefix:startOfSuffix], msg)
112+
rng.Read(buff[:endOfPrefix])
113+
rng.Read(buff[startOfSuffix:])
114+
115+
writeToHash(t, h, buff[endOfPrefix:startOfSuffix])
116+
testDigest := getSum(t, h, nil)
117+
118+
if !bytes.Equal(testDigest, expectedDigest) {
119+
t.Errorf("Write affected by data outside of input slice bounds; got %x, want %x", testDigest, expectedDigest)
120+
}
121+
})
122+
123+
// Test that multiple calls to Write is stateful
124+
t.Run("StatefulWrite", func(t *testing.T) {
125+
h := mh()
126+
rng := newRandReader(t)
127+
128+
prefix, suffix := make([]byte, h.BlockSize()), make([]byte, h.BlockSize())
129+
rng.Read(prefix)
130+
rng.Read(suffix)
131+
132+
// Write prefix then suffix sequentially and record resulting hash
133+
writeToHash(t, h, prefix)
134+
writeToHash(t, h, suffix)
135+
serialSum := getSum(t, h, nil)
136+
137+
h.Reset()
138+
139+
// Write prefix and suffix at the same time and record resulting hash
140+
writeToHash(t, h, append(prefix, suffix...))
141+
compositeSum := getSum(t, h, nil)
142+
143+
// Check that sequential writing results in the same as writing all at once
144+
if !bytes.Equal(compositeSum, serialSum) {
145+
t.Errorf("two successive Write calls resulted in a different Sum than a single one; got %x, want %x", compositeSum, serialSum)
146+
}
147+
})
148+
}
149+
150+
// Helper function for writing. Verifies that Write does not error.
151+
func writeToHash(t *testing.T, h hash.Hash, p []byte) {
152+
t.Helper()
153+
154+
before := make([]byte, len(p))
155+
copy(before, p)
156+
157+
n, err := h.Write(p)
158+
if err != nil || n != len(p) {
159+
t.Errorf("Write returned error; got (%v, %v), want (nil, %v)", err, n, len(p))
160+
}
161+
162+
if !bytes.Equal(p, before) {
163+
t.Errorf("Write modified input slice; got %x, want %x", p, before)
164+
}
165+
}
166+
167+
// Helper function for getting Sum. Checks that Sum doesn't change hash state.
168+
func getSum(t *testing.T, h hash.Hash, buff []byte) []byte {
169+
t.Helper()
170+
171+
testBuff := make([]byte, len(buff))
172+
copy(testBuff, buff)
173+
174+
sum := h.Sum(buff)
175+
testSum := h.Sum(testBuff)
176+
177+
// Check that Sum doesn't change underlying hash state
178+
if !bytes.Equal(sum, testSum) {
179+
t.Errorf("successive calls to Sum yield different results; got %x, want %x", sum, testSum)
180+
}
181+
182+
return sum
183+
}
184+
185+
func newRandReader(t *testing.T) io.Reader {
186+
seed := time.Now().UnixNano()
187+
t.Logf("Deterministic RNG seed: 0x%x", seed)
188+
return rand.New(rand.NewSource(seed))
189+
}

0 commit comments

Comments
 (0)