Skip to content

Commit 3f0842a

Browse files
mdempskygopherbot
authored andcommitted
sha3: have ShakeHash extend hash.Hash
Package sha3 recommends the SHAKE functions for new uses, but this is currently somewhat inconvenient because ShakeHash does not implement hash.Hash. This is understandable, as SHAKE supports arbitrary-length outputs whereas hash.Hash only supports fixed-length outputs. But there's a natural fixed-length output to provide: the minimum output that still provides SHAKE's full-strength generic security. While here, tweak Sum so that its temporary buffer can be stack allocated. Also, tweak the panic message in Write so that the error text is more readily understandable to Go programmers without needing to be familiar with crypto jargon, and add a similar check in Sum. Change-Id: Icf037d3990a71de5630f8825606614443f8c5245 Reviewed-on: https://go-review.googlesource.com/c/crypto/+/526937 LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-by: Adam Langley <[email protected]> Auto-Submit: Matthew Dempsky <[email protected]>
1 parent e90f1e1 commit 3f0842a

File tree

3 files changed

+29
-24
lines changed

3 files changed

+29
-24
lines changed

sha3/sha3.go

+9-5
Original file line numberDiff line numberDiff line change
@@ -121,11 +121,11 @@ func (d *state) padAndPermute(dsbyte byte) {
121121
copyOut(d, d.buf)
122122
}
123123

124-
// Write absorbs more data into the hash's state. It produces an error
125-
// if more data is written to the ShakeHash after writing
124+
// Write absorbs more data into the hash's state. It panics if any
125+
// output has already been read.
126126
func (d *state) Write(p []byte) (written int, err error) {
127127
if d.state != spongeAbsorbing {
128-
panic("sha3: write to sponge after read")
128+
panic("sha3: Write after Read")
129129
}
130130
if d.buf == nil {
131131
d.buf = d.storage.asBytes()[:0]
@@ -182,12 +182,16 @@ func (d *state) Read(out []byte) (n int, err error) {
182182
}
183183

184184
// Sum applies padding to the hash state and then squeezes out the desired
185-
// number of output bytes.
185+
// number of output bytes. It panics if any output has already been read.
186186
func (d *state) Sum(in []byte) []byte {
187+
if d.state != spongeAbsorbing {
188+
panic("sha3: Sum after Read")
189+
}
190+
187191
// Make a copy of the original hash so that caller can keep writing
188192
// and summing.
189193
dup := d.clone()
190-
hash := make([]byte, dup.outputLen)
194+
hash := make([]byte, dup.outputLen, 64) // explicit cap to allow stack allocation
191195
dup.Read(hash)
192196
return append(in, hash...)
193197
}

sha3/sha3_s390x.go

+6-4
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ type asmState struct {
4949
buf []byte // care must be taken to ensure cap(buf) is a multiple of rate
5050
rate int // equivalent to block size
5151
storage [3072]byte // underlying storage for buf
52-
outputLen int // output length if fixed, 0 if not
52+
outputLen int // output length for full security
5353
function code // KIMD/KLMD function code
5454
state spongeDirection // whether the sponge is absorbing or squeezing
5555
}
@@ -72,8 +72,10 @@ func newAsmState(function code) *asmState {
7272
s.outputLen = 64
7373
case shake_128:
7474
s.rate = 168
75+
s.outputLen = 32
7576
case shake_256:
7677
s.rate = 136
78+
s.outputLen = 64
7779
default:
7880
panic("sha3: unrecognized function code")
7981
}
@@ -108,7 +110,7 @@ func (s *asmState) resetBuf() {
108110
// It never returns an error.
109111
func (s *asmState) Write(b []byte) (int, error) {
110112
if s.state != spongeAbsorbing {
111-
panic("sha3: write to sponge after read")
113+
panic("sha3: Write after Read")
112114
}
113115
length := len(b)
114116
for len(b) > 0 {
@@ -192,8 +194,8 @@ func (s *asmState) Read(out []byte) (n int, err error) {
192194
// Sum appends the current hash to b and returns the resulting slice.
193195
// It does not change the underlying hash state.
194196
func (s *asmState) Sum(b []byte) []byte {
195-
if s.outputLen == 0 {
196-
panic("sha3: cannot call Sum on SHAKE functions")
197+
if s.state != spongeAbsorbing {
198+
panic("sha3: Sum after Read")
197199
}
198200

199201
// Copy the state to preserve the original.

sha3/shake.go

+14-15
Original file line numberDiff line numberDiff line change
@@ -17,26 +17,25 @@ package sha3
1717

1818
import (
1919
"encoding/binary"
20+
"hash"
2021
"io"
2122
)
2223

23-
// ShakeHash defines the interface to hash functions that
24-
// support arbitrary-length output.
24+
// ShakeHash defines the interface to hash functions that support
25+
// arbitrary-length output. When used as a plain [hash.Hash], it
26+
// produces minimum-length outputs that provide full-strength generic
27+
// security.
2528
type ShakeHash interface {
26-
// Write absorbs more data into the hash's state. It panics if input is
27-
// written to it after output has been read from it.
28-
io.Writer
29+
hash.Hash
2930

3031
// Read reads more output from the hash; reading affects the hash's
3132
// state. (ShakeHash.Read is thus very different from Hash.Sum)
32-
// It never returns an error.
33+
// It never returns an error, but subsequent calls to Write or Sum
34+
// will panic.
3335
io.Reader
3436

3537
// Clone returns a copy of the ShakeHash in its current state.
3638
Clone() ShakeHash
37-
38-
// Reset resets the ShakeHash to its initial state.
39-
Reset()
4039
}
4140

4241
// cSHAKE specific context
@@ -81,8 +80,8 @@ func leftEncode(value uint64) []byte {
8180
return b[i-1:]
8281
}
8382

84-
func newCShake(N, S []byte, rate int, dsbyte byte) ShakeHash {
85-
c := cshakeState{state: &state{rate: rate, dsbyte: dsbyte}}
83+
func newCShake(N, S []byte, rate, outputLen int, dsbyte byte) ShakeHash {
84+
c := cshakeState{state: &state{rate: rate, outputLen: outputLen, dsbyte: dsbyte}}
8685

8786
// leftEncode returns max 9 bytes
8887
c.initBlock = make([]byte, 0, 9*2+len(N)+len(S))
@@ -119,7 +118,7 @@ func NewShake128() ShakeHash {
119118
if h := newShake128Asm(); h != nil {
120119
return h
121120
}
122-
return &state{rate: rate128, dsbyte: dsbyteShake}
121+
return &state{rate: rate128, outputLen: 32, dsbyte: dsbyteShake}
123122
}
124123

125124
// NewShake256 creates a new SHAKE256 variable-output-length ShakeHash.
@@ -129,7 +128,7 @@ func NewShake256() ShakeHash {
129128
if h := newShake256Asm(); h != nil {
130129
return h
131130
}
132-
return &state{rate: rate256, dsbyte: dsbyteShake}
131+
return &state{rate: rate256, outputLen: 64, dsbyte: dsbyteShake}
133132
}
134133

135134
// NewCShake128 creates a new instance of cSHAKE128 variable-output-length ShakeHash,
@@ -142,7 +141,7 @@ func NewCShake128(N, S []byte) ShakeHash {
142141
if len(N) == 0 && len(S) == 0 {
143142
return NewShake128()
144143
}
145-
return newCShake(N, S, rate128, dsbyteCShake)
144+
return newCShake(N, S, rate128, 32, dsbyteCShake)
146145
}
147146

148147
// NewCShake256 creates a new instance of cSHAKE256 variable-output-length ShakeHash,
@@ -155,7 +154,7 @@ func NewCShake256(N, S []byte) ShakeHash {
155154
if len(N) == 0 && len(S) == 0 {
156155
return NewShake256()
157156
}
158-
return newCShake(N, S, rate256, dsbyteCShake)
157+
return newCShake(N, S, rate256, 64, dsbyteCShake)
159158
}
160159

161160
// ShakeSum128 writes an arbitrary-length digest of data into hash.

0 commit comments

Comments
 (0)