implement XOF
This commit is contained in:
parent
062bad69bc
commit
2ca7badf67
61
blake3.go
61
blake3.go
|
@ -141,24 +141,40 @@ func (o *output) chaining_value() [8]uint32 {
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *output) root_output_bytes(out_slice []byte) {
|
// An OutputReader produces an unbounded stream of output from its initial
|
||||||
output_block_counter := uint64(0)
|
// state.
|
||||||
for len(out_slice) > 0 {
|
type OutputReader struct {
|
||||||
words := compress(
|
o *output
|
||||||
&o.input_chaining_value,
|
block [BLOCK_LEN]byte
|
||||||
&o.block_words,
|
remaining int
|
||||||
output_block_counter,
|
blocks_output uint64
|
||||||
o.block_len,
|
}
|
||||||
o.flags|ROOT,
|
|
||||||
)
|
// Read implements io.Reader. Read always return len(p), nil.
|
||||||
var wordsBytes [16 * 4]byte
|
func (or *OutputReader) Read(p []byte) (int, error) {
|
||||||
for i, w := range words {
|
lenp := len(p)
|
||||||
binary.LittleEndian.PutUint32(wordsBytes[i*4:], w)
|
for len(p) > 0 {
|
||||||
|
if or.remaining == 0 {
|
||||||
|
words := compress(
|
||||||
|
&or.o.input_chaining_value,
|
||||||
|
&or.o.block_words,
|
||||||
|
or.blocks_output,
|
||||||
|
or.o.block_len,
|
||||||
|
or.o.flags|ROOT,
|
||||||
|
)
|
||||||
|
for i, w := range words {
|
||||||
|
binary.LittleEndian.PutUint32(or.block[i*4:], w)
|
||||||
|
}
|
||||||
|
or.remaining = BLOCK_LEN
|
||||||
|
or.blocks_output++
|
||||||
}
|
}
|
||||||
n := copy(out_slice, wordsBytes[:])
|
|
||||||
out_slice = out_slice[n:]
|
// copy from output buffer
|
||||||
output_block_counter++
|
n := copy(p, or.block[BLOCK_LEN-or.remaining:])
|
||||||
|
or.remaining -= n
|
||||||
|
p = p[n:]
|
||||||
}
|
}
|
||||||
|
return lenp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type chunkState struct {
|
type chunkState struct {
|
||||||
|
@ -348,6 +364,13 @@ func (h *Hasher) Write(input []byte) (int, error) {
|
||||||
|
|
||||||
// Sum implements hash.Hash.
|
// Sum implements hash.Hash.
|
||||||
func (h *Hasher) Sum(out_slice []byte) []byte {
|
func (h *Hasher) Sum(out_slice []byte) []byte {
|
||||||
|
out := make([]byte, h.Size())
|
||||||
|
h.XOF().Read(out)
|
||||||
|
return append(out_slice, out...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// XOF returns an OutputReader initialized with the current hash state.
|
||||||
|
func (h *Hasher) XOF() *OutputReader {
|
||||||
// Starting with the output from the current chunk, compute all the
|
// Starting with the output from the current chunk, compute all the
|
||||||
// parent chaining values along the right edge of the tree, until we
|
// parent chaining values along the right edge of the tree, until we
|
||||||
// have the root output.
|
// have the root output.
|
||||||
|
@ -362,9 +385,9 @@ func (h *Hasher) Sum(out_slice []byte) []byte {
|
||||||
h.flags,
|
h.flags,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
out := make([]byte, h.Size())
|
return &OutputReader{
|
||||||
output.root_output_bytes(out)
|
o: output,
|
||||||
return append(out_slice, out...)
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ensure that Hasher implements hash.Hash
|
// ensure that Hasher implements hash.Hash
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
package blake3_test
|
package blake3_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
@ -65,6 +67,14 @@ func TestVectors(t *testing.T) {
|
||||||
if out := toHex(h.Sum(nil)); out != vec.DeriveKey {
|
if out := toHex(h.Sum(nil)); out != vec.DeriveKey {
|
||||||
t.Errorf("output did not match test vector:\n\texpected: %v...\n\t got: %v...", vec.DeriveKey[:10], out[:10])
|
t.Errorf("output did not match test vector:\n\texpected: %v...\n\t got: %v...", vec.DeriveKey[:10], out[:10])
|
||||||
}
|
}
|
||||||
|
// XOF should produce identical results, even when outputting 7 bytes at a time
|
||||||
|
h = blake3.New(len(vec.Hash)/2, nil)
|
||||||
|
h.Write(in)
|
||||||
|
var xofBuf bytes.Buffer
|
||||||
|
io.CopyBuffer(&xofBuf, io.LimitReader(h.XOF(), int64(len(vec.Hash)/2)), make([]byte, 7))
|
||||||
|
if out := toHex(xofBuf.Bytes()); out != vec.Hash {
|
||||||
|
t.Errorf("XOF output did not match test vector:\n\texpected: %v...\n\t got: %v...", vec.Hash[:10], out[:10])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,12 +87,17 @@ func BenchmarkWrite(b *testing.B) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkBlock(b *testing.B) {
|
func BenchmarkChunk(b *testing.B) {
|
||||||
h := blake3.New(32, nil)
|
h := blake3.New(32, nil)
|
||||||
buf := make([]byte, h.BlockSize())
|
buf := make([]byte, blake3.CHUNK_LEN)
|
||||||
out := make([]byte, 32)
|
out := make([]byte, 32)
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
h.Write(buf)
|
h.Write(buf)
|
||||||
h.Sum(out)
|
h.Sum(out)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func BenchmarkXOF(b *testing.B) {
|
||||||
|
b.SetBytes(1)
|
||||||
|
io.CopyN(ioutil.Discard, blake3.New(32, nil).XOF(), int64(b.N))
|
||||||
|
}
|
||||||
|
|
Reference in New Issue