mirror of
https://github.com/henrydcase/nobs.git
synced 2024-11-26 09:01:20 +00:00
sha3: optimizations and cleanup
* complate reset of the SHA-3 code. Affects mostly the code in sha3.go * fixes a bug which causes SHAKE implementation to crash * implementation of Read()/Write() avoid unnecessary buffering as much as possible * NOTE: at some point I've done separated implementation for SumXXX, functions, but after optimizing implementation of Read/Write/Sum, the gain wasn't that big Current speed on Initial speed on i7-8665U@1.90 BenchmarkPermutationFunction 1592787 736 ns/op 271.90 MB/s 0 B/op 0 allocs/op BenchmarkSha3Chunk_x01/SHA-3/224 98752 11630 ns/op 176.02 MB/s 0 B/op 0 allocs/op BenchmarkSha3Chunk_x01/SHA-3/256 92508 12447 ns/op 164.46 MB/s 0 B/op 0 allocs/op BenchmarkSha3Chunk_x01/SHA-3/384 76765 15206 ns/op 134.62 MB/s 0 B/op 0 allocs/op BenchmarkSha3Chunk_x01/SHA-3/512 54333 21932 ns/op 93.33 MB/s 0 B/op 0 allocs/op BenchmarkSha3Chunk_x16/SHA-3/224 10000 102161 ns/op 160.37 MB/s 0 B/op 0 allocs/op BenchmarkSha3Chunk_x16/SHA-3/256 10000 106531 ns/op 153.80 MB/s 0 B/op 0 allocs/op BenchmarkSha3Chunk_x16/SHA-3/384 8641 137272 ns/op 119.35 MB/s 0 B/op 0 allocs/op BenchmarkSha3Chunk_x16/SHA-3/512 6340 189124 ns/op 86.63 MB/s 0 B/op 0 allocs/op BenchmarkShake_x01/SHAKE-128 167062 7149 ns/op 188.83 MB/s 0 B/op 0 allocs/op BenchmarkShake_x01/SHAKE-256 151982 7748 ns/op 174.24 MB/s 0 B/op 0 allocs/op BenchmarkShake_x16/SHAKE-128 12963 87770 ns/op 186.67 MB/s 0 B/op 0 allocs/op BenchmarkShake_x16/SHAKE-256 10000 105554 ns/op 155.22 MB/s 0 B/op 0 allocs/op BenchmarkCShake/cSHAKE-128 109148 10940 ns/op 187.11 MB/s 0 B/op 0 allocs/op BenchmarkCShake/cSHAKE-256 90324 13211 ns/op 154.94 MB/s 0 B/op 0 allocs/op PASS
This commit is contained in:
parent
7dcb72bf74
commit
45139582f3
57
LICENSE
57
LICENSE
@ -1,13 +1,52 @@
|
||||
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
Version 2, December 2004
|
||||
Copyright (c) 2020 Kris Kwiatkowski, All rights reserved.
|
||||
|
||||
Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
Everyone is permitted to copy and distribute verbatim or modified
|
||||
copies of this license document, and changing it is allowed as long
|
||||
as the name is changed.
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIEDi
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
====
|
||||
|
||||
Copyright (c) 2009 The Go Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
@ -1,66 +1,20 @@
|
||||
// Copyright 2020 Kris Kwiatkowski. All rights reserved.
|
||||
// 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 implements the SHA-3 fixed-output-length hash functions and
|
||||
// the SHAKE variable-output-length hash functions defined by FIPS-202.
|
||||
// Package sha3 implements the Keccak-p[1600, 24] permuation.
|
||||
// The 1600 stands for width of the permutation - number of
|
||||
// bits that are permuted at a time, and 24 stands for number
|
||||
// of rounds (iterations) of the permuation.
|
||||
// Package implementds derivatives of the Keccak permuation,
|
||||
// like SHA-3 fixed-output-length hash, SHAKE which is an
|
||||
// extendable-output-functions (XOF) and cSHAKE - a XOF with
|
||||
// domain separation.
|
||||
//
|
||||
// Both types of hash function use the "sponge" construction and the Keccak
|
||||
// permutation. For a detailed specification see http://keccak.noekeon.org/
|
||||
// The SHA-3 and SHAKE are documented in FIPS-PUB-202 [1] and
|
||||
// cSHAKE specification can be found in NIST-SP-800-185 [2].
|
||||
//
|
||||
//
|
||||
// Guidance
|
||||
//
|
||||
// If you aren't sure what function you need, use SHAKE256 with at least 64
|
||||
// bytes of output. The SHAKE instances are faster than the SHA3 instances;
|
||||
// the latter have to allocate memory to conform to the hash.Hash interface.
|
||||
//
|
||||
// If you need a secret-key MAC (message authentication code), prepend the
|
||||
// secret key to the input, hash with SHAKE256 and read at least 32 bytes of
|
||||
// output.
|
||||
//
|
||||
//
|
||||
// Security strengths
|
||||
//
|
||||
// The SHA3-x (x equals 224, 256, 384, or 512) functions have a security
|
||||
// strength against preimage attacks of x bits. Since they only produce "x"
|
||||
// bits of output, their collision-resistance is only "x/2" bits.
|
||||
//
|
||||
// The SHAKE-256 and -128 functions have a generic security strength of 256 and
|
||||
// 128 bits against all attacks, provided that at least 2x bits of their output
|
||||
// is used. Requesting more than 64 or 32 bytes of output, respectively, does
|
||||
// not increase the collision-resistance of the SHAKE functions.
|
||||
//
|
||||
//
|
||||
// The sponge construction
|
||||
//
|
||||
// A sponge builds a pseudo-random function from a public pseudo-random
|
||||
// permutation, by applying the permutation to a state of "rate + capacity"
|
||||
// bytes, but hiding "capacity" of the bytes.
|
||||
//
|
||||
// A sponge starts out with a zero state. To hash an input using a sponge, up
|
||||
// to "rate" bytes of the input are XORed into the sponge's state. The sponge
|
||||
// is then "full" and the permutation is applied to "empty" it. This process is
|
||||
// repeated until all the input has been "absorbed". The input is then padded.
|
||||
// The digest is "squeezed" from the sponge in the same way, except that output
|
||||
// output is copied out instead of input being XORed in.
|
||||
//
|
||||
// A sponge is parameterized by its generic security strength, which is equal
|
||||
// to half its capacity; capacity + rate is equal to the permutation's width.
|
||||
// Since the KeccakF-1600 permutation is 1600 bits (200 bytes) wide, this means
|
||||
// that the security strength of a sponge instance is equal to (1600 - bitrate) / 2.
|
||||
//
|
||||
//
|
||||
// Recommendations
|
||||
//
|
||||
// The SHAKE functions are recommended for most new uses. They can produce
|
||||
// output of arbitrary length. SHAKE256, with an output length of at least
|
||||
// 64 bytes, provides 256-bit security against all attacks. The Keccak team
|
||||
// recommends it for most applications upgrading from SHA2-512. (NIST chose a
|
||||
// much stronger, but much slower, sponge instance for SHA3-512.)
|
||||
//
|
||||
// The SHA-3 functions are "drop-in" replacements for the SHA-2 functions.
|
||||
// They produce output of the same length, with the same security strengths
|
||||
// against all attacks. This means, in particular, that SHA3-256 only has
|
||||
// 128-bit collision resistance, because its output length is 32 bytes.
|
||||
// Implementation was initially based on
|
||||
// https://godoc.org/golang.org/x/crypto/sha3
|
||||
package sha3 // import "github.com/henrydcase/nobs/hash/sha3"
|
||||
|
@ -1,256 +1,240 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// 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 "hash"
|
||||
import (
|
||||
"errors"
|
||||
"hash"
|
||||
)
|
||||
|
||||
// spongeDirection indicates the direction bytes are flowing through the sponge.
|
||||
type spongeDirection int
|
||||
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 (
|
||||
// spongeAbsorbing indicates that the sponge is absorbing input.
|
||||
spongeAbsorbing spongeDirection = iota
|
||||
// spongeSqueezing indicates that the sponge is being squeezed.
|
||||
spongeSqueezing
|
||||
SHA3_224 uint8 = iota
|
||||
SHA3_256
|
||||
SHA3_384
|
||||
SHA3_512
|
||||
SHAKE128
|
||||
SHAKE256
|
||||
)
|
||||
|
||||
const (
|
||||
// maxRate is the maximum size of the internal buffer. SHAKE-256
|
||||
// currently needs the largest buffer.
|
||||
// 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 {
|
||||
// Generic sponge components.
|
||||
a [25]uint64 // main state of the hash
|
||||
buf []byte // points into storage
|
||||
rate int // the number of bytes of state to use
|
||||
// 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
|
||||
}
|
||||
|
||||
// dsbyte contains the "domain separation" bits and the first bit of
|
||||
// the padding. Sections 6.1 and 6.2 of [1] separate the outputs of the
|
||||
// SHA-3 and SHAKE functions by appending bitstrings to the message.
|
||||
// Using a little-endian bit-ordering convention, these are "01" for SHA-3
|
||||
// and "1111" for SHAKE, or 00000010b and 00001111b, respectively. Then the
|
||||
// padding rule from section 5.1 is applied to pad the message to a multiple
|
||||
// of the rate, which involves adding a "1" bit, zero or more "0" bits, and
|
||||
// a final "1" bit. We merge the first "1" bit from the padding into dsbyte,
|
||||
// giving 00000110b (0x06) and 00011111b (0x1f).
|
||||
// [1] http://csrc.nist.gov/publications/drafts/fips-202/fips_202_draft.pdf
|
||||
// "Draft FIPS 202: SHA-3 Standard: Permutation-Based Hash and
|
||||
// Extendable-Output Functions (May 2014)"
|
||||
dsbyte byte
|
||||
|
||||
storage storageBuf
|
||||
|
||||
// Specific to SHA-3 and SHAKE.
|
||||
outputLen int // the default output size in bytes
|
||||
state spongeDirection // whether the sponge is absorbing or squeezing
|
||||
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.rate }
|
||||
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.outputLen }
|
||||
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 Sponge.state to absorbing.
|
||||
// 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
|
||||
}
|
||||
d.state = spongeAbsorbing
|
||||
d.buf = d.storage.asBytes()[:0]
|
||||
for i := range d.data {
|
||||
d.data[i] = 0
|
||||
}
|
||||
d.isSquezing = false
|
||||
d.idx = 0
|
||||
}
|
||||
|
||||
func (d *state) clone() *state {
|
||||
ret := *d
|
||||
if ret.state == spongeAbsorbing {
|
||||
ret.buf = ret.storage.asBytes()[:len(ret.buf)]
|
||||
} else {
|
||||
ret.buf = ret.storage.asBytes()[d.rate-cap(d.buf) : d.rate]
|
||||
// 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
|
||||
}
|
||||
|
||||
return &ret
|
||||
// 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
|
||||
}
|
||||
|
||||
// permute applies the KeccakF-1600 permutation. It handles
|
||||
// any input-output buffering.
|
||||
func (d *state) permute() {
|
||||
switch d.state {
|
||||
case spongeAbsorbing:
|
||||
// If we're absorbing, we need to xor the input into the state
|
||||
// before applying the permutation.
|
||||
xorIn(d, d.buf)
|
||||
d.buf = d.storage.asBytes()[:0]
|
||||
keccakF1600(&d.a)
|
||||
case spongeSqueezing:
|
||||
// If we're squeezing, we need to apply the permutatin before
|
||||
// copying more output.
|
||||
keccakF1600(&d.a)
|
||||
d.buf = d.storage.asBytes()[:d.rate]
|
||||
copyOut(d, d.buf)
|
||||
}
|
||||
}
|
||||
// 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)
|
||||
|
||||
// pads appends the domain separation bits in dsbyte, applies
|
||||
// the multi-bitrate 10..1 padding rule, and permutes the state.
|
||||
func (d *state) padAndPermute(dsbyte byte) {
|
||||
if d.buf == nil {
|
||||
d.buf = d.storage.asBytes()[:0]
|
||||
}
|
||||
// Pad with this instance's domain-separator bits. We know that there's
|
||||
// at least one byte of space in d.buf because, if it were full,
|
||||
// permute would have been called to empty it. dsbyte also contains the
|
||||
// first one bit for the padding. See the comment in the state struct.
|
||||
d.buf = append(d.buf, dsbyte)
|
||||
zerosStart := len(d.buf)
|
||||
d.buf = d.storage.asBytes()[:d.rate]
|
||||
for i := zerosStart; i < d.rate; i++ {
|
||||
d.buf[i] = 0
|
||||
}
|
||||
// This adds the final one bit for the padding. Because of the way that
|
||||
// bits are numbered from the LSB upwards, the final bit is the MSB of
|
||||
// the last byte.
|
||||
d.buf[d.rate-1] ^= 0x80
|
||||
// Apply the permutation
|
||||
d.permute()
|
||||
d.state = spongeSqueezing
|
||||
d.buf = d.storage.asBytes()[:d.rate]
|
||||
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
|
||||
func (d *state) Write(p []byte) (written int, err error) {
|
||||
if d.state != spongeAbsorbing {
|
||||
panic("sha3: write to sponge after read")
|
||||
}
|
||||
if d.buf == nil {
|
||||
d.buf = d.storage.asBytes()[:0]
|
||||
}
|
||||
written = len(p)
|
||||
|
||||
for len(p) > 0 {
|
||||
if len(d.buf) == 0 && len(p) >= d.rate {
|
||||
// The fast path; absorb a full "rate" bytes of input and apply the permutation.
|
||||
xorIn(d, p[:d.rate])
|
||||
p = p[d.rate:]
|
||||
keccakF1600(&d.a)
|
||||
} else {
|
||||
// The slow path; buffer the input until we can fill the sponge, and then xor it in.
|
||||
todo := d.rate - len(d.buf)
|
||||
if todo > len(p) {
|
||||
todo = len(p)
|
||||
}
|
||||
d.buf = append(d.buf, p[:todo]...)
|
||||
p = p[todo:]
|
||||
|
||||
// If the sponge is full, apply the permutation.
|
||||
if len(d.buf) == d.rate {
|
||||
d.permute()
|
||||
}
|
||||
if !c.isSquezing {
|
||||
// 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
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Read squeezes an arbitrary number of bytes from the sponge.
|
||||
func (d *state) Read(out []byte) (n int, err error) {
|
||||
// If we're still absorbing, pad and apply the permutation.
|
||||
if d.state == spongeAbsorbing {
|
||||
d.padAndPermute(d.dsbyte)
|
||||
// Copy-out bytes that are still kept in the buffer
|
||||
if c.idx != 0 {
|
||||
l := min(c.idx, len(out))
|
||||
idx := rate - c.idx
|
||||
copy(out, buf[idx:idx+l])
|
||||
out = out[l:]
|
||||
c.idx -= l
|
||||
}
|
||||
|
||||
n = len(out)
|
||||
|
||||
// Now, do the squeezing.
|
||||
for len(out) > 0 {
|
||||
n := copy(out, d.buf)
|
||||
d.buf = d.buf[n:]
|
||||
out = out[n:]
|
||||
|
||||
// Apply the permutation if we've squeezed the sponge dry.
|
||||
if len(d.buf) == 0 {
|
||||
d.permute()
|
||||
}
|
||||
l := len(out)
|
||||
if l == 0 {
|
||||
// nothing else todo
|
||||
return nread, nil
|
||||
}
|
||||
|
||||
return
|
||||
// copy out full blocks and squeeze. at this point
|
||||
// there is no more data in the buffer.
|
||||
nblocks := l / rate
|
||||
for i := 0; i < nblocks; i++ {
|
||||
keccakF1600(&c.a)
|
||||
copyOut(c, out[:rate])
|
||||
out = out[rate:]
|
||||
}
|
||||
|
||||
// produce more if needed
|
||||
l = len(out)
|
||||
if l == 0 {
|
||||
return nread, nil
|
||||
}
|
||||
|
||||
keccakF1600(&c.a)
|
||||
copyOut(c, buf)
|
||||
copy(out, buf[:l])
|
||||
c.idx = rate - l
|
||||
return nread, nil
|
||||
}
|
||||
|
||||
// Sum applies padding to the hash state and then squeezes out the desired
|
||||
// number of output bytes.
|
||||
func (d *state) Sum(in []byte) []byte {
|
||||
// Make a copy of the original hash so that caller can keep writing
|
||||
// and summing.
|
||||
dup := d.clone()
|
||||
hash := make([]byte, dup.outputLen)
|
||||
dup.Read(hash)
|
||||
return append(in, hash...)
|
||||
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
|
||||
}
|
||||
|
||||
// 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{rate: 144, outputLen: 28, dsbyte: 0x06}
|
||||
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{rate: 136, outputLen: 32, dsbyte: 0x06}
|
||||
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{rate: 104, outputLen: 48, dsbyte: 0x06}
|
||||
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{rate: 72, outputLen: 64, dsbyte: 0x06}
|
||||
}
|
||||
|
||||
// Sum224 returns the SHA3-224 digest of the data.
|
||||
func Sum224(data []byte) (digest [28]byte) {
|
||||
h := New224()
|
||||
h.Write(data)
|
||||
h.Sum(digest[:0])
|
||||
return
|
||||
}
|
||||
|
||||
// Sum256 returns the SHA3-256 digest of the data.
|
||||
func Sum256(data []byte) (digest [32]byte) {
|
||||
h := New256()
|
||||
h.Write(data)
|
||||
h.Sum(digest[:0])
|
||||
return
|
||||
}
|
||||
|
||||
// Sum384 returns the SHA3-384 digest of the data.
|
||||
func Sum384(data []byte) (digest [48]byte) {
|
||||
h := New384()
|
||||
h.Write(data)
|
||||
h.Sum(digest[:0])
|
||||
return
|
||||
}
|
||||
|
||||
// Sum512 returns the SHA3-512 digest of the data.
|
||||
func Sum512(data []byte) (digest [64]byte) {
|
||||
h := New512()
|
||||
h.Write(data)
|
||||
h.Sum(digest[:0])
|
||||
return
|
||||
return &state{sfx: 0x06, desc: Sha3Desc[SHA3_512]}
|
||||
}
|
||||
|
@ -121,7 +121,6 @@ func TestKeccakKats(t *testing.T) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
for algo, v := range testShakes {
|
||||
for _, kat := range katSet.Kats[algo] {
|
||||
N, err := hex.DecodeString(kat.N)
|
||||
@ -159,7 +158,7 @@ func TestKeccakKats(t *testing.T) {
|
||||
// small input buffers.
|
||||
func TestUnalignedWrite(t *testing.T) {
|
||||
testUnalignedAndGeneric(t, func(impl string) {
|
||||
buf := sequentialBytes(0x10000)
|
||||
buf := generateData(0x10000)
|
||||
for alg, df := range testDigests {
|
||||
d := df()
|
||||
d.Reset()
|
||||
@ -273,8 +272,16 @@ func TestSqueezing(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
// sequentialBytes produces a buffer of size consecutive bytes 0x00, 0x01, ..., used for testing.
|
||||
func sequentialBytes(size int) []byte {
|
||||
func doSum(h hash.Hash, data []byte) (digest []byte) {
|
||||
half := int(len(data) / 2)
|
||||
h.Write(data[:half])
|
||||
h.Write(data[half:])
|
||||
digest = h.Sum(data[:0])
|
||||
return
|
||||
}
|
||||
|
||||
// generateData produces a buffer of size consecutive bytes 0x00, 0x01, ..., used for testing.
|
||||
func generateData(size int) []byte {
|
||||
result := make([]byte, size)
|
||||
for i := range result {
|
||||
result[i] = byte(i)
|
||||
@ -289,12 +296,12 @@ func TestReset(t *testing.T) {
|
||||
for _, v := range testShakes {
|
||||
// Calculate hash for the first time
|
||||
c := v.constructor([]byte(v.defAlgoName), []byte(v.defCustomStr))
|
||||
c.Write(sequentialBytes(0x100))
|
||||
c.Write(generateData(0x100))
|
||||
c.Read(out1)
|
||||
|
||||
// Calculate hash again
|
||||
c.Reset()
|
||||
c.Write(sequentialBytes(0x100))
|
||||
c.Write(generateData(0x100))
|
||||
c.Read(out2)
|
||||
|
||||
if !bytes.Equal(out1, out2) {
|
||||
@ -306,7 +313,7 @@ func TestReset(t *testing.T) {
|
||||
func TestClone(t *testing.T) {
|
||||
out1 := make([]byte, 16)
|
||||
out2 := make([]byte, 16)
|
||||
in := sequentialBytes(0x100)
|
||||
in := generateData(0x100)
|
||||
|
||||
for _, v := range testShakes {
|
||||
h1 := v.constructor([]byte(v.defAlgoName), []byte(v.defCustomStr))
|
||||
@ -337,19 +344,22 @@ func BenchmarkPermutationFunction(b *testing.B) {
|
||||
}
|
||||
|
||||
// benchmarkHash tests the speed to hash num buffers of buflen each.
|
||||
func benchmarkHash(b *testing.B, h hash.Hash, size, num int) {
|
||||
// This function uses heap
|
||||
func benchmarkHashChunked(b *testing.B, h hash.Hash, size, num int) {
|
||||
b.StopTimer()
|
||||
h.Reset()
|
||||
data := sequentialBytes(size)
|
||||
data := generateData(size)
|
||||
digestBuf := make([]byte, h.Size())
|
||||
b.SetBytes(int64(size * num))
|
||||
b.StartTimer()
|
||||
|
||||
var state []byte
|
||||
for i := 0; i < b.N; i++ {
|
||||
h.Reset()
|
||||
for j := 0; j < num; j++ {
|
||||
h.Write(data)
|
||||
}
|
||||
state = h.Sum(state[:0])
|
||||
digestBuf = h.Sum(digestBuf[:])
|
||||
// needed to avoid alocations
|
||||
digestBuf = digestBuf[:0]
|
||||
}
|
||||
b.StopTimer()
|
||||
h.Reset()
|
||||
@ -359,9 +369,8 @@ func benchmarkHash(b *testing.B, h hash.Hash, size, num int) {
|
||||
// require a copy on reading output.
|
||||
func benchmarkShake(b *testing.B, h ShakeHash, size, num int) {
|
||||
b.StopTimer()
|
||||
h.Reset()
|
||||
data := sequentialBytes(size)
|
||||
d := make([]byte, 32)
|
||||
out := make([]byte, 32)
|
||||
data := generateData(size)
|
||||
|
||||
b.SetBytes(int64(size * num))
|
||||
b.StartTimer()
|
||||
@ -371,21 +380,61 @@ func benchmarkShake(b *testing.B, h ShakeHash, size, num int) {
|
||||
for j := 0; j < num; j++ {
|
||||
h.Write(data)
|
||||
}
|
||||
h.Read(d)
|
||||
h.Read(out[:])
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkSha3_512_MTU(b *testing.B) { benchmarkHash(b, New512(), 1350, 1) }
|
||||
func BenchmarkSha3_384_MTU(b *testing.B) { benchmarkHash(b, New384(), 1350, 1) }
|
||||
func BenchmarkSha3_256_MTU(b *testing.B) { benchmarkHash(b, New256(), 1350, 1) }
|
||||
func BenchmarkSha3_224_MTU(b *testing.B) { benchmarkHash(b, New224(), 1350, 1) }
|
||||
var domainString = []byte("SHAKE")
|
||||
var customString = []byte("CustomString")
|
||||
|
||||
func BenchmarkShake128_MTU(b *testing.B) { benchmarkShake(b, NewShake128(), 1350, 1) }
|
||||
func BenchmarkShake256_MTU(b *testing.B) { benchmarkShake(b, NewShake256(), 1350, 1) }
|
||||
func BenchmarkShake256_16x(b *testing.B) { benchmarkShake(b, NewShake256(), 16, 1024) }
|
||||
func BenchmarkShake256_1MiB(b *testing.B) { benchmarkShake(b, NewShake256(), 1024, 1024) }
|
||||
// benchmarkShake is specialized to the Shake instances, which don't
|
||||
// require a copy on reading output.
|
||||
func benchmarkCShake(b *testing.B, f func(N, S []byte) ShakeHash, size, num int) {
|
||||
b.StopTimer()
|
||||
h := f(domainString, customString)
|
||||
out := make([]byte, 32)
|
||||
data := generateData(size)
|
||||
|
||||
func BenchmarkSha3_512_1MiB(b *testing.B) { benchmarkHash(b, New512(), 1024, 1024) }
|
||||
b.SetBytes(int64(size * num))
|
||||
b.StartTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
h.Reset()
|
||||
for j := 0; j < num; j++ {
|
||||
h.Write(data)
|
||||
}
|
||||
h.Read(out[:])
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkSha3Chunk_x01(b *testing.B) {
|
||||
b.Run("SHA-3/224", func(b *testing.B) { benchmarkHashChunked(b, New224(), 2047, 1) })
|
||||
b.Run("SHA-3/256", func(b *testing.B) { benchmarkHashChunked(b, New256(), 2047, 1) })
|
||||
b.Run("SHA-3/384", func(b *testing.B) { benchmarkHashChunked(b, New384(), 2047, 1) })
|
||||
b.Run("SHA-3/512", func(b *testing.B) { benchmarkHashChunked(b, New512(), 2047, 1) })
|
||||
}
|
||||
|
||||
func BenchmarkSha3Chunk_x16(b *testing.B) {
|
||||
b.Run("SHA-3/224", func(b *testing.B) { benchmarkHashChunked(b, New224(), 16, 1024) })
|
||||
b.Run("SHA-3/256", func(b *testing.B) { benchmarkHashChunked(b, New256(), 16, 1024) })
|
||||
b.Run("SHA-3/384", func(b *testing.B) { benchmarkHashChunked(b, New384(), 16, 1024) })
|
||||
b.Run("SHA-3/512", func(b *testing.B) { benchmarkHashChunked(b, New512(), 16, 1024) })
|
||||
}
|
||||
|
||||
func BenchmarkShake_x01(b *testing.B) {
|
||||
b.Run("SHAKE-128", func(b *testing.B) { benchmarkShake(b, NewShake128(), 1350, 1) })
|
||||
b.Run("SHAKE-256", func(b *testing.B) { benchmarkShake(b, NewShake256(), 1350, 1) })
|
||||
}
|
||||
|
||||
func BenchmarkShake_x16(b *testing.B) {
|
||||
b.Run("SHAKE-128", func(b *testing.B) { benchmarkShake(b, NewShake128(), 16, 1024) })
|
||||
b.Run("SHAKE-256", func(b *testing.B) { benchmarkShake(b, NewShake256(), 16, 1024) })
|
||||
}
|
||||
|
||||
func BenchmarkCShake(b *testing.B) {
|
||||
b.Run("cSHAKE-128", func(b *testing.B) { benchmarkCShake(b, NewCShake128, 2047, 1) })
|
||||
b.Run("cSHAKE-256", func(b *testing.B) { benchmarkCShake(b, NewCShake256, 2047, 1) })
|
||||
}
|
||||
|
||||
func Example_sum() {
|
||||
buf := []byte("some data to hash")
|
||||
@ -446,3 +495,14 @@ func ExampleCShake256() {
|
||||
//a90a4c6ca9af2156eba43dc8398279e6b60dcd56fb21837afe6c308fd4ceb05b9dd98c6ee866ca7dc5a39d53e960f400bcd5a19c8a2d6ec6459f63696543a0d8
|
||||
//85e73a72228d08b46515553ca3a29d47df3047e5d84b12d6c2c63e579f4fd1105716b7838e92e981863907f434bfd4443c9e56ea09da998d2f9b47db71988109
|
||||
}
|
||||
|
||||
func ExampleSum256() {
|
||||
d := generateData(32)
|
||||
var data [32]byte
|
||||
h := New256()
|
||||
h.Write(d)
|
||||
s1 := h.Sum(data[:0])
|
||||
fmt.Printf("%X\n", s1)
|
||||
//Output:
|
||||
// 050A48733BD5C2756BA95C5828CC83EE16FABCD3C086885B7744F84A0F9E0D94
|
||||
}
|
||||
|
@ -6,14 +6,6 @@ package sha3
|
||||
|
||||
// SHAKE128 and SHAKE256 are FIPS approved XOFs. The cSHAKE128/256
|
||||
// are SHAKE-based XOFs supporting domain separation.
|
||||
//
|
||||
//
|
||||
// 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"
|
||||
"io"
|
||||
@ -44,16 +36,15 @@ type cshakeState struct {
|
||||
|
||||
// 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.
|
||||
// by the method specified in 3.3 of [1] and padded with bytepad function.
|
||||
// Used by Reset() to restore initial state.
|
||||
initBlock []byte
|
||||
}
|
||||
|
||||
// Consts for configuring initial SHA-3 state
|
||||
const (
|
||||
dsbyteShake = 0x1f
|
||||
dsbyteCShake = 0x04
|
||||
sfxShake = 0x1f
|
||||
sfxCShake = 0x04
|
||||
rate128 = 168
|
||||
rate256 = 136
|
||||
)
|
||||
@ -80,49 +71,51 @@ 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, sfx byte, shaId uint8) ShakeHash {
|
||||
c := cshakeState{state: state{sfx: sfx, desc: Sha3Desc[shaId]}}
|
||||
|
||||
// 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))
|
||||
b := make([]byte, 0, 9*2+len(N)+len(S))
|
||||
b = append(b, leftEncode(uint64(len(N)*8))...)
|
||||
b = append(b, N...)
|
||||
b = append(b, leftEncode(uint64(len(S)*8))...)
|
||||
b = append(b, S...)
|
||||
c.initBlock = bytepad(b, c.BlockSize())
|
||||
c.Write(c.initBlock)
|
||||
return &c
|
||||
}
|
||||
|
||||
// Reset resets the hash to initial state.
|
||||
func (c *cshakeState) Reset() {
|
||||
c.state.Reset()
|
||||
c.Write(bytepad(c.initBlock, c.rate))
|
||||
c.Write(c.initBlock)
|
||||
}
|
||||
|
||||
// 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{state: *c.clone(), initBlock: b}
|
||||
return &cshakeState{state: c.state, initBlock: b}
|
||||
}
|
||||
|
||||
// Clone returns copy of SHAKE context within its current state.
|
||||
func (c *state) Clone() ShakeHash {
|
||||
return c.clone()
|
||||
dup := *c
|
||||
return &dup
|
||||
}
|
||||
|
||||
// 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 {
|
||||
return &state{rate: rate128, dsbyte: dsbyteShake}
|
||||
return &state{sfx: sfxShake, desc: Sha3Desc[SHAKE128]}
|
||||
}
|
||||
|
||||
// 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 {
|
||||
return &state{rate: rate256, dsbyte: dsbyteShake}
|
||||
return &state{sfx: sfxShake, desc: Sha3Desc[SHAKE256]}
|
||||
}
|
||||
|
||||
// NewCShake128 creates a new instance of cSHAKE128 variable-output-length ShakeHash,
|
||||
@ -135,7 +128,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, sfxCShake, SHAKE128)
|
||||
}
|
||||
|
||||
// NewCShake256 creates a new instance of cSHAKE256 variable-output-length ShakeHash,
|
||||
@ -148,7 +141,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, sfxCShake, SHAKE256)
|
||||
}
|
||||
|
||||
// ShakeSum128 writes an arbitrary-length digest of data into hash.
|
||||
|
@ -7,7 +7,9 @@
|
||||
|
||||
package sha3
|
||||
|
||||
import "unsafe"
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// A storageBuf is an aligned array of maxRate bytes.
|
||||
type storageBuf [maxRate / 8]uint64
|
||||
@ -57,6 +59,7 @@ func copyOutUnaligned(d *state, buf []byte) {
|
||||
copy(buf, ab[:])
|
||||
}
|
||||
|
||||
// TODO: remove this assignment
|
||||
var (
|
||||
xorIn = xorInUnaligned
|
||||
copyOut = copyOutUnaligned
|
||||
|
@ -76,7 +76,7 @@ func (d *digest) Write(input []byte) (nn int, err error) {
|
||||
|
||||
// this eventually could be done in d.compress
|
||||
copy(d.b[:], input[nblocks*d.BlockSize():])
|
||||
return
|
||||
return len(input), nil
|
||||
}
|
||||
|
||||
func (d *digest) Sum(in []byte) []byte {
|
||||
|
Loading…
Reference in New Issue
Block a user