git.sr.ht/~pingoo/stdx@v0.0.0-20240218134121-094174641f6e/crypto/internal/blake3/blake3_test.go (about) 1 package blake3_test 2 3 import ( 4 "bytes" 5 "encoding/hex" 6 "encoding/json" 7 "io" 8 "os" 9 "testing" 10 11 "lukechampine.com/blake3" 12 ) 13 14 func toHex(data []byte) string { return hex.EncodeToString(data) } 15 16 var testVectors = func() (vecs struct { 17 Key string 18 Cases []struct { 19 InputLen int `json:"input_len"` 20 Hash string `json:"hash"` 21 KeyedHash string `json:"keyed_hash"` 22 DeriveKey string `json:"derive_key"` 23 } 24 }) { 25 data, err := os.ReadFile("testdata/vectors.json") 26 if err != nil { 27 panic(err) 28 } 29 if err := json.Unmarshal(data, &vecs); err != nil { 30 panic(err) 31 } 32 return 33 }() 34 35 var testInput = func() []byte { 36 input := make([]byte, 1e6) 37 for i := range input { 38 input[i] = byte(i % 251) 39 } 40 return input 41 }() 42 43 func TestVectors(t *testing.T) { 44 for _, vec := range testVectors.Cases { 45 in := testInput[:vec.InputLen] 46 47 // regular 48 h := blake3.New(len(vec.Hash)/2, nil) 49 h.Write(in) 50 if out := toHex(h.Sum(nil)); out != vec.Hash { 51 t.Errorf("output did not match test vector:\n\texpected: %v...\n\t got: %v...", vec.Hash[:10], out[:10]) 52 } 53 54 // keyed 55 h = blake3.New(len(vec.KeyedHash)/2, []byte(testVectors.Key)) 56 h.Write(in) 57 if out := toHex(h.Sum(nil)); out != vec.KeyedHash { 58 t.Errorf("output did not match test vector:\n\texpected: %v...\n\t got: %v...", vec.KeyedHash[:10], out[:10]) 59 } 60 61 // derive key 62 const ctx = "BLAKE3 2019-12-27 16:29:52 test vectors context" 63 subKey := make([]byte, len(vec.DeriveKey)/2) 64 blake3.DeriveKey(subKey, ctx, in) 65 if out := toHex(subKey); out != vec.DeriveKey { 66 t.Errorf("output did not match test vector:\n\texpected: %v...\n\t got: %v...", vec.DeriveKey[:10], out[:10]) 67 } 68 } 69 } 70 71 func TestXOF(t *testing.T) { 72 for _, vec := range testVectors.Cases { 73 in := testInput[:vec.InputLen] 74 75 // XOF should produce same output as Sum, even when outputting 7 bytes at a time 76 h := blake3.New(len(vec.Hash)/2, nil) 77 h.Write(in) 78 var xofBuf bytes.Buffer 79 io.CopyBuffer(&xofBuf, io.LimitReader(h.XOF(), int64(len(vec.Hash)/2)), make([]byte, 7)) 80 if out := toHex(xofBuf.Bytes()); out != vec.Hash { 81 t.Errorf("XOF output did not match test vector:\n\texpected: %v...\n\t got: %v...", vec.Hash[:10], out[:10]) 82 } 83 84 // Should be able to Seek around in the output stream without affecting correctness 85 seeks := []struct { 86 offset int64 87 whence int 88 }{ 89 {0, io.SeekStart}, 90 {17, io.SeekCurrent}, 91 {-5, io.SeekCurrent}, 92 {int64(h.Size()), io.SeekStart}, 93 {int64(h.Size()), io.SeekCurrent}, 94 } 95 xof := h.XOF() 96 outR := bytes.NewReader(xofBuf.Bytes()) 97 for _, s := range seeks { 98 outRead := make([]byte, 10) 99 xofRead := make([]byte, 10) 100 offset, _ := outR.Seek(s.offset, s.whence) 101 n, _ := outR.Read(outRead) 102 xof.Seek(s.offset, s.whence) 103 xof.Read(xofRead[:n]) 104 if !bytes.Equal(outRead[:n], xofRead[:n]) { 105 t.Errorf("XOF output did not match test vector at offset %v:\n\texpected: %x...\n\t got: %x...", offset, outRead[:10], xofRead[:10]) 106 } 107 } 108 } 109 // test behavior at end of stream 110 xof := blake3.New(0, nil).XOF() 111 buf := make([]byte, 1024) 112 xof.Seek(-1000, io.SeekEnd) 113 n, err := xof.Read(buf) 114 if n != 1000 || err != nil { 115 t.Errorf("expected (1000, nil) when reading near end of stream, got (%v, %v)", n, err) 116 } 117 n, err = xof.Read(buf) 118 if n != 0 || err != io.EOF { 119 t.Errorf("expected (0, EOF) when reading past end of stream, got (%v, %v)", n, err) 120 } 121 122 // test invalid seek offsets 123 _, err = xof.Seek(-1, io.SeekStart) 124 if err == nil { 125 t.Error("expected invalid offset error, got nil") 126 } 127 xof.Seek(0, io.SeekStart) 128 _, err = xof.Seek(-1, io.SeekCurrent) 129 if err == nil { 130 t.Error("expected invalid offset error, got nil") 131 } 132 133 // test invalid seek whence 134 didPanic := func() (p bool) { 135 defer func() { p = recover() != nil }() 136 xof.Seek(0, 17) 137 return 138 }() 139 if !didPanic { 140 t.Error("expected panic when seeking with invalid whence") 141 } 142 } 143 144 func TestSum(t *testing.T) { 145 for _, vec := range testVectors.Cases { 146 in := testInput[:vec.InputLen] 147 148 var exp256 [32]byte 149 h := blake3.New(32, nil) 150 h.Write(in) 151 h.Sum(exp256[:0]) 152 if got256 := blake3.Sum256(in); exp256 != got256 { 153 t.Errorf("Sum256 output did not match Sum output:\n\texpected: %x...\n\t got: %x...", exp256[:5], got256[:5]) 154 } 155 156 var exp512 [64]byte 157 h = blake3.New(64, nil) 158 h.Write(in) 159 h.Sum(exp512[:0]) 160 if got512 := blake3.Sum512(in); exp512 != got512 { 161 t.Errorf("Sum512 output did not match Sum output:\n\texpected: %x...\n\t got: %x...", exp512[:5], got512[:5]) 162 } 163 } 164 } 165 166 func TestReset(t *testing.T) { 167 for _, vec := range testVectors.Cases { 168 in := testInput[:vec.InputLen] 169 170 h := blake3.New(32, nil) 171 h.Write(in) 172 out1 := h.Sum(nil) 173 h.Reset() 174 h.Write(in) 175 out2 := h.Sum(nil) 176 if !bytes.Equal(out1, out2) { 177 t.Error("Reset did not reset Hasher state properly") 178 } 179 } 180 181 // gotta have 100% test coverage... 182 if blake3.New(0, nil).BlockSize() != 64 { 183 t.Error("incorrect block size") 184 } 185 } 186 187 type nopReader struct{} 188 189 func (nopReader) Read(p []byte) (int, error) { return len(p), nil } 190 191 func BenchmarkWrite(b *testing.B) { 192 b.ReportAllocs() 193 b.SetBytes(1024) 194 io.CopyN(blake3.New(0, nil), nopReader{}, int64(b.N*1024)) 195 } 196 197 func BenchmarkXOF(b *testing.B) { 198 b.ReportAllocs() 199 b.SetBytes(1024) 200 io.CopyN(io.Discard, blake3.New(0, nil).XOF(), int64(b.N*1024)) 201 } 202 203 func BenchmarkSum256(b *testing.B) { 204 b.Run("64", func(b *testing.B) { 205 b.ReportAllocs() 206 b.SetBytes(64) 207 buf := make([]byte, 64) 208 for i := 0; i < b.N; i++ { 209 blake3.Sum256(buf) 210 } 211 }) 212 b.Run("1024", func(b *testing.B) { 213 b.ReportAllocs() 214 b.SetBytes(1024) 215 buf := make([]byte, 1024) 216 for i := 0; i < b.N; i++ { 217 blake3.Sum256(buf) 218 } 219 }) 220 b.Run("65536", func(b *testing.B) { 221 b.ReportAllocs() 222 b.SetBytes(65536) 223 buf := make([]byte, 65536) 224 for i := 0; i < b.N; i++ { 225 blake3.Sum256(buf) 226 } 227 }) 228 }