DataHoarder
2cb3a24eaa
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.
173 lines
5.4 KiB
Go
173 lines
5.4 KiB
Go
// Copyright 2014 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package sha3
|
|
|
|
// This file defines the ShakeHash interface, and provides
|
|
// functions for creating SHAKE and cSHAKE instances, as well as utility
|
|
// functions for hashing bytes to arbitrary-length output.
|
|
//
|
|
//
|
|
// SHAKE implementation is based on FIPS PUB 202 [1]
|
|
// cSHAKE implementations is based on NIST SP 800-185 [2]
|
|
//
|
|
// [1] https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf
|
|
// [2] https://doi.org/10.6028/NIST.SP.800-185
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"hash"
|
|
"io"
|
|
)
|
|
|
|
// 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 {
|
|
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, but subsequent calls to Write or Sum
|
|
// will panic.
|
|
io.Reader
|
|
|
|
// Clone returns a copy of the ShakeHash in its current state.
|
|
Clone() ShakeHash
|
|
}
|
|
|
|
// cSHAKE specific context
|
|
type cshakeState struct {
|
|
*HasherState // SHA-3 state context and Read/Write operations
|
|
|
|
// initBlock is the cSHAKE specific initialization set of bytes. It is initialized
|
|
// by newCShake function and stores concatenation of N followed by S, encoded
|
|
// by the method specified in 3.3 of [1].
|
|
// It is stored here in order for Reset() to be able to put context into
|
|
// initial state.
|
|
initBlock []byte
|
|
}
|
|
|
|
// Consts for configuring initial SHA-3 state
|
|
const (
|
|
dsbyteShake = 0x1f
|
|
dsbyteCShake = 0x04
|
|
rate128 = 168
|
|
rate256 = 136
|
|
)
|
|
|
|
func bytepad(input []byte, w int) []byte {
|
|
// leftEncode always returns max 9 bytes
|
|
buf := make([]byte, 0, 9+len(input)+w)
|
|
buf = append(buf, leftEncode(uint64(w))...)
|
|
buf = append(buf, input...)
|
|
padlen := w - (len(buf) % w)
|
|
return append(buf, make([]byte, padlen)...)
|
|
}
|
|
|
|
func leftEncode(value uint64) []byte {
|
|
var b [9]byte
|
|
binary.BigEndian.PutUint64(b[1:], value)
|
|
// Trim all but last leading zero bytes
|
|
i := byte(1)
|
|
for i < 8 && b[i] == 0 {
|
|
i++
|
|
}
|
|
// Prepend number of encoded bytes
|
|
b[i-1] = 9 - i
|
|
return b[i-1:]
|
|
}
|
|
|
|
func newCShake(N, S []byte, rate, outputLen int, dsbyte byte) ShakeHash {
|
|
c := cshakeState{HasherState: &HasherState{rate: rate, outputLen: outputLen, dsbyte: dsbyte}}
|
|
|
|
// leftEncode returns max 9 bytes
|
|
c.initBlock = make([]byte, 0, 9*2+len(N)+len(S))
|
|
c.initBlock = append(c.initBlock, leftEncode(uint64(len(N)*8))...)
|
|
c.initBlock = append(c.initBlock, N...)
|
|
c.initBlock = append(c.initBlock, leftEncode(uint64(len(S)*8))...)
|
|
c.initBlock = append(c.initBlock, S...)
|
|
c.Write(bytepad(c.initBlock, c.rate))
|
|
return &c
|
|
}
|
|
|
|
// Reset resets the hash to initial state.
|
|
func (c *cshakeState) Reset() {
|
|
c.HasherState.Reset()
|
|
c.Write(bytepad(c.initBlock, c.rate))
|
|
}
|
|
|
|
// Clone returns copy of a cSHAKE context within its current state.
|
|
func (c *cshakeState) Clone() ShakeHash {
|
|
b := make([]byte, len(c.initBlock))
|
|
copy(b, c.initBlock)
|
|
return &cshakeState{HasherState: c.clone(), initBlock: b}
|
|
}
|
|
|
|
// Clone returns copy of SHAKE context within its current state.
|
|
func (c *HasherState) Clone() ShakeHash {
|
|
return c.clone()
|
|
}
|
|
|
|
// NewShake128 creates a new SHAKE128 variable-output-length ShakeHash.
|
|
// Its generic security strength is 128 bits against all attacks if at
|
|
// least 32 bytes of its output are used.
|
|
func NewShake128() ShakeHash {
|
|
if h := newShake128Asm(); h != nil {
|
|
return h
|
|
}
|
|
return &HasherState{rate: rate128, outputLen: 32, dsbyte: dsbyteShake}
|
|
}
|
|
|
|
// NewShake256 creates a new SHAKE256 variable-output-length ShakeHash.
|
|
// Its generic security strength is 256 bits against all attacks if
|
|
// at least 64 bytes of its output are used.
|
|
func NewShake256() ShakeHash {
|
|
if h := newShake256Asm(); h != nil {
|
|
return h
|
|
}
|
|
return &HasherState{rate: rate256, outputLen: 64, dsbyte: dsbyteShake}
|
|
}
|
|
|
|
// NewCShake128 creates a new instance of cSHAKE128 variable-output-length ShakeHash,
|
|
// a customizable variant of SHAKE128.
|
|
// N is used to define functions based on cSHAKE, it can be empty when plain cSHAKE is
|
|
// desired. S is a customization byte string used for domain separation - two cSHAKE
|
|
// computations on same input with different S yield unrelated outputs.
|
|
// When N and S are both empty, this is equivalent to NewShake128.
|
|
func NewCShake128(N, S []byte) ShakeHash {
|
|
if len(N) == 0 && len(S) == 0 {
|
|
return NewShake128()
|
|
}
|
|
return newCShake(N, S, rate128, 32, dsbyteCShake)
|
|
}
|
|
|
|
// NewCShake256 creates a new instance of cSHAKE256 variable-output-length ShakeHash,
|
|
// a customizable variant of SHAKE256.
|
|
// N is used to define functions based on cSHAKE, it can be empty when plain cSHAKE is
|
|
// desired. S is a customization byte string used for domain separation - two cSHAKE
|
|
// computations on same input with different S yield unrelated outputs.
|
|
// When N and S are both empty, this is equivalent to NewShake256.
|
|
func NewCShake256(N, S []byte) ShakeHash {
|
|
if len(N) == 0 && len(S) == 0 {
|
|
return NewShake256()
|
|
}
|
|
return newCShake(N, S, rate256, 64, dsbyteCShake)
|
|
}
|
|
|
|
// ShakeSum128 writes an arbitrary-length digest of data into hash.
|
|
func ShakeSum128(hash, data []byte) {
|
|
h := NewShake128()
|
|
h.Write(data)
|
|
h.Read(hash)
|
|
}
|
|
|
|
// ShakeSum256 writes an arbitrary-length digest of data into hash.
|
|
func ShakeSum256(hash, data []byte) {
|
|
h := NewShake256()
|
|
h.Write(data)
|
|
h.Read(hash)
|
|
}
|