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.
13 // XOF defines the interface to hash functions that
14 // support arbitrary-length output.
16 // Write absorbs more data into the hash's state. It panics if called
20 // Read reads more output from the hash. It returns io.EOF if the limit
24 // Clone returns a copy of the XOF in its current state.
27 // Reset resets the XOF to its initial state.
31 // OutputLengthUnknown can be used as the size argument to NewXOF to indicate
32 // the the length of the output is not known in advance.
33 const OutputLengthUnknown = 0
35 // magicUnknownOutputLength is a magic value for the output size that indicates
36 // an unknown number of output bytes.
37 const magicUnknownOutputLength = 65535
39 // maxOutputLength is the absolute maximum number of bytes to produce when the
40 // number of output bytes is unknown.
41 const maxOutputLength = (1 << 32) * 32
43 // NewXOF creates a new variable-output-length hash. The hash either produce a
44 // known number of bytes (1 <= size < 65535), or an unknown number of bytes
45 // (size == OutputLengthUnknown). In the latter case, an absolute limit of
48 // A non-nil key turns the hash into a MAC. The key must between
49 // zero and 32 bytes long.
50 func NewXOF(size uint16, key []byte) (XOF, error) {
52 return nil, errKeySize
54 if size == magicUnknownOutputLength {
55 // 2^16-1 indicates an unknown number of bytes and thus isn't a
57 return nil, errors.New("blake2s: XOF length too large")
59 if size == OutputLengthUnknown {
60 size = magicUnknownOutputLength
78 cfg, root, block [Size]byte
84 func (x *xof) Write(p []byte) (n int, err error) {
86 panic("blake2s: write to XOF after read")
91 func (x *xof) Clone() XOF {
96 func (x *xof) Reset() {
98 binary.LittleEndian.PutUint32(x.cfg[4:], uint32(Size)) // leaf length
99 binary.LittleEndian.PutUint16(x.cfg[12:], x.length) // XOF length
100 x.cfg[15] = byte(Size) // inner hash size
103 x.d.h[3] ^= uint32(x.length)
105 x.remaining = uint64(x.length)
106 if x.remaining == magicUnknownOutputLength {
107 x.remaining = maxOutputLength
109 x.offset, x.nodeOffset = 0, 0
113 func (x *xof) Read(p []byte) (n int, err error) {
115 x.d.finalize(&x.root)
119 if x.remaining == 0 {
124 if uint64(n) > x.remaining {
130 blockRemaining := Size - x.offset
131 if n < blockRemaining {
132 x.offset += copy(p, x.block[x.offset:])
133 x.remaining -= uint64(n)
136 copy(p, x.block[x.offset:])
137 p = p[blockRemaining:]
139 x.remaining -= uint64(blockRemaining)
143 binary.LittleEndian.PutUint32(x.cfg[8:], x.nodeOffset)
146 x.d.initConfig(&x.cfg)
148 x.d.finalize(&x.block)
152 x.remaining -= uint64(Size)
155 if todo := len(p); todo > 0 {
156 if x.remaining < uint64(Size) {
157 x.cfg[0] = byte(x.remaining)
159 binary.LittleEndian.PutUint32(x.cfg[8:], x.nodeOffset)
162 x.d.initConfig(&x.cfg)
164 x.d.finalize(&x.block)
166 x.offset = copy(p, x.block[:todo])
167 x.remaining -= uint64(todo)
173 func (d *digest) initConfig(cfg *[Size]byte) {
174 d.offset, d.c[0], d.c[1] = 0, 0, 0
176 d.h[i] = iv[i] ^ binary.LittleEndian.Uint32(cfg[i*4:])