forked from cerc-io/ipld-eth-server
225 lines
3.9 KiB
Go
225 lines
3.9 KiB
Go
// Package keccak implements the Keccak (SHA-3) hash algorithm.
|
|
// http://keccak.noekeon.org.
|
|
package keccakpg
|
|
|
|
import (
|
|
_ "fmt"
|
|
"hash"
|
|
)
|
|
|
|
const stdRounds = 24
|
|
|
|
var roundConstants = []uint64{
|
|
0x0000000000000001, 0x0000000000008082,
|
|
0x800000000000808A, 0x8000000080008000,
|
|
0x000000000000808B, 0x0000000080000001,
|
|
0x8000000080008081, 0x8000000000008009,
|
|
0x000000000000008A, 0x0000000000000088,
|
|
0x0000000080008009, 0x000000008000000A,
|
|
0x000000008000808B, 0x800000000000008B,
|
|
0x8000000000008089, 0x8000000000008003,
|
|
0x8000000000008002, 0x8000000000000080,
|
|
0x000000000000800A, 0x800000008000000A,
|
|
0x8000000080008081, 0x8000000000008080,
|
|
0x0000000080000001, 0x8000000080008008,
|
|
}
|
|
|
|
var rotationConstants = [24]uint{
|
|
1, 3, 6, 10, 15, 21, 28, 36,
|
|
45, 55, 2, 14, 27, 41, 56, 8,
|
|
25, 43, 62, 18, 39, 61, 20, 44,
|
|
}
|
|
|
|
var piLane = [24]uint{
|
|
10, 7, 11, 17, 18, 3, 5, 16,
|
|
8, 21, 24, 4, 15, 23, 19, 13,
|
|
12, 2, 20, 14, 22, 9, 6, 1,
|
|
}
|
|
|
|
type keccak struct {
|
|
S [25]uint64
|
|
size int
|
|
blockSize int
|
|
rounds int
|
|
buf []byte
|
|
}
|
|
|
|
func newKeccak(bitlen, rounds int) hash.Hash {
|
|
var h keccak
|
|
h.size = bitlen / 8
|
|
h.blockSize = (200 - 2*h.size)
|
|
h.rounds = rounds
|
|
if rounds != stdRounds {
|
|
//fmt.Printf("keccak: warning non standard number of rounds %d vs %d\n", rounds, stdRounds)
|
|
}
|
|
return &h
|
|
}
|
|
|
|
func NewCustom(bits, rounds int) hash.Hash {
|
|
return newKeccak(bits, rounds)
|
|
}
|
|
|
|
func New160() hash.Hash {
|
|
return newKeccak(160, stdRounds)
|
|
}
|
|
|
|
func New224() hash.Hash {
|
|
return newKeccak(224, stdRounds)
|
|
}
|
|
|
|
func New256() hash.Hash {
|
|
return newKeccak(256, stdRounds)
|
|
}
|
|
|
|
func New384() hash.Hash {
|
|
return newKeccak(384, stdRounds)
|
|
}
|
|
|
|
func New512() hash.Hash {
|
|
return newKeccak(512, stdRounds)
|
|
}
|
|
|
|
func (k *keccak) Write(b []byte) (int, error) {
|
|
n := len(b)
|
|
|
|
if len(k.buf) > 0 {
|
|
x := k.blockSize - len(k.buf)
|
|
if x > len(b) {
|
|
x = len(b)
|
|
}
|
|
k.buf = append(k.buf, b[:x]...)
|
|
b = b[x:]
|
|
|
|
if len(k.buf) < k.blockSize {
|
|
return n, nil
|
|
}
|
|
|
|
k.f(k.buf)
|
|
k.buf = nil
|
|
}
|
|
|
|
for len(b) >= k.blockSize {
|
|
k.f(b[:k.blockSize])
|
|
b = b[k.blockSize:]
|
|
}
|
|
|
|
k.buf = b
|
|
|
|
return n, nil
|
|
}
|
|
|
|
func (k0 *keccak) Sum(b []byte) []byte {
|
|
|
|
k := *k0
|
|
|
|
last := k.pad(k.buf)
|
|
k.f(last)
|
|
|
|
buf := make([]byte, len(k.S)*8)
|
|
for i := range k.S {
|
|
putUint64le(buf[i*8:], k.S[i])
|
|
}
|
|
return append(b, buf[:k.size]...)
|
|
}
|
|
|
|
func (k *keccak) Reset() {
|
|
for i := range k.S {
|
|
k.S[i] = 0
|
|
}
|
|
k.buf = nil
|
|
}
|
|
|
|
func (k *keccak) Size() int {
|
|
return k.size
|
|
}
|
|
|
|
func (k *keccak) BlockSize() int {
|
|
return k.blockSize
|
|
}
|
|
|
|
func rotl64(x uint64, n uint) uint64 {
|
|
return (x << n) | (x >> (64 - n))
|
|
}
|
|
|
|
func (k *keccak) f(block []byte) {
|
|
|
|
if len(block) != k.blockSize {
|
|
panic("f() called with invalid block size")
|
|
}
|
|
|
|
for i := 0; i < k.blockSize/8; i++ {
|
|
k.S[i] ^= uint64le(block[i*8:])
|
|
}
|
|
|
|
for r := 0; r < k.rounds; r++ {
|
|
var bc [5]uint64
|
|
|
|
// theta
|
|
for i := range bc {
|
|
bc[i] = k.S[i] ^ k.S[5+i] ^ k.S[10+i] ^ k.S[15+i] ^ k.S[20+i]
|
|
}
|
|
for i := range bc {
|
|
t := bc[(i+4)%5] ^ rotl64(bc[(i+1)%5], 1)
|
|
for j := 0; j < len(k.S); j += 5 {
|
|
k.S[i+j] ^= t
|
|
}
|
|
}
|
|
|
|
// rho phi
|
|
temp := k.S[1]
|
|
for i := range piLane {
|
|
j := piLane[i]
|
|
temp2 := k.S[j]
|
|
k.S[j] = rotl64(temp, rotationConstants[i])
|
|
temp = temp2
|
|
}
|
|
|
|
// chi
|
|
for j := 0; j < len(k.S); j += 5 {
|
|
for i := range bc {
|
|
bc[i] = k.S[j+i]
|
|
}
|
|
for i := range bc {
|
|
k.S[j+i] ^= (^bc[(i+1)%5]) & bc[(i+2)%5]
|
|
}
|
|
}
|
|
|
|
// iota
|
|
k.S[0] ^= roundConstants[r]
|
|
}
|
|
}
|
|
|
|
func (k *keccak) pad(block []byte) []byte {
|
|
|
|
padded := make([]byte, k.blockSize)
|
|
|
|
copy(padded, k.buf)
|
|
padded[len(k.buf)] = 0x01
|
|
padded[len(padded)-1] |= 0x80
|
|
|
|
return padded
|
|
}
|
|
|
|
func uint64le(v []byte) uint64 {
|
|
return uint64(v[0]) |
|
|
uint64(v[1])<<8 |
|
|
uint64(v[2])<<16 |
|
|
uint64(v[3])<<24 |
|
|
uint64(v[4])<<32 |
|
|
uint64(v[5])<<40 |
|
|
uint64(v[6])<<48 |
|
|
uint64(v[7])<<56
|
|
|
|
}
|
|
|
|
func putUint64le(v []byte, x uint64) {
|
|
v[0] = byte(x)
|
|
v[1] = byte(x >> 8)
|
|
v[2] = byte(x >> 16)
|
|
v[3] = byte(x >> 24)
|
|
v[4] = byte(x >> 32)
|
|
v[5] = byte(x >> 40)
|
|
v[6] = byte(x >> 48)
|
|
v[7] = byte(x >> 56)
|
|
}
|