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 }