...
1
2
3
4
5 package blake2b
6
7 import (
8 "encoding/binary"
9 "errors"
10 "io"
11 )
12
13
14
15 type XOF interface {
16
17
18 io.Writer
19
20
21
22 io.Reader
23
24
25 Clone() XOF
26
27
28 Reset()
29 }
30
31
32
33 const OutputLengthUnknown = 0
34
35
36
37 const magicUnknownOutputLength = (1 << 32) - 1
38
39
40
41 const maxOutputLength = (1 << 32) * 64
42
43
44
45
46
47
48
49
50 func NewXOF(size uint32, key []byte) (XOF, error) {
51 if len(key) > Size {
52 return nil, errKeySize
53 }
54 if size == magicUnknownOutputLength {
55
56
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))
99 binary.LittleEndian.PutUint32(x.cfg[12:], x.length)
100 x.cfg[17] = byte(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 }
178
View as plain text