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 }