| 
									
										
										
										
											2021-08-12 21:03:24 +02:00
										 |  |  | // Copyright 2017 The Go Authors. All rights reserved. | 
					
						
							|  |  |  | // Use of this source code is governed by a BSD-style | 
					
						
							|  |  |  | // license that can be found in the LICENSE file. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | package blake2b | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"encoding/binary" | 
					
						
							|  |  |  | 	"errors" | 
					
						
							|  |  |  | 	"io" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // XOF defines the interface to hash functions that | 
					
						
							|  |  |  | // support arbitrary-length output. | 
					
						
							| 
									
										
										
										
											2025-07-18 11:56:40 +02:00
										 |  |  | // | 
					
						
							|  |  |  | // New callers should prefer the standard library [hash.XOF]. | 
					
						
							| 
									
										
										
										
											2021-08-12 21:03:24 +02:00
										 |  |  | type XOF interface { | 
					
						
							|  |  |  | 	// Write absorbs more data into the hash's state. It panics if called | 
					
						
							|  |  |  | 	// after Read. | 
					
						
							|  |  |  | 	io.Writer | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Read reads more output from the hash. It returns io.EOF if the limit | 
					
						
							|  |  |  | 	// has been reached. | 
					
						
							|  |  |  | 	io.Reader | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Clone returns a copy of the XOF in its current state. | 
					
						
							|  |  |  | 	Clone() XOF | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Reset resets the XOF to its initial state. | 
					
						
							|  |  |  | 	Reset() | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // OutputLengthUnknown can be used as the size argument to NewXOF to indicate | 
					
						
							|  |  |  | // the length of the output is not known in advance. | 
					
						
							|  |  |  | const OutputLengthUnknown = 0 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // magicUnknownOutputLength is a magic value for the output size that indicates | 
					
						
							|  |  |  | // an unknown number of output bytes. | 
					
						
							|  |  |  | const magicUnknownOutputLength = (1 << 32) - 1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // maxOutputLength is the absolute maximum number of bytes to produce when the | 
					
						
							|  |  |  | // number of output bytes is unknown. | 
					
						
							|  |  |  | const maxOutputLength = (1 << 32) * 64 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // NewXOF creates a new variable-output-length hash. The hash either produce a | 
					
						
							|  |  |  | // known number of bytes (1 <= size < 2**32-1), or an unknown number of bytes | 
					
						
							|  |  |  | // (size == OutputLengthUnknown). In the latter case, an absolute limit of | 
					
						
							|  |  |  | // 256GiB applies. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // A non-nil key turns the hash into a MAC. The key must between | 
					
						
							|  |  |  | // zero and 32 bytes long. | 
					
						
							| 
									
										
										
										
											2025-07-18 11:56:40 +02:00
										 |  |  | // | 
					
						
							|  |  |  | // The result can be safely interface-upgraded to [hash.XOF]. | 
					
						
							| 
									
										
										
										
											2021-08-12 21:03:24 +02:00
										 |  |  | func NewXOF(size uint32, key []byte) (XOF, error) { | 
					
						
							|  |  |  | 	if len(key) > Size { | 
					
						
							|  |  |  | 		return nil, errKeySize | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if size == magicUnknownOutputLength { | 
					
						
							|  |  |  | 		// 2^32-1 indicates an unknown number of bytes and thus isn't a | 
					
						
							|  |  |  | 		// valid length. | 
					
						
							|  |  |  | 		return nil, errors.New("blake2b: XOF length too large") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if size == OutputLengthUnknown { | 
					
						
							|  |  |  | 		size = magicUnknownOutputLength | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	x := &xof{ | 
					
						
							|  |  |  | 		d: digest{ | 
					
						
							|  |  |  | 			size:   Size, | 
					
						
							|  |  |  | 			keyLen: len(key), | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		length: size, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	copy(x.d.key[:], key) | 
					
						
							|  |  |  | 	x.Reset() | 
					
						
							|  |  |  | 	return x, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type xof struct { | 
					
						
							|  |  |  | 	d                digest | 
					
						
							|  |  |  | 	length           uint32 | 
					
						
							|  |  |  | 	remaining        uint64 | 
					
						
							|  |  |  | 	cfg, root, block [Size]byte | 
					
						
							|  |  |  | 	offset           int | 
					
						
							|  |  |  | 	nodeOffset       uint32 | 
					
						
							|  |  |  | 	readMode         bool | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (x *xof) Write(p []byte) (n int, err error) { | 
					
						
							|  |  |  | 	if x.readMode { | 
					
						
							|  |  |  | 		panic("blake2b: write to XOF after read") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return x.d.Write(p) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (x *xof) Clone() XOF { | 
					
						
							|  |  |  | 	clone := *x | 
					
						
							|  |  |  | 	return &clone | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-18 11:56:40 +02:00
										 |  |  | func (x *xof) BlockSize() int { | 
					
						
							|  |  |  | 	return x.d.BlockSize() | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-12 21:03:24 +02:00
										 |  |  | func (x *xof) Reset() { | 
					
						
							|  |  |  | 	x.cfg[0] = byte(Size) | 
					
						
							|  |  |  | 	binary.LittleEndian.PutUint32(x.cfg[4:], uint32(Size)) // leaf length | 
					
						
							|  |  |  | 	binary.LittleEndian.PutUint32(x.cfg[12:], x.length)    // XOF length | 
					
						
							|  |  |  | 	x.cfg[17] = byte(Size)                                 // inner hash size | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	x.d.Reset() | 
					
						
							|  |  |  | 	x.d.h[1] ^= uint64(x.length) << 32 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	x.remaining = uint64(x.length) | 
					
						
							|  |  |  | 	if x.remaining == magicUnknownOutputLength { | 
					
						
							|  |  |  | 		x.remaining = maxOutputLength | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	x.offset, x.nodeOffset = 0, 0 | 
					
						
							|  |  |  | 	x.readMode = false | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (x *xof) Read(p []byte) (n int, err error) { | 
					
						
							|  |  |  | 	if !x.readMode { | 
					
						
							|  |  |  | 		x.d.finalize(&x.root) | 
					
						
							|  |  |  | 		x.readMode = true | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if x.remaining == 0 { | 
					
						
							|  |  |  | 		return 0, io.EOF | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	n = len(p) | 
					
						
							|  |  |  | 	if uint64(n) > x.remaining { | 
					
						
							|  |  |  | 		n = int(x.remaining) | 
					
						
							|  |  |  | 		p = p[:n] | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if x.offset > 0 { | 
					
						
							|  |  |  | 		blockRemaining := Size - x.offset | 
					
						
							|  |  |  | 		if n < blockRemaining { | 
					
						
							|  |  |  | 			x.offset += copy(p, x.block[x.offset:]) | 
					
						
							|  |  |  | 			x.remaining -= uint64(n) | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		copy(p, x.block[x.offset:]) | 
					
						
							|  |  |  | 		p = p[blockRemaining:] | 
					
						
							|  |  |  | 		x.offset = 0 | 
					
						
							|  |  |  | 		x.remaining -= uint64(blockRemaining) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for len(p) >= Size { | 
					
						
							|  |  |  | 		binary.LittleEndian.PutUint32(x.cfg[8:], x.nodeOffset) | 
					
						
							|  |  |  | 		x.nodeOffset++ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		x.d.initConfig(&x.cfg) | 
					
						
							|  |  |  | 		x.d.Write(x.root[:]) | 
					
						
							|  |  |  | 		x.d.finalize(&x.block) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		copy(p, x.block[:]) | 
					
						
							|  |  |  | 		p = p[Size:] | 
					
						
							|  |  |  | 		x.remaining -= uint64(Size) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if todo := len(p); todo > 0 { | 
					
						
							|  |  |  | 		if x.remaining < uint64(Size) { | 
					
						
							|  |  |  | 			x.cfg[0] = byte(x.remaining) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		binary.LittleEndian.PutUint32(x.cfg[8:], x.nodeOffset) | 
					
						
							|  |  |  | 		x.nodeOffset++ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		x.d.initConfig(&x.cfg) | 
					
						
							|  |  |  | 		x.d.Write(x.root[:]) | 
					
						
							|  |  |  | 		x.d.finalize(&x.block) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		x.offset = copy(p, x.block[:todo]) | 
					
						
							|  |  |  | 		x.remaining -= uint64(todo) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (d *digest) initConfig(cfg *[Size]byte) { | 
					
						
							|  |  |  | 	d.offset, d.c[0], d.c[1] = 0, 0, 0 | 
					
						
							|  |  |  | 	for i := range d.h { | 
					
						
							|  |  |  | 		d.h[i] = iv[i] ^ binary.LittleEndian.Uint64(cfg[i*8:]) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } |