github.com/core-coin/go-core/v2@v2.1.9/crypto/blake2b/blake2x.go (about)

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