OSDN Git Service

new repo
[bytom/vapor.git] / vendor / golang.org / x / crypto / blake2b / blake2x.go
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 package blake2b
6
7 import (
8         "encoding/binary"
9         "errors"
10         "io"
11 )
12
13 // XOF defines the interface to hash functions that
14 // support arbitrary-length output.
15 type XOF interface {
16         // Write absorbs more data into the hash's state. It panics if called
17         // after Read.
18         io.Writer
19
20         // Read reads more output from the hash. It returns io.EOF if the limit
21         // has been reached.
22         io.Reader
23
24         // Clone returns a copy of the XOF in its current state.
25         Clone() XOF
26
27         // Reset resets the XOF to its initial state.
28         Reset()
29 }
30
31 // OutputLengthUnknown can be used as the size argument to NewXOF to indicate
32 // the the length of the output is not known in advance.
33 const OutputLengthUnknown = 0
34
35 // magicUnknownOutputLength is a magic value for the output size that indicates
36 // an unknown number of output bytes.
37 const magicUnknownOutputLength = (1 << 32) - 1
38
39 // maxOutputLength is the absolute maximum number of bytes to produce when the
40 // number of output bytes is unknown.
41 const maxOutputLength = (1 << 32) * 64
42
43 // NewXOF creates a new variable-output-length hash. The hash either produce a
44 // known number of bytes (1 <= size < 2**32-1), or an unknown number of bytes
45 // (size == OutputLengthUnknown). In the latter case, an absolute limit of
46 // 256GiB applies.
47 //
48 // A non-nil key turns the hash into a MAC. The key must between
49 // zero and 32 bytes long.
50 func NewXOF(size uint32, key []byte) (XOF, error) {
51         if len(key) > Size {
52                 return nil, errKeySize
53         }
54         if size == magicUnknownOutputLength {
55                 // 2^32-1 indicates an unknown number of bytes and thus isn't a
56                 // valid length.
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)) // leaf length
99         binary.LittleEndian.PutUint32(x.cfg[12:], x.length)    // XOF length
100         x.cfg[17] = byte(Size)                                 // inner hash 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 }