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  }