1
0
mirror of https://github.com/henrydcase/nobs.git synced 2024-11-25 16:41:32 +00:00
nobs/hash/sha3/sha3.go
Henry Case 8474981cfc
SHA-3: speedups (#47)
* add function for one-off calculation

* sha3: simplifies Read function

* sha3: remove if from Read
2020-10-03 23:27:08 +01:00

255 lines
6.1 KiB
Go

// Copyright 2020 Kris Kwiatkowski. 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
import (
"errors"
"hash"
)
type spongeDesc struct {
r int // rate
d int // output size of SHA-3
name string // human readable name of the scheme
}
// Id's of SHA3 instantiations
const (
SHA3_224 uint8 = iota
SHA3_256
SHA3_384
SHA3_512
SHAKE128
SHAKE256
)
const (
// maximum value for rate used by keccak functions
maxRate = 168
)
// Statically allocated error message
var ErrWriteAfterRead = errors.New("sha3: can't write after read")
var Sha3Desc = map[uint8]spongeDesc{
SHA3_224: {r: 144, d: 224 / 8, name: "SHA3-224"},
SHA3_256: {r: 136, d: 256 / 8, name: "SHA3-256"},
SHA3_384: {r: 104, d: 384 / 8, name: "SHA3-384"},
SHA3_512: {r: 72, d: 512 / 8, name: "SHA3-512"},
SHAKE128: {r: 168, d: 0, name: "SHAKE-128"},
SHAKE256: {r: 136, d: 0, name: "SHAKE-128"},
}
type state struct {
// Structure describing the details of hash algorithm
desc spongeDesc
// permuation state. 25*64 is a width of the keccak permutation used
a [25]uint64
// sfx is a concatenation of "domain separator" as described in FIPS-202,
// (section 6.1 and 6.2) with first bit of a pad10*1 (see section 5.1).
sfx byte
// Temporary data buffer
data storageBuf
// Index in the buffer. it points to the next available possition
// in the data buffer if isSquezing is false. In case it is true
// it indicates amount of unconsumed data.
idx int
// Indicates state of the sponge function. Whether it is absorbing
// or squezing
isSquezing bool
}
func min(a, b int) int {
if a < b {
return a
}
return b
}
// BlockSize returns block size in bytes. Corresponds to the input
// block size B of the HMAC
func (d *state) BlockSize() int { return d.desc.r }
// Size returns the output size of the hash function in bytes.
func (d *state) Size() int { return d.desc.d }
// Reset clears the internal state by zeroing the sponge state and
// the byte buffer, and setting spongeState to absorbing.
func (d *state) Reset() {
// Zero the permutation's state.
for i := range d.a {
d.a[i] = 0
}
for i := range d.data {
d.data[i] = 0
}
d.isSquezing = false
d.idx = 0
}
// Write consumes data from the user. The data may change state of the
// hash in case caller provided at least "rate" bytes of data. The "rate" value
// for the hash is returned by the BlockSize() function. It may return an
// error if sponge state has changed to "squeezing", meaning - Write was
// called after at least one call to Read() has been done.
func (c *state) Write(in []byte) (nwrite int, err error) {
if c.isSquezing {
return 0, ErrWriteAfterRead
}
nwrite = len(in)
rate := c.BlockSize()
buf := c.data.asBytes()
processLen := c.idx + len(in)
if processLen < c.BlockSize() {
// not enough data to process
copy(buf[c.idx:], in)
c.idx = processLen
return nwrite, nil
}
// process first block
fbLen := rate - c.idx
copy(buf[c.idx:], in[:fbLen])
xorIn(c, buf[:])
keccakF1600(&c.a)
// process remaining blocks
in = in[fbLen:]
for len(in) >= rate {
xorIn(c, in[:rate])
keccakF1600(&c.a)
in = in[rate:]
}
// store unprocessed data
copy(buf[:], in)
c.idx = len(in)
return nwrite, nil
}
func (c *state) finalize_sha3() {
buf := c.data.asBytes()[:]
rate := c.BlockSize()
// there is at least one byte free, otherise
// buf would be squezed already
for i := c.idx + 1; i < rate; i++ {
buf[i] = 0
}
buf[c.idx] = c.sfx
buf[rate-1] |= 0x80
xorIn(c, buf[:rate])
keccakF1600(&c.a)
copyOut(c, buf[:rate])
c.idx = rate // now, idx indicates unconsumed amount of data
c.isSquezing = true
}
// Read changes state of the hash if called first time. It will
// return len(out) bytes of data. Never fails.
func (c *state) Read(out []byte) (nread int, err error) {
buf := c.data.asBytes()[:]
rate := c.BlockSize()
nread = len(out)
// finalize if not done yet
if !c.isSquezing {
c.finalize_sha3()
}
// Copy-out bytes that are still kept in the buffer
l := min(c.idx, len(out))
copy(out, buf[rate-c.idx:rate-c.idx+l])
out = out[l:]
c.idx -= l
if len(out) == 0 {
// nothing else todo
return nread, nil
}
// copy out full blocks and squeeze. at this point
// there is no more data in the buffer.
nblocks := len(out) / rate
for nblocks > 0 {
keccakF1600(&c.a)
copyOut(c, out[:rate])
out = out[rate:]
nblocks--
}
keccakF1600(&c.a)
copyOut(c, buf)
copy(out, buf[:len(out)])
c.idx = rate - len(out)
return nread, nil
}
// Sum applies padding to the hash state and then squeezes out the desired
// number of output bytes.
func (c *state) Sum(in []byte) []byte {
l := len(in)
// create buffer if nil has been provided
if in == nil {
in = make([]byte, c.Size())
}
// enlarge capacity of the buffer if needed
if cap(in) < (l + c.Size()) {
b := make([]byte, l+c.Size()-cap(in))
in = append(in[:cap(in)], b...)
}
in = in[:l+c.Size()]
c.Read(in[l:])
return in
}
func (c *state) digest(out, in []byte) {
nread := len(out)
rate := c.BlockSize()
nblocks := nread / rate
c.Write(in)
c.finalize_sha3()
for i := 0; i < nblocks-1; i++ {
keccakF1600(&c.a)
copyOut(c, out[:])
out = out[rate:]
}
keccakF1600(&c.a)
copyOut(c, out[:len(out)])
}
// New224 creates a new SHA3-224 hash.
// Its generic security strength is 224 bits against preimage attacks,
// and 112 bits against collision attacks.
func New224() hash.Hash {
return &state{sfx: 0x06, desc: Sha3Desc[SHA3_224]}
}
// New256 creates a new SHA3-256 hash.
// Its generic security strength is 256 bits against preimage attacks,
// and 128 bits against collision attacks.
func New256() hash.Hash {
return &state{sfx: 0x06, desc: Sha3Desc[SHA3_256]}
}
// New384 creates a new SHA3-384 hash.
// Its generic security strength is 384 bits against preimage attacks,
// and 192 bits against collision attacks.
func New384() hash.Hash {
return &state{sfx: 0x06, desc: Sha3Desc[SHA3_384]}
}
// New512 creates a new SHA3-512 hash.
// Its generic security strength is 512 bits against preimage attacks,
// and 256 bits against collision attacks.
func New512() hash.Hash {
return &state{sfx: 0x06, desc: Sha3Desc[SHA3_512]}
}