github.com/vegaprotocol/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 }