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 }