forked from toolshed/abra
		
	
		
			
				
	
	
		
			186 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			186 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // 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.
 | |
| //
 | |
| // New callers should prefer the standard library [hash.XOF].
 | |
| 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.
 | |
| //
 | |
| // The result can be safely interface-upgraded to [hash.XOF].
 | |
| 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
 | |
| }
 | |
| 
 | |
| func (x *xof) BlockSize() int {
 | |
| 	return x.d.BlockSize()
 | |
| }
 | |
| 
 | |
| 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:])
 | |
| 	}
 | |
| }
 |