mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-05-24 08:49:29 +00:00
233 lines
5.3 KiB
Go
233 lines
5.3 KiB
Go
//go:build (amd64 || arm64) && !purego
|
|
|
|
package keccak
|
|
|
|
import (
|
|
"encoding/binary"
|
|
|
|
"golang.org/x/crypto/sha3"
|
|
)
|
|
|
|
// useASM is set by platform-specific init to indicate hardware acceleration is available.
|
|
// When false, Sum256 and Hasher fall back to x/crypto/sha3.
|
|
var useASM bool
|
|
|
|
// sponge is the core Keccak-256 sponge state used by native (asm) implementations.
|
|
type sponge struct {
|
|
state [200]byte
|
|
buf [rate]byte
|
|
absorbed int
|
|
squeezing bool
|
|
readIdx int // index into state for next Read byte
|
|
}
|
|
|
|
// Reset resets the sponge to its initial state.
|
|
func (s *sponge) Reset() {
|
|
s.state = [200]byte{}
|
|
s.absorbed = 0
|
|
s.squeezing = false
|
|
s.readIdx = 0
|
|
}
|
|
|
|
// Write absorbs data into the sponge.
|
|
// Panics if called after Read.
|
|
func (s *sponge) Write(p []byte) (int, error) {
|
|
if s.squeezing {
|
|
panic("keccak: Write after Read")
|
|
}
|
|
n := len(p)
|
|
if s.absorbed > 0 {
|
|
x := copy(s.buf[s.absorbed:rate], p)
|
|
s.absorbed += x
|
|
p = p[x:]
|
|
if s.absorbed == rate {
|
|
xorAndPermute(&s.state, &s.buf[0])
|
|
s.absorbed = 0
|
|
}
|
|
}
|
|
|
|
for len(p) >= rate {
|
|
xorAndPermute(&s.state, &p[0])
|
|
p = p[rate:]
|
|
}
|
|
|
|
if len(p) > 0 {
|
|
s.absorbed = copy(s.buf[:], p)
|
|
}
|
|
return n, nil
|
|
}
|
|
|
|
// Sum256 finalizes and returns the 32-byte Keccak-256 digest.
|
|
// Does not modify the sponge state.
|
|
// Panics if called after Read.
|
|
func (s *sponge) Sum256() [32]byte {
|
|
if s.squeezing {
|
|
panic("keccak: Sum after Read")
|
|
}
|
|
state := s.state
|
|
xorIn(&state, s.buf[:s.absorbed])
|
|
state[s.absorbed] ^= 0x01
|
|
state[rate-1] ^= 0x80
|
|
keccakF1600(&state)
|
|
return [32]byte(state[:32])
|
|
}
|
|
|
|
// Sum appends the current Keccak-256 digest to b and returns the resulting slice.
|
|
// Does not modify the sponge state.
|
|
func (s *sponge) Sum(b []byte) []byte {
|
|
d := s.Sum256()
|
|
return append(b, d[:]...)
|
|
}
|
|
|
|
// Size returns the number of bytes Sum will produce (32).
|
|
func (s *sponge) Size() int { return 32 }
|
|
|
|
// BlockSize returns the sponge rate in bytes (136).
|
|
func (s *sponge) BlockSize() int { return rate }
|
|
|
|
// Read squeezes an arbitrary number of bytes from the sponge.
|
|
// On the first call, it pads and permutes, transitioning from absorbing to squeezing.
|
|
// Subsequent calls to Write will panic. It never returns an error.
|
|
func (s *sponge) Read(out []byte) (int, error) {
|
|
if !s.squeezing {
|
|
s.padAndSqueeze()
|
|
}
|
|
|
|
n := len(out)
|
|
for len(out) > 0 {
|
|
x := copy(out, s.state[s.readIdx:rate])
|
|
s.readIdx += x
|
|
out = out[x:]
|
|
if s.readIdx == rate {
|
|
keccakF1600(&s.state)
|
|
s.readIdx = 0
|
|
}
|
|
}
|
|
return n, nil
|
|
}
|
|
|
|
func (s *sponge) padAndSqueeze() {
|
|
xorIn(&s.state, s.buf[:s.absorbed])
|
|
s.state[s.absorbed] ^= 0x01
|
|
s.state[rate-1] ^= 0x80
|
|
keccakF1600(&s.state)
|
|
s.squeezing = true
|
|
s.readIdx = 0
|
|
}
|
|
|
|
// sum256Sponge computes Keccak-256 in one shot using the assembly permutation.
|
|
func sum256Sponge(data []byte) [32]byte {
|
|
var state [200]byte
|
|
|
|
for len(data) >= rate {
|
|
xorAndPermute(&state, &data[0])
|
|
data = data[rate:]
|
|
}
|
|
|
|
xorIn(&state, data)
|
|
state[len(data)] ^= 0x01
|
|
state[rate-1] ^= 0x80
|
|
keccakF1600(&state)
|
|
|
|
return [32]byte(state[:32])
|
|
}
|
|
|
|
// Sum256 computes the Keccak-256 hash of data. Zero heap allocations when hardware
|
|
// acceleration is available.
|
|
func Sum256(data []byte) [32]byte {
|
|
if !useASM {
|
|
return sum256XCrypto(data)
|
|
}
|
|
return sum256Sponge(data)
|
|
}
|
|
|
|
func sum256XCrypto(data []byte) [32]byte {
|
|
h := sha3.NewLegacyKeccak256()
|
|
h.Write(data)
|
|
var out [32]byte
|
|
h.Sum(out[:0])
|
|
return out
|
|
}
|
|
|
|
// Hasher is a streaming Keccak-256 hasher.
|
|
// Uses platform assembly when available, x/crypto/sha3 otherwise.
|
|
type Hasher struct {
|
|
sponge
|
|
xc KeccakState // x/crypto fallback
|
|
}
|
|
|
|
// Reset resets the hasher to its initial state.
|
|
func (h *Hasher) Reset() {
|
|
if useASM {
|
|
h.sponge.Reset()
|
|
} else {
|
|
if h.xc == nil {
|
|
h.xc = sha3.NewLegacyKeccak256().(KeccakState)
|
|
} else {
|
|
h.xc.Reset()
|
|
}
|
|
}
|
|
}
|
|
|
|
// Write absorbs data into the hasher.
|
|
// Panics if called after Read.
|
|
func (h *Hasher) Write(p []byte) (int, error) {
|
|
if !useASM {
|
|
if h.xc == nil {
|
|
h.xc = sha3.NewLegacyKeccak256().(KeccakState)
|
|
}
|
|
return h.xc.Write(p)
|
|
}
|
|
return h.sponge.Write(p)
|
|
}
|
|
|
|
// Sum256 finalizes and returns the 32-byte Keccak-256 digest.
|
|
// Does not modify the hasher state.
|
|
func (h *Hasher) Sum256() [32]byte {
|
|
if !useASM {
|
|
if h.xc == nil {
|
|
return Sum256(nil)
|
|
}
|
|
var out [32]byte
|
|
h.xc.Sum(out[:0])
|
|
return out
|
|
}
|
|
return h.sponge.Sum256()
|
|
}
|
|
|
|
// Sum appends the current Keccak-256 digest to b and returns the resulting slice.
|
|
// Does not modify the hasher state.
|
|
func (h *Hasher) Sum(b []byte) []byte {
|
|
if !useASM {
|
|
if h.xc == nil {
|
|
d := Sum256(nil)
|
|
return append(b, d[:]...)
|
|
}
|
|
return h.xc.Sum(b)
|
|
}
|
|
return h.sponge.Sum(b)
|
|
}
|
|
|
|
// Read squeezes an arbitrary number of bytes from the sponge.
|
|
// On the first call, it pads and permutes, transitioning from absorbing to squeezing.
|
|
// Subsequent calls to Write will panic. It never returns an error.
|
|
func (h *Hasher) Read(out []byte) (int, error) {
|
|
if !useASM {
|
|
if h.xc == nil {
|
|
h.xc = sha3.NewLegacyKeccak256().(KeccakState)
|
|
}
|
|
return h.xc.Read(out)
|
|
}
|
|
return h.sponge.Read(out)
|
|
}
|
|
|
|
// xorIn XORs data into the first len(data) bytes of state using uint64 loads.
|
|
func xorIn(state *[200]byte, data []byte) {
|
|
for i := 0; i+8 <= len(data); i += 8 {
|
|
v := binary.LittleEndian.Uint64(state[i:]) ^ binary.LittleEndian.Uint64(data[i:])
|
|
binary.LittleEndian.PutUint64(state[i:], v)
|
|
}
|
|
for i := len(data) &^ 7; i < len(data); i++ {
|
|
state[i] ^= data[i]
|
|
}
|
|
}
|