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