// Use of this source code is governed by an ISC // license that can be found in the LICENSE file. package skein import ( "fmt" "gitlab.com/nitya-sattva/go-x11/hash" ) // HashSize holds the size of a hash in bytes. const HashSize = int(64) // BlockSize holds the size of a block in bytes. const BlockSize = uintptr(64) //////////////// type digest struct { ptr uintptr cnt uint64 h [8]uint64 b [BlockSize]byte } // New returns a new digest to compute a BLAKE512 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, ref.cnt = 0, 0 copy(ref.h[:], kInit[:]) } // 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 } cnt := ref.cnt b := ref.b[:] h := [27]uint64{} h[0] = ref.h[0] h[1] = ref.h[1] h[2] = ref.h[2] h[3] = ref.h[3] h[4] = ref.h[4] h[5] = ref.h[5] h[6] = ref.h[6] h[7] = ref.h[7] var first uint8 if cnt == 0 { first = uint8(1 << 7) } if ptr == BlockSize { cnt += 1 compress(b, h[:], uint64(96+first), cnt, 0) first = 0 ptr = 0 } cln := BlockSize - ptr if cln > sln { cln = sln } sln -= cln copy(b[ptr:], src[:cln]) src = src[cln:] ptr += cln for sln > 0 { if ptr == BlockSize { cnt += 1 compress(b, h[:], uint64(96+first), cnt, 0) first = 0 ptr = 0 } cln := BlockSize - ptr if cln > sln { cln = sln } sln -= cln copy(b[ptr:], src[:cln]) src = src[cln:] ptr += cln } ref.h[0] = h[0] ref.h[1] = h[1] ref.h[2] = h[2] ref.h[3] = h[3] ref.h[4] = h[4] ref.h[5] = h[5] ref.h[6] = h[6] ref.h[7] = h[7] ref.ptr = ptr ref.cnt = cnt 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 > ln { return fmt.Errorf("Skein Close: dst min length: %d, got %d", HashSize, ln) } if bcnt != 0 { off := uint8(0x80) >> bcnt x := [1]uint8{uint8((bits & -off) | off)} ref.Write(x[:]) } ptr := ref.ptr cnt := ref.cnt b := ref.b[:] h := [27]uint64{} h[0] = ref.h[0] h[1] = ref.h[1] h[2] = ref.h[2] h[3] = ref.h[3] h[4] = ref.h[4] h[5] = ref.h[5] h[6] = ref.h[6] h[7] = ref.h[7] memset(b[ptr:ptr+(BlockSize-ptr)], 0) var etv uint64 if cnt == 0 { etv = 352 + uint64(1<<7) } else { etv = 352 } if bcnt != 0 { etv += 1 } for i := uintptr(0); i < 2; i++ { compress(b, h[:], etv, cnt, ptr) if i == 0 { memset(b[:], 0) etv = 510 ptr = 8 cnt = 0 } } for u := uintptr(0); u < 64; u += 8 { encUInt64le(dst[u:], h[u>>3]) } ref.Reset() return nil } // Size returns the number of bytes required to store the hash. func (*digest) Size() int { return HashSize } // BlockSize returns the block size of the hash. func (*digest) BlockSize() int { return int(BlockSize) } //////////////// func memset(dst []byte, src byte) { for i := range dst { dst[i] = src } } 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 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) } func compress(buf []uint8, h []uint64, etv, cnt uint64, ext uintptr) { var t0, t1, t2 uint64 m0 := decUInt64le(buf[0:]) m1 := decUInt64le(buf[8:]) m2 := decUInt64le(buf[16:]) m3 := decUInt64le(buf[24:]) m4 := decUInt64le(buf[32:]) m5 := decUInt64le(buf[40:]) m6 := decUInt64le(buf[48:]) m7 := decUInt64le(buf[56:]) p0 := m0 p1 := m1 p2 := m2 p3 := m3 p4 := m4 p5 := m5 p6 := m6 p7 := m7 t0 = uint64(cnt<<6) + uint64(ext) t1 = (cnt >> 58) + (uint64(etv) << 55) t2 = t0 ^ t1 h[8] = (((h[0] ^ h[1]) ^ (h[2] ^ h[3])) ^ ((h[4] ^ h[5]) ^ (h[6] ^ h[7])) ^ uint64(0x1BD11BDAA9FC1A22)) for u := uintptr(0); u <= 15; u += 3 { h[u+9] = h[u+0] h[u+10] = h[u+1] h[u+11] = h[u+2] } var tmp uint64 for u := uintptr(0); u < 9; u++ { s := uint64(u << 1) p0 = uint64(p0 + h[s+0]) p1 = uint64(p1 + h[s+1]) p2 = uint64(p2 + h[s+2]) p3 = uint64(p3 + h[s+3]) p4 = uint64(p4 + h[s+4]) p5 = uint64(p5 + h[s+5] + t0) p6 = uint64(p6 + h[s+6] + t1) p7 = uint64(p7 + h[s+7] + s) p0 = (p0 + p1) p1 = ((p1 << 46) | (p1 >> (64 - 46))) ^ p0 p2 = (p2 + p3) p3 = ((p3 << 36) | (p3 >> (64 - 36))) ^ p2 p4 = (p4 + p5) p5 = ((p5 << 19) | (p5 >> (64 - 19))) ^ p4 p6 = (p6 + p7) p7 = ((p7 << 37) | (p7 >> (64 - 37))) ^ p6 p2 = (p2 + p1) p1 = ((p1 << 33) | (p1 >> (64 - 33))) ^ p2 p4 = (p4 + p7) p7 = ((p7 << 27) | (p7 >> (64 - 27))) ^ p4 p6 = (p6 + p5) p5 = ((p5 << 14) | (p5 >> (64 - 14))) ^ p6 p0 = (p0 + p3) p3 = ((p3 << 42) | (p3 >> (64 - 42))) ^ p0 p4 = (p4 + p1) p1 = ((p1 << 17) | (p1 >> (64 - 17))) ^ p4 p6 = (p6 + p3) p3 = ((p3 << 49) | (p3 >> (64 - 49))) ^ p6 p0 = (p0 + p5) p5 = ((p5 << 36) | (p5 >> (64 - 36))) ^ p0 p2 = (p2 + p7) p7 = ((p7 << 39) | (p7 >> (64 - 39))) ^ p2 p6 = (p6 + p1) p1 = ((p1 << 44) | (p1 >> (64 - 44))) ^ p6 p0 = (p0 + p7) p7 = ((p7 << 9) | (p7 >> (64 - 9))) ^ p0 p2 = (p2 + p5) p5 = ((p5 << 54) | (p5 >> (64 - 54))) ^ p2 p4 = (p4 + p3) p3 = ((p3 << 56) | (p3 >> (64 - 56))) ^ p4 p0 = (p0 + h[s+1]) p1 = (p1 + h[s+2]) p2 = (p2 + h[s+3]) p3 = (p3 + h[s+4]) p4 = (p4 + h[s+5]) p5 = (p5 + h[s+6] + t1) p6 = (p6 + h[s+7] + t2) p7 = (p7 + h[s+8] + (s + 1)) p0 = (p0 + p1) p1 = ((p1 << 39) | (p1 >> (64 - 39))) ^ p0 p2 = (p2 + p3) p3 = ((p3 << 30) | (p3 >> (64 - 30))) ^ p2 p4 = (p4 + p5) p5 = ((p5 << 34) | (p5 >> (64 - 34))) ^ p4 p6 = (p6 + p7) p7 = ((p7 << 24) | (p7 >> (64 - 24))) ^ p6 p2 = (p2 + p1) p1 = ((p1 << 13) | (p1 >> (64 - 13))) ^ p2 p4 = (p4 + p7) p7 = ((p7 << 50) | (p7 >> (64 - 50))) ^ p4 p6 = (p6 + p5) p5 = ((p5 << 10) | (p5 >> (64 - 10))) ^ p6 p0 = (p0 + p3) p3 = ((p3 << 17) | (p3 >> (64 - 17))) ^ p0 p4 = (p4 + p1) p1 = ((p1 << 25) | (p1 >> (64 - 25))) ^ p4 p6 = (p6 + p3) p3 = ((p3 << 29) | (p3 >> (64 - 29))) ^ p6 p0 = (p0 + p5) p5 = ((p5 << 39) | (p5 >> (64 - 39))) ^ p0 p2 = (p2 + p7) p7 = ((p7 << 43) | (p7 >> (64 - 43))) ^ p2 p6 = (p6 + p1) p1 = ((p1 << 8) | (p1 >> (64 - 8))) ^ p6 p0 = (p0 + p7) p7 = ((p7 << 35) | (p7 >> (64 - 35))) ^ p0 p2 = (p2 + p5) p5 = ((p5 << 56) | (p5 >> (64 - 56))) ^ p2 p4 = (p4 + p3) p3 = ((p3 << 22) | (p3 >> (64 - 22))) ^ p4 tmp = t2 t2 = t1 t1 = t0 t0 = tmp } p0 += h[18+0] p1 += h[18+1] p2 += h[18+2] p3 += h[18+3] p4 += h[18+4] p5 += h[18+5] + t0 p6 += h[18+6] + t1 p7 += h[18+7] + 18 h[0] = m0 ^ p0 h[1] = m1 ^ p1 h[2] = m2 ^ p2 h[3] = m3 ^ p3 h[4] = m4 ^ p4 h[5] = m5 ^ p5 h[6] = m6 ^ p6 h[7] = m7 ^ p7 } //////////////// var kInit = [8]uint64{ uint64(0x4903ADFF749C51CE), uint64(0x0D95DE399746DF03), uint64(0x8FD1934127C79BCE), uint64(0x9A255629FF352CB1), uint64(0x5DB62599DF6CA7B0), uint64(0xEABE394CA9D5C3F4), uint64(0x991112C71A75B523), uint64(0xAE18A40B660FCC33), }