1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
|
package md5x16
import (
"crypto/md5"
"encoding/hex"
"math/rand"
"testing"
"testing/quick"
"golang.org/x/sys/cpu"
)
func RequireISA(t *testing.T) {
t.Helper()
if !cpu.X86.HasAVX512F {
t.Skip("requires AVX512F instruction set")
}
}
func TestVectors(t *testing.T) {
RequireISA(t)
cases := []struct {
Data string
HexDigest string
}{
{"", "d41d8cd98f00b204e9800998ecf8427e"},
{"The quick brown fox jumps over the lazy dog", "9e107d9d372bb6826bd81d3542a419d6"},
{"The quick brown fox jumps over the lazy dog.", "e4d909c290d0fb1ca068ffaddf22cbd0"},
}
for _, c := range cases {
digest := Single(t, []byte(c.Data))
got := hex.EncodeToString(digest[:])
if got != c.HexDigest {
t.Errorf("Sum(%#v) = %s; expect %s", c.Data, got, c.HexDigest)
}
}
}
func TestCmp(t *testing.T) {
RequireISA(t)
sum := func(data []byte) [Size]byte { return Single(t, data) }
if err := quick.CheckEqual(sum, md5.Sum, nil); err != nil {
t.Fatal(err)
}
}
func TestLengths(t *testing.T) {
RequireISA(t)
const max = BlockSize << 6
data := make([]byte, max)
rand.Read(data)
for n := 0; n <= max; n++ {
got := Single(t, data[:n])
expect := md5.Sum(data[:n])
if got != expect {
t.Fatalf("failed on length %d", n)
}
}
}
// Single hashes a single data buffer in all 16 lanes and returns the result,
// after asserting that all lanes are the same.
func Single(t *testing.T, d []byte) [Size]byte {
// Place the same data in every lane.
var data [Lanes][]byte
for l := range data {
data[l] = d
}
if err := Validate(data); err != nil {
t.Fatal(err)
}
// Hash and check the lanes are the same.
digest := Sum(data)
for l := range data {
if digest[0] != digest[l] {
t.Logf("lane %02d: %x", 0, digest[0])
t.Logf("lane %02d: %x", l, digest[l])
t.Fatal("lane mismatch")
}
}
return digest[0]
}
func TestActiveLanes(t *testing.T) {
RequireISA(t)
const trials = 1 << 10
const maxlen = BlockSize << 6
for trial := 0; trial < trials; trial++ {
// Pick active lanes.
lanes := 1 + rand.Intn(Lanes-1)
active := rand.Perm(Lanes)[:lanes]
// Fill active lanes with random data.
n := rand.Intn(maxlen)
buffer := make([]byte, lanes*n)
rand.Read(buffer)
var data [Lanes][]byte
for i, l := range active {
data[l] = buffer[i*n : (i+1)*n]
}
// Hash.
digest := Sum(data)
// Verify correct result in active lanes.
for _, l := range active {
expect := md5.Sum(data[l])
if digest[l] != expect {
t.Fatalf("lane %02d: mismatch", l)
}
}
// Verify other lanes are zero.
isactive := map[int]bool{}
for _, l := range active {
isactive[l] = true
}
for l := 0; l < Lanes; l++ {
if !isactive[l] {
var zero [Size]byte
if digest[l] != zero {
t.Fatalf("inactive lane %d is non-zero", l)
}
}
}
}
}
|