1// Copyright 2017 The Go Authors. All rights reserved.
  2// Use of this source code is governed by a BSD-style
  3// license that can be found in the LICENSE file.
  4
  5package blake2b
  6
  7import (
  8	"encoding/binary"
  9	"errors"
 10	"io"
 11)
 12
 13// XOF defines the interface to hash functions that
 14// support arbitrary-length output.
 15//
 16// New callers should prefer the standard library [hash.XOF].
 17type XOF interface {
 18	// Write absorbs more data into the hash's state. It panics if called
 19	// after Read.
 20	io.Writer
 21
 22	// Read reads more output from the hash. It returns io.EOF if the limit
 23	// has been reached.
 24	io.Reader
 25
 26	// Clone returns a copy of the XOF in its current state.
 27	Clone() XOF
 28
 29	// Reset resets the XOF to its initial state.
 30	Reset()
 31}
 32
 33// OutputLengthUnknown can be used as the size argument to NewXOF to indicate
 34// the length of the output is not known in advance.
 35const OutputLengthUnknown = 0
 36
 37// magicUnknownOutputLength is a magic value for the output size that indicates
 38// an unknown number of output bytes.
 39const magicUnknownOutputLength = (1 << 32) - 1
 40
 41// maxOutputLength is the absolute maximum number of bytes to produce when the
 42// number of output bytes is unknown.
 43const maxOutputLength = (1 << 32) * 64
 44
 45// NewXOF creates a new variable-output-length hash. The hash either produce a
 46// known number of bytes (1 <= size < 2**32-1), or an unknown number of bytes
 47// (size == OutputLengthUnknown). In the latter case, an absolute limit of
 48// 256GiB applies.
 49//
 50// A non-nil key turns the hash into a MAC. The key must between
 51// zero and 32 bytes long.
 52//
 53// The result can be safely interface-upgraded to [hash.XOF].
 54func NewXOF(size uint32, key []byte) (XOF, error) {
 55	if len(key) > Size {
 56		return nil, errKeySize
 57	}
 58	if size == magicUnknownOutputLength {
 59		// 2^32-1 indicates an unknown number of bytes and thus isn't a
 60		// valid length.
 61		return nil, errors.New("blake2b: XOF length too large")
 62	}
 63	if size == OutputLengthUnknown {
 64		size = magicUnknownOutputLength
 65	}
 66	x := &xof{
 67		d: digest{
 68			size:   Size,
 69			keyLen: len(key),
 70		},
 71		length: size,
 72	}
 73	copy(x.d.key[:], key)
 74	x.Reset()
 75	return x, nil
 76}
 77
 78type xof struct {
 79	d                digest
 80	length           uint32
 81	remaining        uint64
 82	cfg, root, block [Size]byte
 83	offset           int
 84	nodeOffset       uint32
 85	readMode         bool
 86}
 87
 88func (x *xof) Write(p []byte) (n int, err error) {
 89	if x.readMode {
 90		panic("blake2b: write to XOF after read")
 91	}
 92	return x.d.Write(p)
 93}
 94
 95func (x *xof) Clone() XOF {
 96	clone := *x
 97	return &clone
 98}
 99
100func (x *xof) BlockSize() int {
101	return x.d.BlockSize()
102}
103
104func (x *xof) Reset() {
105	x.cfg[0] = byte(Size)
106	binary.LittleEndian.PutUint32(x.cfg[4:], uint32(Size)) // leaf length
107	binary.LittleEndian.PutUint32(x.cfg[12:], x.length)    // XOF length
108	x.cfg[17] = byte(Size)                                 // inner hash size
109
110	x.d.Reset()
111	x.d.h[1] ^= uint64(x.length) << 32
112
113	x.remaining = uint64(x.length)
114	if x.remaining == magicUnknownOutputLength {
115		x.remaining = maxOutputLength
116	}
117	x.offset, x.nodeOffset = 0, 0
118	x.readMode = false
119}
120
121func (x *xof) Read(p []byte) (n int, err error) {
122	if !x.readMode {
123		x.d.finalize(&x.root)
124		x.readMode = true
125	}
126
127	if x.remaining == 0 {
128		return 0, io.EOF
129	}
130
131	n = len(p)
132	if uint64(n) > x.remaining {
133		n = int(x.remaining)
134		p = p[:n]
135	}
136
137	if x.offset > 0 {
138		blockRemaining := Size - x.offset
139		if n < blockRemaining {
140			x.offset += copy(p, x.block[x.offset:])
141			x.remaining -= uint64(n)
142			return
143		}
144		copy(p, x.block[x.offset:])
145		p = p[blockRemaining:]
146		x.offset = 0
147		x.remaining -= uint64(blockRemaining)
148	}
149
150	for len(p) >= Size {
151		binary.LittleEndian.PutUint32(x.cfg[8:], x.nodeOffset)
152		x.nodeOffset++
153
154		x.d.initConfig(&x.cfg)
155		x.d.Write(x.root[:])
156		x.d.finalize(&x.block)
157
158		copy(p, x.block[:])
159		p = p[Size:]
160		x.remaining -= uint64(Size)
161	}
162
163	if todo := len(p); todo > 0 {
164		if x.remaining < uint64(Size) {
165			x.cfg[0] = byte(x.remaining)
166		}
167		binary.LittleEndian.PutUint32(x.cfg[8:], x.nodeOffset)
168		x.nodeOffset++
169
170		x.d.initConfig(&x.cfg)
171		x.d.Write(x.root[:])
172		x.d.finalize(&x.block)
173
174		x.offset = copy(p, x.block[:todo])
175		x.remaining -= uint64(todo)
176	}
177	return
178}
179
180func (d *digest) initConfig(cfg *[Size]byte) {
181	d.offset, d.c[0], d.c[1] = 0, 0, 0
182	for i := range d.h {
183		d.h[i] = iv[i] ^ binary.LittleEndian.Uint64(cfg[i*8:])
184	}
185}