github.com/aigarnetwork/aigar@v0.0.0-20191115204914-d59a6eb70f8e/crypto/blake2b/blake2x.go (about)

     1  //  Copyright 2018 The go-ethereum Authors
     2  //  Copyright 2019 The go-aigar Authors
     3  //  This file is part of the go-aigar library.
     4  //
     5  //  The go-aigar library is free software: you can redistribute it and/or modify
     6  //  it under the terms of the GNU Lesser General Public License as published by
     7  //  the Free Software Foundation, either version 3 of the License, or
     8  //  (at your option) any later version.
     9  //
    10  //  The go-aigar library is distributed in the hope that it will be useful,
    11  //  but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    13  //  GNU Lesser General Public License for more details.
    14  //
    15  //  You should have received a copy of the GNU Lesser General Public License
    16  //  along with the go-aigar library. If not, see <http://www.gnu.org/licenses/>.
    17  
    18  package blake2b
    19  
    20  import (
    21  	"encoding/binary"
    22  	"errors"
    23  	"io"
    24  )
    25  
    26  // XOF defines the interface to hash functions that
    27  // support arbitrary-length output.
    28  type XOF interface {
    29  	// Write absorbs more data into the hash's state. It panics if called
    30  	// after Read.
    31  	io.Writer
    32  
    33  	// Read reads more output from the hash. It returns io.EOF if the limit
    34  	// has been reached.
    35  	io.Reader
    36  
    37  	// Clone returns a copy of the XOF in its current state.
    38  	Clone() XOF
    39  
    40  	// Reset resets the XOF to its initial state.
    41  	Reset()
    42  }
    43  
    44  // OutputLengthUnknown can be used as the size argument to NewXOF to indicate
    45  // the length of the output is not known in advance.
    46  const OutputLengthUnknown = 0
    47  
    48  // magicUnknownOutputLength is a magic value for the output size that indicates
    49  // an unknown number of output bytes.
    50  const magicUnknownOutputLength = (1 << 32) - 1
    51  
    52  // maxOutputLength is the absolute maximum number of bytes to produce when the
    53  // number of output bytes is unknown.
    54  const maxOutputLength = (1 << 32) * 64
    55  
    56  // NewXOF creates a new variable-output-length hash. The hash either produce a
    57  // known number of bytes (1 <= size < 2**32-1), or an unknown number of bytes
    58  // (size == OutputLengthUnknown). In the latter case, an absolute limit of
    59  // 256GiB applies.
    60  //
    61  // A non-nil key turns the hash into a MAC. The key must between
    62  // zero and 32 bytes long.
    63  func NewXOF(size uint32, key []byte) (XOF, error) {
    64  	if len(key) > Size {
    65  		return nil, errKeySize
    66  	}
    67  	if size == magicUnknownOutputLength {
    68  		// 2^32-1 indicates an unknown number of bytes and thus isn't a
    69  		// valid length.
    70  		return nil, errors.New("blake2b: XOF length too large")
    71  	}
    72  	if size == OutputLengthUnknown {
    73  		size = magicUnknownOutputLength
    74  	}
    75  	x := &xof{
    76  		d: digest{
    77  			size:   Size,
    78  			keyLen: len(key),
    79  		},
    80  		length: size,
    81  	}
    82  	copy(x.d.key[:], key)
    83  	x.Reset()
    84  	return x, nil
    85  }
    86  
    87  type xof struct {
    88  	d                digest
    89  	length           uint32
    90  	remaining        uint64
    91  	cfg, root, block [Size]byte
    92  	offset           int
    93  	nodeOffset       uint32
    94  	readMode         bool
    95  }
    96  
    97  func (x *xof) Write(p []byte) (n int, err error) {
    98  	if x.readMode {
    99  		panic("blake2b: write to XOF after read")
   100  	}
   101  	return x.d.Write(p)
   102  }
   103  
   104  func (x *xof) Clone() XOF {
   105  	clone := *x
   106  	return &clone
   107  }
   108  
   109  func (x *xof) Reset() {
   110  	x.cfg[0] = byte(Size)
   111  	binary.LittleEndian.PutUint32(x.cfg[4:], uint32(Size)) // leaf length
   112  	binary.LittleEndian.PutUint32(x.cfg[12:], x.length)    // XOF length
   113  	x.cfg[17] = byte(Size)                                 // inner hash size
   114  
   115  	x.d.Reset()
   116  	x.d.h[1] ^= uint64(x.length) << 32
   117  
   118  	x.remaining = uint64(x.length)
   119  	if x.remaining == magicUnknownOutputLength {
   120  		x.remaining = maxOutputLength
   121  	}
   122  	x.offset, x.nodeOffset = 0, 0
   123  	x.readMode = false
   124  }
   125  
   126  func (x *xof) Read(p []byte) (n int, err error) {
   127  	if !x.readMode {
   128  		x.d.finalize(&x.root)
   129  		x.readMode = true
   130  	}
   131  
   132  	if x.remaining == 0 {
   133  		return 0, io.EOF
   134  	}
   135  
   136  	n = len(p)
   137  	if uint64(n) > x.remaining {
   138  		n = int(x.remaining)
   139  		p = p[:n]
   140  	}
   141  
   142  	if x.offset > 0 {
   143  		blockRemaining := Size - x.offset
   144  		if n < blockRemaining {
   145  			x.offset += copy(p, x.block[x.offset:])
   146  			x.remaining -= uint64(n)
   147  			return
   148  		}
   149  		copy(p, x.block[x.offset:])
   150  		p = p[blockRemaining:]
   151  		x.offset = 0
   152  		x.remaining -= uint64(blockRemaining)
   153  	}
   154  
   155  	for len(p) >= Size {
   156  		binary.LittleEndian.PutUint32(x.cfg[8:], x.nodeOffset)
   157  		x.nodeOffset++
   158  
   159  		x.d.initConfig(&x.cfg)
   160  		x.d.Write(x.root[:])
   161  		x.d.finalize(&x.block)
   162  
   163  		copy(p, x.block[:])
   164  		p = p[Size:]
   165  		x.remaining -= uint64(Size)
   166  	}
   167  
   168  	if todo := len(p); todo > 0 {
   169  		if x.remaining < uint64(Size) {
   170  			x.cfg[0] = byte(x.remaining)
   171  		}
   172  		binary.LittleEndian.PutUint32(x.cfg[8:], x.nodeOffset)
   173  		x.nodeOffset++
   174  
   175  		x.d.initConfig(&x.cfg)
   176  		x.d.Write(x.root[:])
   177  		x.d.finalize(&x.block)
   178  
   179  		x.offset = copy(p, x.block[:todo])
   180  		x.remaining -= uint64(todo)
   181  	}
   182  	return
   183  }
   184  
   185  func (d *digest) initConfig(cfg *[Size]byte) {
   186  	d.offset, d.c[0], d.c[1] = 0, 0, 0
   187  	for i := range d.h {
   188  		d.h[i] = iv[i] ^ binary.LittleEndian.Uint64(cfg[i*8:])
   189  	}
   190  }