2020-01-09 20:10:01 +00:00
|
|
|
// Package blake3 implements the BLAKE3 cryptographic hash function.
|
2020-01-14 05:28:18 +00:00
|
|
|
package blake3 // import "lukechampine.com/blake3"
|
2020-01-09 20:10:01 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/binary"
|
2020-01-10 17:43:02 +00:00
|
|
|
"errors"
|
2020-01-09 20:10:01 +00:00
|
|
|
"hash"
|
2020-01-10 17:43:02 +00:00
|
|
|
"io"
|
2020-01-10 19:39:34 +00:00
|
|
|
"math"
|
2020-01-10 17:37:31 +00:00
|
|
|
"math/bits"
|
2020-01-09 20:10:01 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
2020-01-10 21:01:33 +00:00
|
|
|
blockSize = 64
|
2020-01-10 21:37:28 +00:00
|
|
|
chunkSize = 1024
|
2020-01-09 20:10:01 +00:00
|
|
|
)
|
|
|
|
|
2020-01-09 22:58:48 +00:00
|
|
|
// flags
|
|
|
|
const (
|
|
|
|
flagChunkStart = 1 << iota
|
|
|
|
flagChunkEnd
|
|
|
|
flagParent
|
|
|
|
flagRoot
|
|
|
|
flagKeyedHash
|
|
|
|
flagDeriveKeyContext
|
|
|
|
flagDeriveKeyMaterial
|
|
|
|
)
|
2020-01-09 20:10:01 +00:00
|
|
|
|
2020-01-09 22:58:48 +00:00
|
|
|
var iv = [8]uint32{
|
|
|
|
0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A,
|
|
|
|
0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19,
|
2020-01-09 20:10:01 +00:00
|
|
|
}
|
|
|
|
|
2020-01-12 01:09:16 +00:00
|
|
|
// helper functions for converting between bytes and BLAKE3 "words"
|
|
|
|
|
2020-01-13 19:27:21 +00:00
|
|
|
func bytesToWords(bytes [64]byte, words *[16]uint32) {
|
|
|
|
words[0] = binary.LittleEndian.Uint32(bytes[0:])
|
|
|
|
words[1] = binary.LittleEndian.Uint32(bytes[4:])
|
|
|
|
words[2] = binary.LittleEndian.Uint32(bytes[8:])
|
|
|
|
words[3] = binary.LittleEndian.Uint32(bytes[12:])
|
|
|
|
words[4] = binary.LittleEndian.Uint32(bytes[16:])
|
|
|
|
words[5] = binary.LittleEndian.Uint32(bytes[20:])
|
|
|
|
words[6] = binary.LittleEndian.Uint32(bytes[24:])
|
|
|
|
words[7] = binary.LittleEndian.Uint32(bytes[28:])
|
|
|
|
words[8] = binary.LittleEndian.Uint32(bytes[32:])
|
|
|
|
words[9] = binary.LittleEndian.Uint32(bytes[36:])
|
|
|
|
words[10] = binary.LittleEndian.Uint32(bytes[40:])
|
|
|
|
words[11] = binary.LittleEndian.Uint32(bytes[44:])
|
|
|
|
words[12] = binary.LittleEndian.Uint32(bytes[48:])
|
|
|
|
words[13] = binary.LittleEndian.Uint32(bytes[52:])
|
|
|
|
words[14] = binary.LittleEndian.Uint32(bytes[56:])
|
|
|
|
words[15] = binary.LittleEndian.Uint32(bytes[60:])
|
2020-01-12 01:09:16 +00:00
|
|
|
}
|
|
|
|
|
2020-01-13 19:27:21 +00:00
|
|
|
func wordsToBytes(words [16]uint32, block *[64]byte) {
|
|
|
|
binary.LittleEndian.PutUint32(block[0:], words[0])
|
|
|
|
binary.LittleEndian.PutUint32(block[4:], words[1])
|
|
|
|
binary.LittleEndian.PutUint32(block[8:], words[2])
|
|
|
|
binary.LittleEndian.PutUint32(block[12:], words[3])
|
|
|
|
binary.LittleEndian.PutUint32(block[16:], words[4])
|
|
|
|
binary.LittleEndian.PutUint32(block[20:], words[5])
|
|
|
|
binary.LittleEndian.PutUint32(block[24:], words[6])
|
|
|
|
binary.LittleEndian.PutUint32(block[28:], words[7])
|
|
|
|
binary.LittleEndian.PutUint32(block[32:], words[8])
|
|
|
|
binary.LittleEndian.PutUint32(block[36:], words[9])
|
|
|
|
binary.LittleEndian.PutUint32(block[40:], words[10])
|
|
|
|
binary.LittleEndian.PutUint32(block[44:], words[11])
|
|
|
|
binary.LittleEndian.PutUint32(block[48:], words[12])
|
|
|
|
binary.LittleEndian.PutUint32(block[52:], words[13])
|
|
|
|
binary.LittleEndian.PutUint32(block[56:], words[14])
|
|
|
|
binary.LittleEndian.PutUint32(block[60:], words[15])
|
2020-01-12 01:09:16 +00:00
|
|
|
}
|
|
|
|
|
2020-01-12 18:20:37 +00:00
|
|
|
func g(a, b, c, d, mx, my uint32) (uint32, uint32, uint32, uint32) {
|
|
|
|
a += b + mx
|
|
|
|
d = bits.RotateLeft32(d^a, -16)
|
|
|
|
c += d
|
|
|
|
b = bits.RotateLeft32(b^c, -12)
|
|
|
|
a += b + my
|
|
|
|
d = bits.RotateLeft32(d^a, -8)
|
|
|
|
c += d
|
|
|
|
b = bits.RotateLeft32(b^c, -7)
|
|
|
|
return a, b, c, d
|
2020-01-09 20:10:01 +00:00
|
|
|
}
|
|
|
|
|
2020-01-12 01:09:16 +00:00
|
|
|
// A node represents a chunk or parent in the BLAKE3 Merkle tree. In BLAKE3
|
|
|
|
// terminology, the elements of the bottom layer (aka "leaves") of the tree are
|
|
|
|
// called chunk nodes, and the elements of upper layers (aka "interior nodes")
|
|
|
|
// are called parent nodes.
|
|
|
|
//
|
|
|
|
// Computing a BLAKE3 hash involves splitting the input into chunk nodes, then
|
|
|
|
// repeatedly merging these nodes into parent nodes, until only a single "root"
|
|
|
|
// node remains. The root node can then be used to generate up to 2^64 - 1 bytes
|
|
|
|
// of pseudorandom output.
|
2020-01-10 03:46:19 +00:00
|
|
|
type node struct {
|
2020-01-12 01:09:16 +00:00
|
|
|
// the chaining value from the previous state
|
|
|
|
cv [8]uint32
|
|
|
|
// the current state
|
2020-01-10 03:46:19 +00:00
|
|
|
block [16]uint32
|
|
|
|
counter uint64
|
|
|
|
blockLen uint32
|
|
|
|
flags uint32
|
|
|
|
}
|
|
|
|
|
2020-01-12 01:09:16 +00:00
|
|
|
// compress is the core hash function, generating 16 pseudorandom words from a
|
2020-01-13 18:53:16 +00:00
|
|
|
// node.
|
|
|
|
func (n node) compress() [16]uint32 {
|
|
|
|
// NOTE: we unroll all of the rounds, as well as the permutations that occur
|
|
|
|
// between rounds.
|
|
|
|
|
|
|
|
// round 1 (also initializes state)
|
|
|
|
// columns
|
2020-01-12 18:59:44 +00:00
|
|
|
s0, s4, s8, s12 := g(n.cv[0], n.cv[4], iv[0], uint32(n.counter), n.block[0], n.block[1])
|
|
|
|
s1, s5, s9, s13 := g(n.cv[1], n.cv[5], iv[1], uint32(n.counter>>32), n.block[2], n.block[3])
|
|
|
|
s2, s6, s10, s14 := g(n.cv[2], n.cv[6], iv[2], n.blockLen, n.block[4], n.block[5])
|
|
|
|
s3, s7, s11, s15 := g(n.cv[3], n.cv[7], iv[3], n.flags, n.block[6], n.block[7])
|
2020-01-13 18:53:16 +00:00
|
|
|
// diagonals
|
2020-01-12 18:59:44 +00:00
|
|
|
s0, s5, s10, s15 = g(s0, s5, s10, s15, n.block[8], n.block[9])
|
|
|
|
s1, s6, s11, s12 = g(s1, s6, s11, s12, n.block[10], n.block[11])
|
|
|
|
s2, s7, s8, s13 = g(s2, s7, s8, s13, n.block[12], n.block[13])
|
|
|
|
s3, s4, s9, s14 = g(s3, s4, s9, s14, n.block[14], n.block[15])
|
2020-01-12 14:47:47 +00:00
|
|
|
|
2020-01-12 22:01:00 +00:00
|
|
|
// round 2
|
2020-01-12 18:59:44 +00:00
|
|
|
s0, s4, s8, s12 = g(s0, s4, s8, s12, n.block[2], n.block[6])
|
|
|
|
s1, s5, s9, s13 = g(s1, s5, s9, s13, n.block[3], n.block[10])
|
|
|
|
s2, s6, s10, s14 = g(s2, s6, s10, s14, n.block[7], n.block[0])
|
|
|
|
s3, s7, s11, s15 = g(s3, s7, s11, s15, n.block[4], n.block[13])
|
|
|
|
s0, s5, s10, s15 = g(s0, s5, s10, s15, n.block[1], n.block[11])
|
|
|
|
s1, s6, s11, s12 = g(s1, s6, s11, s12, n.block[12], n.block[5])
|
|
|
|
s2, s7, s8, s13 = g(s2, s7, s8, s13, n.block[9], n.block[14])
|
|
|
|
s3, s4, s9, s14 = g(s3, s4, s9, s14, n.block[15], n.block[8])
|
2020-01-12 14:47:47 +00:00
|
|
|
|
2020-01-12 22:01:00 +00:00
|
|
|
// round 3
|
2020-01-12 18:59:44 +00:00
|
|
|
s0, s4, s8, s12 = g(s0, s4, s8, s12, n.block[3], n.block[4])
|
|
|
|
s1, s5, s9, s13 = g(s1, s5, s9, s13, n.block[10], n.block[12])
|
|
|
|
s2, s6, s10, s14 = g(s2, s6, s10, s14, n.block[13], n.block[2])
|
|
|
|
s3, s7, s11, s15 = g(s3, s7, s11, s15, n.block[7], n.block[14])
|
|
|
|
s0, s5, s10, s15 = g(s0, s5, s10, s15, n.block[6], n.block[5])
|
|
|
|
s1, s6, s11, s12 = g(s1, s6, s11, s12, n.block[9], n.block[0])
|
|
|
|
s2, s7, s8, s13 = g(s2, s7, s8, s13, n.block[11], n.block[15])
|
|
|
|
s3, s4, s9, s14 = g(s3, s4, s9, s14, n.block[8], n.block[1])
|
2020-01-12 14:47:47 +00:00
|
|
|
|
2020-01-12 22:01:00 +00:00
|
|
|
// round 4
|
2020-01-12 18:59:44 +00:00
|
|
|
s0, s4, s8, s12 = g(s0, s4, s8, s12, n.block[10], n.block[7])
|
|
|
|
s1, s5, s9, s13 = g(s1, s5, s9, s13, n.block[12], n.block[9])
|
|
|
|
s2, s6, s10, s14 = g(s2, s6, s10, s14, n.block[14], n.block[3])
|
|
|
|
s3, s7, s11, s15 = g(s3, s7, s11, s15, n.block[13], n.block[15])
|
|
|
|
s0, s5, s10, s15 = g(s0, s5, s10, s15, n.block[4], n.block[0])
|
|
|
|
s1, s6, s11, s12 = g(s1, s6, s11, s12, n.block[11], n.block[2])
|
|
|
|
s2, s7, s8, s13 = g(s2, s7, s8, s13, n.block[5], n.block[8])
|
|
|
|
s3, s4, s9, s14 = g(s3, s4, s9, s14, n.block[1], n.block[6])
|
2020-01-12 14:47:47 +00:00
|
|
|
|
2020-01-12 22:01:00 +00:00
|
|
|
// round 5
|
2020-01-12 18:59:44 +00:00
|
|
|
s0, s4, s8, s12 = g(s0, s4, s8, s12, n.block[12], n.block[13])
|
|
|
|
s1, s5, s9, s13 = g(s1, s5, s9, s13, n.block[9], n.block[11])
|
|
|
|
s2, s6, s10, s14 = g(s2, s6, s10, s14, n.block[15], n.block[10])
|
|
|
|
s3, s7, s11, s15 = g(s3, s7, s11, s15, n.block[14], n.block[8])
|
|
|
|
s0, s5, s10, s15 = g(s0, s5, s10, s15, n.block[7], n.block[2])
|
|
|
|
s1, s6, s11, s12 = g(s1, s6, s11, s12, n.block[5], n.block[3])
|
|
|
|
s2, s7, s8, s13 = g(s2, s7, s8, s13, n.block[0], n.block[1])
|
|
|
|
s3, s4, s9, s14 = g(s3, s4, s9, s14, n.block[6], n.block[4])
|
2020-01-12 14:47:47 +00:00
|
|
|
|
2020-01-12 22:01:00 +00:00
|
|
|
// round 6
|
2020-01-12 18:59:44 +00:00
|
|
|
s0, s4, s8, s12 = g(s0, s4, s8, s12, n.block[9], n.block[14])
|
|
|
|
s1, s5, s9, s13 = g(s1, s5, s9, s13, n.block[11], n.block[5])
|
|
|
|
s2, s6, s10, s14 = g(s2, s6, s10, s14, n.block[8], n.block[12])
|
|
|
|
s3, s7, s11, s15 = g(s3, s7, s11, s15, n.block[15], n.block[1])
|
|
|
|
s0, s5, s10, s15 = g(s0, s5, s10, s15, n.block[13], n.block[3])
|
|
|
|
s1, s6, s11, s12 = g(s1, s6, s11, s12, n.block[0], n.block[10])
|
|
|
|
s2, s7, s8, s13 = g(s2, s7, s8, s13, n.block[2], n.block[6])
|
|
|
|
s3, s4, s9, s14 = g(s3, s4, s9, s14, n.block[4], n.block[7])
|
2020-01-12 14:47:47 +00:00
|
|
|
|
2020-01-12 22:01:00 +00:00
|
|
|
// round 7
|
2020-01-12 18:59:44 +00:00
|
|
|
s0, s4, s8, s12 = g(s0, s4, s8, s12, n.block[11], n.block[15])
|
|
|
|
s1, s5, s9, s13 = g(s1, s5, s9, s13, n.block[5], n.block[0])
|
|
|
|
s2, s6, s10, s14 = g(s2, s6, s10, s14, n.block[1], n.block[9])
|
|
|
|
s3, s7, s11, s15 = g(s3, s7, s11, s15, n.block[8], n.block[6])
|
|
|
|
s0, s5, s10, s15 = g(s0, s5, s10, s15, n.block[14], n.block[10])
|
|
|
|
s1, s6, s11, s12 = g(s1, s6, s11, s12, n.block[2], n.block[12])
|
|
|
|
s2, s7, s8, s13 = g(s2, s7, s8, s13, n.block[3], n.block[4])
|
|
|
|
s3, s4, s9, s14 = g(s3, s4, s9, s14, n.block[7], n.block[13])
|
|
|
|
|
2020-01-13 18:53:16 +00:00
|
|
|
// finalization
|
|
|
|
return [16]uint32{
|
2020-01-12 18:59:44 +00:00
|
|
|
s0 ^ s8, s1 ^ s9, s2 ^ s10, s3 ^ s11,
|
|
|
|
s4 ^ s12, s5 ^ s13, s6 ^ s14, s7 ^ s15,
|
|
|
|
s8 ^ n.cv[0], s9 ^ n.cv[1], s10 ^ n.cv[2], s11 ^ n.cv[3],
|
|
|
|
s12 ^ n.cv[4], s13 ^ n.cv[5], s14 ^ n.cv[6], s15 ^ n.cv[7],
|
|
|
|
}
|
2020-01-09 20:10:01 +00:00
|
|
|
}
|
|
|
|
|
2020-01-12 01:09:16 +00:00
|
|
|
// chainingValue returns the first 8 words of the compressed node. This is used
|
|
|
|
// in two places. First, when a chunk node is being constructed, its cv is
|
|
|
|
// overwritten with this value after each block of input is processed. Second,
|
|
|
|
// when two nodes are merged into a parent, each of their chaining values
|
2020-01-16 04:50:45 +00:00
|
|
|
// supplies half of the new node's block.
|
2020-01-10 03:46:19 +00:00
|
|
|
func (n node) chainingValue() (cv [8]uint32) {
|
|
|
|
full := n.compress()
|
|
|
|
copy(cv[:], full[:8])
|
2020-01-09 20:10:01 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-01-12 01:09:16 +00:00
|
|
|
// chunkState manages the state involved in hashing a single chunk of input.
|
2020-01-09 20:10:01 +00:00
|
|
|
type chunkState struct {
|
2020-01-10 03:46:19 +00:00
|
|
|
n node
|
2020-01-10 21:01:33 +00:00
|
|
|
block [blockSize]byte
|
2020-01-09 22:58:48 +00:00
|
|
|
blockLen int
|
|
|
|
bytesConsumed int
|
2020-01-10 03:46:19 +00:00
|
|
|
}
|
|
|
|
|
2020-01-12 01:09:16 +00:00
|
|
|
// chunkCounter is the index of this chunk, i.e. the number of chunks that have
|
|
|
|
// been processed prior to this one.
|
2020-01-10 03:46:19 +00:00
|
|
|
func (cs *chunkState) chunkCounter() uint64 {
|
|
|
|
return cs.n.counter
|
2020-01-09 20:10:01 +00:00
|
|
|
}
|
|
|
|
|
2020-01-12 02:45:09 +00:00
|
|
|
func (cs *chunkState) complete() bool {
|
|
|
|
return cs.bytesConsumed == chunkSize
|
|
|
|
}
|
|
|
|
|
2020-01-12 01:09:16 +00:00
|
|
|
// update incorporates input into the chunkState.
|
2020-01-09 20:10:01 +00:00
|
|
|
func (cs *chunkState) update(input []byte) {
|
|
|
|
for len(input) > 0 {
|
|
|
|
// If the block buffer is full, compress it and clear it. More
|
2020-01-09 22:58:48 +00:00
|
|
|
// input is coming, so this compression is not flagChunkEnd.
|
2020-01-10 21:01:33 +00:00
|
|
|
if cs.blockLen == blockSize {
|
2020-01-12 01:09:16 +00:00
|
|
|
// copy the chunk block (bytes) into the node block and chain it.
|
2020-01-13 19:27:21 +00:00
|
|
|
bytesToWords(cs.block, &cs.n.block)
|
2020-01-10 03:46:19 +00:00
|
|
|
cs.n.cv = cs.n.chainingValue()
|
2020-01-12 01:09:16 +00:00
|
|
|
// clear the start flag for all but the first block
|
|
|
|
cs.n.flags &^= flagChunkStart
|
2020-01-09 22:58:48 +00:00
|
|
|
cs.blockLen = 0
|
2020-01-09 20:10:01 +00:00
|
|
|
}
|
|
|
|
|
2020-01-12 01:09:16 +00:00
|
|
|
// Copy input bytes into the chunk block.
|
2020-01-09 22:58:48 +00:00
|
|
|
n := copy(cs.block[cs.blockLen:], input)
|
|
|
|
cs.blockLen += n
|
|
|
|
cs.bytesConsumed += n
|
2020-01-09 20:10:01 +00:00
|
|
|
input = input[n:]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-12 01:58:07 +00:00
|
|
|
// compiles to memclr
|
|
|
|
func clear(b []byte) {
|
|
|
|
for i := range b {
|
|
|
|
b[i] = 0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-12 01:09:16 +00:00
|
|
|
// node returns a node containing the chunkState's current state, with the
|
|
|
|
// ChunkEnd flag set.
|
2020-01-10 03:46:19 +00:00
|
|
|
func (cs *chunkState) node() node {
|
|
|
|
n := cs.n
|
2020-01-12 01:58:07 +00:00
|
|
|
// pad the remaining space in the block with zeros
|
|
|
|
clear(cs.block[cs.blockLen:])
|
2020-01-13 19:27:21 +00:00
|
|
|
bytesToWords(cs.block, &n.block)
|
2020-01-10 03:46:19 +00:00
|
|
|
n.blockLen = uint32(cs.blockLen)
|
|
|
|
n.flags |= flagChunkEnd
|
|
|
|
return n
|
2020-01-09 20:10:01 +00:00
|
|
|
}
|
|
|
|
|
2020-01-12 01:09:16 +00:00
|
|
|
func newChunkState(iv [8]uint32, chunkCounter uint64, flags uint32) chunkState {
|
2020-01-09 20:10:01 +00:00
|
|
|
return chunkState{
|
2020-01-10 03:46:19 +00:00
|
|
|
n: node{
|
2020-01-12 01:09:16 +00:00
|
|
|
cv: iv,
|
2020-01-10 03:46:19 +00:00
|
|
|
counter: chunkCounter,
|
2020-01-10 21:01:33 +00:00
|
|
|
blockLen: blockSize,
|
2020-01-12 01:09:16 +00:00
|
|
|
// compress the first block with the start flag set
|
2020-01-10 03:46:19 +00:00
|
|
|
flags: flags | flagChunkStart,
|
|
|
|
},
|
2020-01-09 20:10:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-12 01:09:16 +00:00
|
|
|
// parentNode returns a node that incorporates the chaining values of two child
|
|
|
|
// nodes.
|
2020-01-10 03:46:19 +00:00
|
|
|
func parentNode(left, right [8]uint32, key [8]uint32, flags uint32) node {
|
2020-01-09 22:58:48 +00:00
|
|
|
var blockWords [16]uint32
|
|
|
|
copy(blockWords[:8], left[:])
|
|
|
|
copy(blockWords[8:], right[:])
|
2020-01-10 03:46:19 +00:00
|
|
|
return node{
|
|
|
|
cv: key,
|
|
|
|
block: blockWords,
|
2020-01-12 01:09:16 +00:00
|
|
|
counter: 0, // counter is reset for parents
|
|
|
|
blockLen: blockSize, // block is full: 8 words from left, 8 from right
|
2020-01-10 03:46:19 +00:00
|
|
|
flags: flags | flagParent,
|
2020-01-09 20:10:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Hasher implements hash.Hash.
|
|
|
|
type Hasher struct {
|
2020-01-12 01:59:28 +00:00
|
|
|
cs chunkState
|
|
|
|
key [8]uint32
|
|
|
|
flags uint32
|
|
|
|
size int // output size, for Sum
|
|
|
|
|
|
|
|
// log(n) set of Merkle subtree roots, at most one per height.
|
|
|
|
stack [54][8]uint32 // 2^54 * chunkSize = 2^64
|
|
|
|
used uint64 // bit vector indicating which stack elems are valid; also number of chunks added
|
|
|
|
}
|
|
|
|
|
|
|
|
func (h *Hasher) hasSubtreeAtHeight(i uint64) bool {
|
|
|
|
return h.used&(1<<i) != 0
|
2020-01-09 20:10:01 +00:00
|
|
|
}
|
|
|
|
|
2020-01-12 01:09:16 +00:00
|
|
|
// addChunkChainingValue appends a chunk to the right edge of the Merkle tree.
|
2020-01-12 01:59:28 +00:00
|
|
|
func (h *Hasher) addChunkChainingValue(cv [8]uint32) {
|
|
|
|
// seek to first open stack slot, merging subtrees as we go
|
|
|
|
i := uint64(0)
|
|
|
|
for ; h.hasSubtreeAtHeight(i); i++ {
|
|
|
|
cv = parentNode(h.stack[i], cv, h.key, h.flags).chainingValue()
|
2020-01-09 20:10:01 +00:00
|
|
|
}
|
2020-01-12 01:59:28 +00:00
|
|
|
h.stack[i] = cv
|
|
|
|
h.used++
|
2020-01-09 20:10:01 +00:00
|
|
|
}
|
|
|
|
|
2020-01-12 01:09:16 +00:00
|
|
|
// rootNode computes the root of the Merkle tree. It does not modify the
|
|
|
|
// chainStack.
|
|
|
|
func (h *Hasher) rootNode() node {
|
2020-01-10 22:17:39 +00:00
|
|
|
n := h.cs.node()
|
2020-01-12 01:59:28 +00:00
|
|
|
for i := uint64(bits.TrailingZeros64(h.used)); i < 64; i++ {
|
|
|
|
if h.hasSubtreeAtHeight(i) {
|
|
|
|
n = parentNode(h.stack[i], n.chainingValue(), h.key, h.flags)
|
|
|
|
}
|
2020-01-10 22:17:39 +00:00
|
|
|
}
|
|
|
|
n.flags |= flagRoot
|
|
|
|
return n
|
|
|
|
}
|
|
|
|
|
2020-01-09 20:10:01 +00:00
|
|
|
// Reset implements hash.Hash.
|
|
|
|
func (h *Hasher) Reset() {
|
2020-01-09 22:58:48 +00:00
|
|
|
h.cs = newChunkState(h.key, 0, h.flags)
|
2020-01-12 01:59:28 +00:00
|
|
|
h.used = 0
|
2020-01-09 20:10:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// BlockSize implements hash.Hash.
|
2020-01-09 20:39:50 +00:00
|
|
|
func (h *Hasher) BlockSize() int { return 64 }
|
2020-01-09 20:10:01 +00:00
|
|
|
|
|
|
|
// Size implements hash.Hash.
|
2020-01-09 22:58:48 +00:00
|
|
|
func (h *Hasher) Size() int { return h.size }
|
2020-01-09 20:10:01 +00:00
|
|
|
|
|
|
|
// Write implements hash.Hash.
|
2020-01-09 22:58:48 +00:00
|
|
|
func (h *Hasher) Write(p []byte) (int, error) {
|
|
|
|
lenp := len(p)
|
|
|
|
for len(p) > 0 {
|
2020-01-12 01:09:16 +00:00
|
|
|
// If the current chunk is complete, finalize it and add it to the tree,
|
|
|
|
// then reset the chunk state (but keep incrementing the counter across
|
|
|
|
// chunks).
|
2020-01-12 02:45:09 +00:00
|
|
|
if h.cs.complete() {
|
2020-01-10 03:46:19 +00:00
|
|
|
cv := h.cs.node().chainingValue()
|
2020-01-12 01:59:28 +00:00
|
|
|
h.addChunkChainingValue(cv)
|
|
|
|
h.cs = newChunkState(h.key, h.cs.chunkCounter()+1, h.flags)
|
2020-01-09 20:10:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Compress input bytes into the current chunk state.
|
2020-01-10 21:37:28 +00:00
|
|
|
n := chunkSize - h.cs.bytesConsumed
|
2020-01-09 22:58:48 +00:00
|
|
|
if n > len(p) {
|
|
|
|
n = len(p)
|
2020-01-09 20:10:01 +00:00
|
|
|
}
|
2020-01-09 22:58:48 +00:00
|
|
|
h.cs.update(p[:n])
|
|
|
|
p = p[n:]
|
2020-01-09 20:10:01 +00:00
|
|
|
}
|
2020-01-09 22:58:48 +00:00
|
|
|
return lenp, nil
|
2020-01-09 20:10:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Sum implements hash.Hash.
|
2020-01-12 01:09:16 +00:00
|
|
|
func (h *Hasher) Sum(b []byte) (sum []byte) {
|
|
|
|
// We need to append h.Size() bytes to b. Reuse b's capacity if possible;
|
|
|
|
// otherwise, allocate a new slice.
|
|
|
|
if total := len(b) + h.Size(); cap(b) >= total {
|
|
|
|
sum = b[:total]
|
|
|
|
} else {
|
|
|
|
sum = make([]byte, total)
|
|
|
|
copy(sum, b)
|
|
|
|
}
|
|
|
|
// Read into the appended portion of sum
|
|
|
|
h.XOF().Read(sum[len(b):])
|
|
|
|
return
|
2020-01-09 21:25:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// XOF returns an OutputReader initialized with the current hash state.
|
|
|
|
func (h *Hasher) XOF() *OutputReader {
|
|
|
|
return &OutputReader{
|
2020-01-12 01:09:16 +00:00
|
|
|
n: h.rootNode(),
|
2020-01-09 21:25:24 +00:00
|
|
|
}
|
2020-01-09 20:10:01 +00:00
|
|
|
}
|
|
|
|
|
2020-01-12 02:45:09 +00:00
|
|
|
func newHasher(key [8]uint32, flags uint32, size int) *Hasher {
|
|
|
|
return &Hasher{
|
|
|
|
cs: newChunkState(key, 0, flags),
|
|
|
|
key: key,
|
|
|
|
flags: flags,
|
|
|
|
size: size,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// New returns a Hasher for the specified size and key. If key is nil, the hash
|
|
|
|
// is unkeyed.
|
|
|
|
func New(size int, key []byte) *Hasher {
|
|
|
|
if key == nil {
|
|
|
|
return newHasher(iv, 0, size)
|
|
|
|
}
|
|
|
|
var keyWords [8]uint32
|
2020-01-13 19:27:21 +00:00
|
|
|
for i := range keyWords {
|
|
|
|
keyWords[i] = binary.LittleEndian.Uint32(key[i*4:])
|
|
|
|
}
|
2020-01-12 02:45:09 +00:00
|
|
|
return newHasher(keyWords, flagKeyedHash, size)
|
|
|
|
}
|
|
|
|
|
2020-01-10 03:46:19 +00:00
|
|
|
// Sum256 returns the unkeyed BLAKE3 hash of b, truncated to 256 bits.
|
2020-01-10 22:17:39 +00:00
|
|
|
func Sum256(b []byte) (out [32]byte) {
|
|
|
|
h := newHasher(iv, 0, 0)
|
2020-01-10 03:46:19 +00:00
|
|
|
h.Write(b)
|
2020-01-10 22:17:39 +00:00
|
|
|
h.XOF().Read(out[:])
|
|
|
|
return
|
2020-01-10 03:46:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Sum512 returns the unkeyed BLAKE3 hash of b, truncated to 512 bits.
|
2020-01-10 22:17:39 +00:00
|
|
|
func Sum512(b []byte) (out [64]byte) {
|
|
|
|
h := newHasher(iv, 0, 0)
|
2020-01-10 03:46:19 +00:00
|
|
|
h.Write(b)
|
2020-01-10 22:17:39 +00:00
|
|
|
h.XOF().Read(out[:])
|
|
|
|
return
|
2020-01-10 03:46:19 +00:00
|
|
|
}
|
|
|
|
|
2020-01-12 01:09:16 +00:00
|
|
|
// DeriveKey derives a subkey from ctx and srcKey. ctx should be hardcoded,
|
|
|
|
// globally unique, and application-specific. A good format for ctx strings is:
|
|
|
|
//
|
|
|
|
// [application] [commit timestamp] [purpose]
|
|
|
|
//
|
|
|
|
// e.g.:
|
|
|
|
//
|
|
|
|
// example.com 2019-12-25 16:18:03 session tokens v1
|
|
|
|
//
|
|
|
|
// The purpose of these requirements is to ensure that an attacker cannot trick
|
|
|
|
// two different applications into using the same context string.
|
2020-01-10 05:46:44 +00:00
|
|
|
func DeriveKey(subKey []byte, ctx string, srcKey []byte) {
|
|
|
|
// construct the derivation Hasher
|
|
|
|
const derivationIVLen = 32
|
|
|
|
h := newHasher(iv, flagDeriveKeyContext, 32)
|
|
|
|
h.Write([]byte(ctx))
|
2020-01-13 19:27:21 +00:00
|
|
|
derivationIV := h.Sum(make([]byte, 0, derivationIVLen))
|
|
|
|
var ivWords [8]uint32
|
|
|
|
for i := range ivWords {
|
|
|
|
ivWords[i] = binary.LittleEndian.Uint32(derivationIV[i*4:])
|
|
|
|
}
|
|
|
|
h = newHasher(ivWords, flagDeriveKeyMaterial, 0)
|
2020-01-10 05:46:44 +00:00
|
|
|
// derive the subKey
|
|
|
|
h.Write(srcKey)
|
2020-01-12 01:09:16 +00:00
|
|
|
h.XOF().Read(subKey)
|
2020-01-10 05:46:44 +00:00
|
|
|
}
|
|
|
|
|
2020-01-12 02:45:09 +00:00
|
|
|
// An OutputReader produces an seekable stream of 2^64 - 1 pseudorandom output
|
|
|
|
// bytes.
|
|
|
|
type OutputReader struct {
|
|
|
|
n node
|
|
|
|
block [blockSize]byte
|
|
|
|
off uint64
|
|
|
|
}
|
|
|
|
|
|
|
|
// Read implements io.Reader. Callers may assume that Read returns len(p), nil
|
|
|
|
// unless the read would extend beyond the end of the stream.
|
|
|
|
func (or *OutputReader) Read(p []byte) (int, error) {
|
|
|
|
if or.off == math.MaxUint64 {
|
|
|
|
return 0, io.EOF
|
|
|
|
} else if rem := math.MaxUint64 - or.off; uint64(len(p)) > rem {
|
|
|
|
p = p[:rem]
|
|
|
|
}
|
|
|
|
lenp := len(p)
|
|
|
|
for len(p) > 0 {
|
|
|
|
if or.off%blockSize == 0 {
|
|
|
|
or.n.counter = or.off / blockSize
|
|
|
|
words := or.n.compress()
|
2020-01-13 19:27:21 +00:00
|
|
|
wordsToBytes(words, &or.block)
|
2020-01-12 02:45:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
n := copy(p, or.block[or.off%blockSize:])
|
|
|
|
p = p[n:]
|
|
|
|
or.off += uint64(n)
|
|
|
|
}
|
|
|
|
return lenp, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Seek implements io.Seeker.
|
|
|
|
func (or *OutputReader) Seek(offset int64, whence int) (int64, error) {
|
|
|
|
off := or.off
|
|
|
|
switch whence {
|
|
|
|
case io.SeekStart:
|
|
|
|
if offset < 0 {
|
|
|
|
return 0, errors.New("seek position cannot be negative")
|
|
|
|
}
|
|
|
|
off = uint64(offset)
|
|
|
|
case io.SeekCurrent:
|
|
|
|
if offset < 0 {
|
|
|
|
if uint64(-offset) > off {
|
|
|
|
return 0, errors.New("seek position cannot be negative")
|
|
|
|
}
|
|
|
|
off -= uint64(-offset)
|
|
|
|
} else {
|
|
|
|
off += uint64(offset)
|
|
|
|
}
|
|
|
|
case io.SeekEnd:
|
|
|
|
off = uint64(offset) - 1
|
|
|
|
default:
|
|
|
|
panic("invalid whence")
|
|
|
|
}
|
|
|
|
or.off = off
|
|
|
|
or.n.counter = uint64(off) / blockSize
|
|
|
|
if or.off%blockSize != 0 {
|
|
|
|
words := or.n.compress()
|
2020-01-13 19:27:21 +00:00
|
|
|
wordsToBytes(words, &or.block)
|
2020-01-12 02:45:09 +00:00
|
|
|
}
|
|
|
|
// NOTE: or.off >= 2^63 will result in a negative return value.
|
|
|
|
// Nothing we can do about this.
|
|
|
|
return int64(or.off), nil
|
|
|
|
}
|
|
|
|
|
2020-01-09 20:10:01 +00:00
|
|
|
// ensure that Hasher implements hash.Hash
|
|
|
|
var _ hash.Hash = (*Hasher)(nil)
|