github.com/kaleido-io/go-ethereum@v1.9.7/crypto/blake2b/blake2x.go (about)

     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 blake2b
     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 = (1 << 32) - 1
    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) * 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.
    50  func 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  
    74  type 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  
    84  func (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  
    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.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  
   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  	return
   170  }
   171  
   172  func (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  }