sha3: avoid buffer copy · golang/crypto@c17aa50 (original) (raw)
`@@ -4,6 +4,14 @@
`
4
4
``
5
5
`package sha3
`
6
6
``
``
7
`+
import (
`
``
8
`+
"crypto/subtle"
`
``
9
`+
"encoding/binary"
`
``
10
`+
"unsafe"
`
``
11
+
``
12
`+
"golang.org/x/sys/cpu"
`
``
13
`+
)
`
``
14
+
7
15
`// spongeDirection indicates the direction bytes are flowing through the sponge.
`
8
16
`type spongeDirection int
`
9
17
``
`@@ -14,16 +22,13 @@ const (
`
14
22
`spongeSqueezing
`
15
23
`)
`
16
24
``
17
``
`-
const (
`
18
``
`-
// maxRate is the maximum size of the internal buffer. SHAKE-256
`
19
``
`-
// currently needs the largest buffer.
`
20
``
`-
maxRate = 168
`
21
``
`-
)
`
22
``
-
23
25
`type state struct {
`
24
``
`-
// Generic sponge components.
`
25
``
`-
a [25]uint64 // main state of the hash
`
26
``
`-
rate int // the number of bytes of state to use
`
``
26
`+
a [1600 / 8]byte // main state of the hash
`
``
27
+
``
28
`+
// a[n:rate] is the buffer. If absorbing, it's the remaining space to XOR
`
``
29
`+
// into before running the permutation. If squeezing, it's the remaining
`
``
30
`+
// output to produce before running the permutation.
`
``
31
`+
n, rate int
`
27
32
``
28
33
`// dsbyte contains the "domain separation" bits and the first bit of
`
29
34
`// the padding. Sections 6.1 and 6.2 of [1] separate the outputs of the
`
`@@ -39,10 +44,6 @@ type state struct {
`
39
44
`// Extendable-Output Functions (May 2014)"
`
40
45
`dsbyte byte
`
41
46
``
42
``
`-
i, n int // storage[i:n] is the buffer, i is only used while squeezing
`
43
``
`-
storage [maxRate]byte
`
44
``
-
45
``
`-
// Specific to SHA-3 and SHAKE.
`
46
47
`outputLen int // the default output size in bytes
`
47
48
`state spongeDirection // whether the sponge is absorbing or squeezing
`
48
49
`}
`
`@@ -61,84 +62,70 @@ func (d *state) Reset() {
`
61
62
`d.a[i] = 0
`
62
63
` }
`
63
64
`d.state = spongeAbsorbing
`
64
``
`-
d.i, d.n = 0, 0
`
``
65
`+
d.n = 0
`
65
66
`}
`
66
67
``
67
68
`func (d *state) clone() *state {
`
68
69
`ret := *d
`
69
70
`return &ret
`
70
71
`}
`
71
72
``
72
``
`-
// permute applies the KeccakF-1600 permutation. It handles
`
73
``
`-
// any input-output buffering.
`
``
73
`+
// permute applies the KeccakF-1600 permutation.
`
74
74
`func (d *state) permute() {
`
75
``
`-
switch d.state {
`
76
``
`-
case spongeAbsorbing:
`
77
``
`-
// If we're absorbing, we need to xor the input into the state
`
78
``
`-
// before applying the permutation.
`
79
``
`-
xorIn(d, d.storage[:d.rate])
`
80
``
`-
d.n = 0
`
81
``
`-
keccakF1600(&d.a)
`
82
``
`-
case spongeSqueezing:
`
83
``
`-
// If we're squeezing, we need to apply the permutation before
`
84
``
`-
// copying more output.
`
85
``
`-
keccakF1600(&d.a)
`
86
``
`-
d.i = 0
`
87
``
`-
copyOut(d, d.storage[:d.rate])
`
``
75
`+
var a *[25]uint64
`
``
76
`+
if cpu.IsBigEndian {
`
``
77
`+
a = new([25]uint64)
`
``
78
`+
for i := range a {
`
``
79
`+
a[i] = binary.LittleEndian.Uint64(d.a[i*8:])
`
``
80
`+
}
`
``
81
`+
} else {
`
``
82
`+
a = (*[25]uint64)(unsafe.Pointer(&d.a))
`
``
83
`+
}
`
``
84
+
``
85
`+
keccakF1600(a)
`
``
86
`+
d.n = 0
`
``
87
+
``
88
`+
if cpu.IsBigEndian {
`
``
89
`+
for i := range a {
`
``
90
`+
binary.LittleEndian.PutUint64(d.a[i*8:], a[i])
`
``
91
`+
}
`
88
92
` }
`
89
93
`}
`
90
94
``
91
95
`// pads appends the domain separation bits in dsbyte, applies
`
92
96
`// the multi-bitrate 10..1 padding rule, and permutes the state.
`
93
97
`func (d *state) padAndPermute() {
`
94
98
`// Pad with this instance's domain-separator bits. We know that there's
`
95
``
`-
// at least one byte of space in d.buf because, if it were full,
`
``
99
`+
// at least one byte of space in the sponge because, if it were full,
`
96
100
`// permute would have been called to empty it. dsbyte also contains the
`
97
101
`// first one bit for the padding. See the comment in the state struct.
`
98
``
`-
d.storage[d.n] = d.dsbyte
`
99
``
`-
d.n++
`
100
``
`-
for d.n < d.rate {
`
101
``
`-
d.storage[d.n] = 0
`
102
``
`-
d.n++
`
103
``
`-
}
`
``
102
`+
d.a[d.n] ^= d.dsbyte
`
104
103
`// This adds the final one bit for the padding. Because of the way that
`
105
104
`// bits are numbered from the LSB upwards, the final bit is the MSB of
`
106
105
`// the last byte.
`
107
``
`-
d.storage[d.rate-1] ^= 0x80
`
``
106
`+
d.a[d.rate-1] ^= 0x80
`
108
107
`// Apply the permutation
`
109
108
`d.permute()
`
110
109
`d.state = spongeSqueezing
`
111
``
`-
d.n = d.rate
`
112
``
`-
copyOut(d, d.storage[:d.rate])
`
113
110
`}
`
114
111
``
115
112
`// Write absorbs more data into the hash's state. It panics if any
`
116
113
`// output has already been read.
`
117
``
`-
func (d *state) Write(p []byte) (written int, err error) {
`
``
114
`+
func (d *state) Write(p []byte) (n int, err error) {
`
118
115
`if d.state != spongeAbsorbing {
`
119
116
`panic("sha3: Write after Read")
`
120
117
` }
`
121
``
`-
written = len(p)
`
``
118
+
``
119
`+
n = len(p)
`
122
120
``
123
121
`for len(p) > 0 {
`
124
``
`-
if d.n == 0 && len(p) >= d.rate {
`
125
``
`-
// The fast path; absorb a full "rate" bytes of input and apply the permutation.
`
126
``
`-
xorIn(d, p[:d.rate])
`
127
``
`-
p = p[d.rate:]
`
128
``
`-
keccakF1600(&d.a)
`
129
``
`-
} else {
`
130
``
`-
// The slow path; buffer the input until we can fill the sponge, and then xor it in.
`
131
``
`-
todo := d.rate - d.n
`
132
``
`-
if todo > len(p) {
`
133
``
`-
todo = len(p)
`
134
``
`-
}
`
135
``
`-
d.n += copy(d.storage[d.n:], p[:todo])
`
136
``
`-
p = p[todo:]
`
137
``
-
138
``
`-
// If the sponge is full, apply the permutation.
`
139
``
`-
if d.n == d.rate {
`
140
``
`-
d.permute()
`
141
``
`-
}
`
``
122
`+
x := subtle.XORBytes(d.a[d.n:d.rate], d.a[d.n:d.rate], p)
`
``
123
`+
d.n += x
`
``
124
`+
p = p[x:]
`
``
125
+
``
126
`+
// If the sponge is full, apply the permutation.
`
``
127
`+
if d.n == d.rate {
`
``
128
`+
d.permute()
`
142
129
` }
`
143
130
` }
`
144
131
``
`@@ -156,12 +143,12 @@ func (d *state) Read(out []byte) (n int, err error) {
`
156
143
``
157
144
`// Now, do the squeezing.
`
158
145
`for len(out) > 0 {
`
159
``
`-
n := copy(out, d.storage[d.i:d.n])
`
160
``
`-
d.i += n
`
161
``
`-
out = out[n:]
`
``
146
`+
x := copy(out, d.a[d.n:d.rate])
`
``
147
`+
d.n += x
`
``
148
`+
out = out[x:]
`
162
149
``
163
150
`// Apply the permutation if we've squeezed the sponge dry.
`
164
``
`-
if d.i == d.rate {
`
``
151
`+
if d.n == d.rate {
`
165
152
`d.permute()
`
166
153
` }
`
167
154
` }
`