Skip to content

Commit

Permalink
sha3: have ShakeHash extend hash.Hash
Browse files Browse the repository at this point in the history
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 <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Adam Langley <agl@google.com>
Auto-Submit: Matthew Dempsky <mdempsky@google.com>
  • Loading branch information
mdempsky authored and gopherbot committed Sep 10, 2023
1 parent e90f1e1 commit 3f0842a
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 24 deletions.
14 changes: 9 additions & 5 deletions sha3/sha3.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,11 +121,11 @@ func (d *state) padAndPermute(dsbyte byte) {
copyOut(d, d.buf)
}

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

// Sum applies padding to the hash state and then squeezes out the desired
// number of output bytes.
// number of output bytes. It panics if any output has already been read.
func (d *state) Sum(in []byte) []byte {
if d.state != spongeAbsorbing {
panic("sha3: Sum after Read")
}

// Make a copy of the original hash so that caller can keep writing
// and summing.
dup := d.clone()
hash := make([]byte, dup.outputLen)
hash := make([]byte, dup.outputLen, 64) // explicit cap to allow stack allocation
dup.Read(hash)
return append(in, hash...)
}
10 changes: 6 additions & 4 deletions sha3/sha3_s390x.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ type asmState struct {
buf []byte // care must be taken to ensure cap(buf) is a multiple of rate
rate int // equivalent to block size
storage [3072]byte // underlying storage for buf
outputLen int // output length if fixed, 0 if not
outputLen int // output length for full security
function code // KIMD/KLMD function code
state spongeDirection // whether the sponge is absorbing or squeezing
}
Expand All @@ -72,8 +72,10 @@ func newAsmState(function code) *asmState {
s.outputLen = 64
case shake_128:
s.rate = 168
s.outputLen = 32
case shake_256:
s.rate = 136
s.outputLen = 64
default:
panic("sha3: unrecognized function code")
}
Expand Down Expand Up @@ -108,7 +110,7 @@ func (s *asmState) resetBuf() {
// It never returns an error.
func (s *asmState) Write(b []byte) (int, error) {
if s.state != spongeAbsorbing {
panic("sha3: write to sponge after read")
panic("sha3: Write after Read")
}
length := len(b)
for len(b) > 0 {
Expand Down Expand Up @@ -192,8 +194,8 @@ func (s *asmState) Read(out []byte) (n int, err error) {
// Sum appends the current hash to b and returns the resulting slice.
// It does not change the underlying hash state.
func (s *asmState) Sum(b []byte) []byte {
if s.outputLen == 0 {
panic("sha3: cannot call Sum on SHAKE functions")
if s.state != spongeAbsorbing {
panic("sha3: Sum after Read")
}

// Copy the state to preserve the original.
Expand Down
29 changes: 14 additions & 15 deletions sha3/shake.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,26 +17,25 @@ package sha3

import (
"encoding/binary"
"hash"
"io"
)

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

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

// Clone returns a copy of the ShakeHash in its current state.
Clone() ShakeHash

// Reset resets the ShakeHash to its initial state.
Reset()
}

// cSHAKE specific context
Expand Down Expand Up @@ -81,8 +80,8 @@ func leftEncode(value uint64) []byte {
return b[i-1:]
}

func newCShake(N, S []byte, rate int, dsbyte byte) ShakeHash {
c := cshakeState{state: &state{rate: rate, dsbyte: dsbyte}}
func newCShake(N, S []byte, rate, outputLen int, dsbyte byte) ShakeHash {
c := cshakeState{state: &state{rate: rate, outputLen: outputLen, dsbyte: dsbyte}}

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

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

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

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

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

0 comments on commit 3f0842a

Please sign in to comment.