You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

413 lines
8.2 KiB

// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package echo
import (
"fmt"
"gitlab.com/nitya-sattva/go-x11/aesr"
"gitlab.com/nitya-sattva/go-x11/hash"
)
// HashSize holds the size of a hash in bytes.
const HashSize = uintptr(64)
// BlockSize holds the size of a block in bytes.
const BlockSize = uintptr(128)
////////////////
type digest struct {
ptr uintptr
h [8][2]uint64
c [4]uint32
b [BlockSize]byte
}
// New returns a new digest compute a ECHO512 hash.
func New() hash.Digest {
ref := &digest{}
ref.Reset()
return ref
}
////////////////
// Reset resets the digest to its initial state.
func (ref *digest) Reset() {
ref.ptr = 0
ref.h[0][0] = uint64(8 * HashSize)
ref.h[0][1] = 0
ref.h[1][0] = uint64(8 * HashSize)
ref.h[1][1] = 0
ref.h[2][0] = uint64(8 * HashSize)
ref.h[2][1] = 0
ref.h[3][0] = uint64(8 * HashSize)
ref.h[3][1] = 0
ref.h[4][0] = uint64(8 * HashSize)
ref.h[4][1] = 0
ref.h[5][0] = uint64(8 * HashSize)
ref.h[5][1] = 0
ref.h[6][0] = uint64(8 * HashSize)
ref.h[6][1] = 0
ref.h[7][0] = uint64(8 * HashSize)
ref.h[7][1] = 0
memset32(ref.c[:], 0)
}
// Sum appends the current hash to dst and returns the result
// as a slice. It does not change the underlying hash state.
func (ref *digest) Sum(dst []byte) []byte {
dgt := *ref
hsh := [64]byte{}
dgt.Close(hsh[:], 0, 0)
return append(dst, hsh[:]...)
}
// Write more data to the running hash, never returns an error.
func (ref *digest) Write(src []byte) (int, error) {
sln := uintptr(len(src))
fln := len(src)
ptr := ref.ptr
if sln < (BlockSize - ptr) {
copy(ref.b[ptr:], src)
ref.ptr += sln
return int(sln), nil
}
for sln > 0 {
cln := BlockSize - ptr
if cln > sln {
cln = sln
}
sln -= cln
copy(ref.b[ptr:], src[:cln])
src = src[cln:]
ptr += cln
if ptr == BlockSize {
ref.c[0] += 1024
if ref.c[0] < 1024 {
ref.c[1] += 1
if ref.c[1] == 0 {
ref.c[2] += 1
if ref.c[2] == 0 {
ref.c[3] += 1
}
}
}
compress(ref)
ptr = 0
}
}
ref.ptr = ptr
return fln, nil
}
// Close the digest by writing the last bits and storing the hash
// in dst. This prepares the digest for reuse by calling reset. A call
// to Close with a dst that is smaller then HashSize will return an error.
func (ref *digest) Close(dst []byte, bits uint8, bcnt uint8) error {
if ln := len(dst); HashSize > uintptr(ln) {
return fmt.Errorf("Echo Close: dst min length: %d, got %d", HashSize, ln)
}
ptr := ref.ptr
buf := ref.b[:]
eln := uint32(ptr<<3) + uint32(bcnt)
ref.c[0] += eln
if ref.c[0] < eln {
ref.c[1] += 1
if ref.c[1] == 0 {
ref.c[2] += 1
if ref.c[2] == 0 {
ref.c[3] += 1
}
}
}
var tmp [64]uint8
encUInt32le(tmp[:], ref.c[0])
encUInt32le(tmp[4:], ref.c[1])
encUInt32le(tmp[8:], ref.c[2])
encUInt32le(tmp[12:], ref.c[3])
if eln == 0 {
memset32(ref.c[:], 0)
}
{
off := uint8(0x80) >> bcnt
buf[ptr] = uint8((bits & -off) | off)
}
ptr += 1
memset8(buf[ptr:], 0)
if ptr > (BlockSize - 18) {
compress(ref)
memset8(buf[:], 0)
memset32(ref.c[:], 0)
}
encUInt16le(buf[(BlockSize-18):], uint16(16<<5))
copy(buf[(BlockSize-16):], tmp[:])
compress(ref)
h := ref.h[:]
for x := uintptr(0); x < 4; x++ {
for y := uintptr(0); y < 2; y++ {
encUInt64le(dst[(((x*2)+y)*8):], h[x][y])
}
}
ref.Reset()
return nil
}
// Size returns the number of bytes required to store the hash.
func (*digest) Size() int {
return int(HashSize)
}
// BlockSize returns the block size of the hash.
func (*digest) BlockSize() int {
return int(BlockSize)
}
////////////////
func memset8(dst []byte, src byte) {
for i := range dst {
dst[i] = src
}
}
func memset32(dst []uint32, src uint32) {
for i := range dst {
dst[i] = src
}
}
func compress(ref *digest) {
var w [16][2]uint64
k0 := ref.c[0]
k1 := ref.c[1]
k2 := ref.c[2]
k3 := ref.c[3]
for i := uintptr(0); i < 8; i++ {
w[i][0] = ref.h[i][0]
w[i+8][0] = decUInt64le(ref.b[(16 * i):])
w[i][1] = ref.h[i][1]
w[i+8][1] = decUInt64le(ref.b[((16 * i) + 8):])
}
var a0, a1, a2 uint64
var b0, b1, b2 uint64
var w0, w1, w2, w3 uint64
var x0, x1, x2, x3 uint32
var y0, y1, y2, y3 uint32
for u := uintptr(0); u < 10; u++ {
for n := uintptr(0); n < 16; n++ {
x0 = uint32(w[n][0])
x1 = uint32(w[n][0] >> 32)
x2 = uint32(w[n][1])
x3 = uint32(w[n][1] >> 32)
y0, y1, y2, y3 = aesr.Round32ble(
x0, x1, x2, x3,
k0, k1, k2, k3,
)
x0, x1, x2, x3 = aesr.Round32ble(
y0, y1, y2, y3,
0, 0, 0, 0,
)
w[n][0] = uint64(x0) | (uint64(x1) << 32)
w[n][1] = uint64(x2) | (uint64(x3) << 32)
k0 += 1
if k0 == 0 {
k1 += 1
if k1 == 0 {
k2 += 1
if k2 == 0 {
k3 += 1
}
}
}
}
a0 = w[1][0]
w[1][0] = w[5][0]
w[5][0] = w[9][0]
w[9][0] = w[13][0]
w[13][0] = a0
a0 = w[1][1]
w[1][1] = w[5][1]
w[5][1] = w[9][1]
w[9][1] = w[13][1]
w[13][1] = a0
a0 = w[2][0]
w[2][0] = w[10][0]
w[10][0] = a0
a0 = w[6][0]
w[6][0] = w[14][0]
w[14][0] = a0
a0 = w[2][1]
w[2][1] = w[10][1]
w[10][1] = a0
a0 = w[6][1]
w[6][1] = w[14][1]
w[14][1] = a0
a0 = w[15][0]
w[15][0] = w[11][0]
w[11][0] = w[7][0]
w[7][0] = w[3][0]
w[3][0] = a0
a0 = w[15][1]
w[15][1] = w[11][1]
w[11][1] = w[7][1]
w[7][1] = w[3][1]
w[3][1] = a0
for n := uintptr(0); n < 2; n++ {
w0 = w[0][n]
w1 = w[1][n]
w2 = w[2][n]
w3 = w[3][n]
a0 = w0 ^ w1
a1 = w1 ^ w2
a2 = w2 ^ w3
b0 = (((a0&uint64(0x8080808080808080))>>7)*27 ^
((a0 & uint64(0x7F7F7F7F7F7F7F7F)) << 1))
b1 = (((a1&uint64(0x8080808080808080))>>7)*27 ^
((a1 & uint64(0x7F7F7F7F7F7F7F7F)) << 1))
b2 = (((a2&uint64(0x8080808080808080))>>7)*27 ^
((a2 & uint64(0x7F7F7F7F7F7F7F7F)) << 1))
w[0][n] = b0 ^ a1 ^ w3
w[1][n] = b1 ^ w0 ^ a2
w[2][n] = b2 ^ a0 ^ w3
w[3][n] = b0 ^ b1 ^ b2 ^ a0 ^ w2
w0 = w[4][n]
w1 = w[5][n]
w2 = w[6][n]
w3 = w[7][n]
a0 = w0 ^ w1
a1 = w1 ^ w2
a2 = w2 ^ w3
b0 = (((a0&uint64(0x8080808080808080))>>7)*27 ^
((a0 & uint64(0x7F7F7F7F7F7F7F7F)) << 1))
b1 = (((a1&uint64(0x8080808080808080))>>7)*27 ^
((a1 & uint64(0x7F7F7F7F7F7F7F7F)) << 1))
b2 = (((a2&uint64(0x8080808080808080))>>7)*27 ^
((a2 & uint64(0x7F7F7F7F7F7F7F7F)) << 1))
w[4][n] = b0 ^ a1 ^ w3
w[5][n] = b1 ^ w0 ^ a2
w[6][n] = b2 ^ a0 ^ w3
w[7][n] = b0 ^ b1 ^ b2 ^ a0 ^ w2
w0 = w[8][n]
w1 = w[9][n]
w2 = w[10][n]
w3 = w[11][n]
a0 = w0 ^ w1
a1 = w1 ^ w2
a2 = w2 ^ w3
b0 = (((a0&uint64(0x8080808080808080))>>7)*27 ^
((a0 & uint64(0x7F7F7F7F7F7F7F7F)) << 1))
b1 = (((a1&uint64(0x8080808080808080))>>7)*27 ^
((a1 & uint64(0x7F7F7F7F7F7F7F7F)) << 1))
b2 = (((a2&uint64(0x8080808080808080))>>7)*27 ^
((a2 & uint64(0x7F7F7F7F7F7F7F7F)) << 1))
w[8][n] = b0 ^ a1 ^ w3
w[9][n] = b1 ^ w0 ^ a2
w[10][n] = b2 ^ a0 ^ w3
w[11][n] = b0 ^ b1 ^ b2 ^ a0 ^ w2
w0 = w[12][n]
w1 = w[13][n]
w2 = w[14][n]
w3 = w[15][n]
a0 = w0 ^ w1
a1 = w1 ^ w2
a2 = w2 ^ w3
b0 = (((a0&uint64(0x8080808080808080))>>7)*27 ^
((a0 & uint64(0x7F7F7F7F7F7F7F7F)) << 1))
b1 = (((a1&uint64(0x8080808080808080))>>7)*27 ^
((a1 & uint64(0x7F7F7F7F7F7F7F7F)) << 1))
b2 = (((a2&uint64(0x8080808080808080))>>7)*27 ^
((a2 & uint64(0x7F7F7F7F7F7F7F7F)) << 1))
w[12][n] = b0 ^ a1 ^ w3
w[13][n] = b1 ^ w0 ^ a2
w[14][n] = b2 ^ a0 ^ w3
w[15][n] = b0 ^ b1 ^ b2 ^ a0 ^ w2
}
}
h := ref.h[:]
for x := uintptr(0); x < 8; x++ {
for y := uintptr(0); y < 2; y++ {
h[x][y] ^= decUInt64le(ref.b[(((x*2)+y)*8):]) ^ w[x][y] ^ w[x+8][y]
}
}
}
func decUInt64le(src []byte) uint64 {
return (uint64(src[0]) |
uint64(src[1])<<8 |
uint64(src[2])<<16 |
uint64(src[3])<<24 |
uint64(src[4])<<32 |
uint64(src[5])<<40 |
uint64(src[6])<<48 |
uint64(src[7])<<56)
}
func encUInt16le(dst []byte, src uint16) {
dst[0] = uint8(src)
dst[1] = uint8(src >> 8)
}
func encUInt32le(dst []uint8, src uint32) {
dst[0] = uint8(src)
dst[1] = uint8(src >> 8)
dst[2] = uint8(src >> 16)
dst[3] = uint8(src >> 24)
}
func encUInt64le(dst []byte, src uint64) {
dst[0] = uint8(src)
dst[1] = uint8(src >> 8)
dst[2] = uint8(src >> 16)
dst[3] = uint8(src >> 24)
dst[4] = uint8(src >> 32)
dst[5] = uint8(src >> 40)
dst[6] = uint8(src >> 48)
dst[7] = uint8(src >> 56)
}