package blake2b
import (
)
type XOF interface {
io.Writer
io.Reader
Clone() XOF
Reset()
}
const OutputLengthUnknown = 0
const magicUnknownOutputLength = (1 << 32) - 1
const maxOutputLength = (1 << 32) * 64
func ( uint32, []byte) (XOF, error) {
if len() > Size {
return nil, errKeySize
}
if == magicUnknownOutputLength {
return nil, errors.New("blake2b: XOF length too large")
}
if == OutputLengthUnknown {
= magicUnknownOutputLength
}
:= &xof{
d: digest{
size: Size,
keyLen: len(),
},
length: ,
}
copy(.d.key[:], )
.Reset()
return , nil
}
type xof struct {
d digest
length uint32
remaining uint64
cfg, root, block [Size]byte
offset int
nodeOffset uint32
readMode bool
}
func ( *xof) ( []byte) ( int, error) {
if .readMode {
panic("blake2b: write to XOF after read")
}
return .d.Write()
}
func ( *xof) () XOF {
:= *
return &
}
func ( *xof) () {
.cfg[0] = byte(Size)
binary.LittleEndian.PutUint32(.cfg[4:], uint32(Size))
binary.LittleEndian.PutUint32(.cfg[12:], .length)
.cfg[17] = byte(Size)
.d.Reset()
.d.h[1] ^= uint64(.length) << 32
.remaining = uint64(.length)
if .remaining == magicUnknownOutputLength {
.remaining = maxOutputLength
}
.offset, .nodeOffset = 0, 0
.readMode = false
}
func ( *xof) ( []byte) ( int, error) {
if !.readMode {
.d.finalize(&.root)
.readMode = true
}
if .remaining == 0 {
return 0, io.EOF
}
= len()
if uint64() > .remaining {
= int(.remaining)
= [:]
}
if .offset > 0 {
:= Size - .offset
if < {
.offset += copy(, .block[.offset:])
.remaining -= uint64()
return
}
copy(, .block[.offset:])
= [:]
.offset = 0
.remaining -= uint64()
}
for len() >= Size {
binary.LittleEndian.PutUint32(.cfg[8:], .nodeOffset)
.nodeOffset++
.d.initConfig(&.cfg)
.d.Write(.root[:])
.d.finalize(&.block)
copy(, .block[:])
= [Size:]
.remaining -= uint64(Size)
}
if := len(); > 0 {
if .remaining < uint64(Size) {
.cfg[0] = byte(.remaining)
}
binary.LittleEndian.PutUint32(.cfg[8:], .nodeOffset)
.nodeOffset++
.d.initConfig(&.cfg)
.d.Write(.root[:])
.d.finalize(&.block)
.offset = copy(, .block[:])
.remaining -= uint64()
}
return
}
func ( *digest) ( *[Size]byte) {
.offset, .c[0], .c[1] = 0, 0, 0
for := range .h {
.h[] = iv[] ^ binary.LittleEndian.Uint64([*8:])
}
}