// 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) }