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