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

` }

`