blake2x.go raw

   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  
   5  package blake2s
   6  
   7  import (
   8  	"encoding/binary"
   9  	"errors"
  10  	"io"
  11  )
  12  
  13  // XOF defines the interface to hash functions that
  14  // support arbitrary-length output.
  15  type 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.
  33  const OutputLengthUnknown = 0
  34  
  35  // magicUnknownOutputLength is a magic value for the output size that indicates
  36  // an unknown number of output bytes.
  37  const magicUnknownOutputLength = 65535
  38  
  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
  42  
  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
  46  // 128GiB applies.
  47  //
  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) {
  51  	if len(key) > Size {
  52  		return nil, errKeySize
  53  	}
  54  	if size == magicUnknownOutputLength {
  55  		// 2^16-1 indicates an unknown number of bytes and thus isn't a
  56  		// valid length.
  57  		return nil, errors.New("blake2s: 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  
  74  type xof struct {
  75  	d                digest
  76  	length           uint16
  77  	remaining        uint64
  78  	cfg, root, block [Size]byte
  79  	offset           int
  80  	nodeOffset       uint32
  81  	readMode         bool
  82  }
  83  
  84  func (x *xof) Write(p []byte) (n int, err error) {
  85  	if x.readMode {
  86  		panic("blake2s: write to XOF after read")
  87  	}
  88  	return x.d.Write(p)
  89  }
  90  
  91  func (x *xof) Clone() XOF {
  92  	clone := *x
  93  	return &clone
  94  }
  95  
  96  func (x *xof) Reset() {
  97  	x.cfg[0] = byte(Size)
  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
 101  
 102  	x.d.Reset()
 103  	x.d.h[3] ^= uint32(x.length)
 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  
 113  func (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  
 170  	return
 171  }
 172  
 173  func (d *digest) initConfig(cfg *[Size]byte) {
 174  	d.offset, d.c[0], d.c[1] = 0, 0, 0
 175  	for i := range d.h {
 176  		d.h[i] = iv[i] ^ binary.LittleEndian.Uint32(cfg[i*4:])
 177  	}
 178  }
 179