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.

384 lines
7.3 KiB

// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package shavite
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 = int(64)
// BlockSize holds the size of a block in bytes.
const BlockSize = uintptr(128)
////////////////
type digest struct {
ptr uintptr
h [16]uint32
c [4]uint32
b [BlockSize]byte
}
// New returns a new digest to compute a SHAVITE512 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
copy(ref.h[:], kInit[:])
ref.c[0], ref.c[1] = 0, 0
ref.c[2], ref.c[3] = 0, 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
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] == 0 {
ref.c[1] += 1
if ref.c[1] == 0 {
ref.c[2] += 1
if ref.c[2] == 0 {
ref.c[3] += 1
}
}
}
ref.compress()
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 > ln {
return fmt.Errorf("Shavite Close: dst min length: %d, got %d", HashSize, ln)
}
var cnt [4]uint32
ptr := ref.ptr
buf := ref.b[:]
ref.c[0] += uint32(ptr<<3) + uint32(bcnt)
cnt[0] = ref.c[0]
cnt[1] = ref.c[1]
cnt[2] = ref.c[2]
cnt[3] = ref.c[3]
z := uint8(0x80) >> bcnt
z = uint8((bits & -z) | z)
if (ptr == 0) && (bcnt == 0) {
buf[0] = 0x80
memset(buf[1:110], 0)
ref.c[0], ref.c[1] = 0, 0
ref.c[2], ref.c[3] = 0, 0
} else if ptr < 110 {
buf[ptr] = z
ptr += 1
memset(buf[ptr:(ptr+(110-ptr))], 0)
} else {
buf[ptr] = z
ptr += 1
memset(buf[ptr:(ptr+(128-ptr))], 0)
ref.compress()
memset(buf[:], 0)
ref.c[0], ref.c[1] = 0, 0
ref.c[2], ref.c[3] = 0, 0
}
encUInt32le(buf[110:], cnt[0])
encUInt32le(buf[114:], cnt[1])
encUInt32le(buf[118:], cnt[2])
encUInt32le(buf[122:], cnt[3])
buf[126] = 0
buf[127] = 2
ref.compress()
for u := uintptr(0); u < 16; u++ {
encUInt32le(dst[(u<<2):], ref.h[u])
}
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 decUInt32le(src []byte) uint32 {
return (uint32(src[0]) |
uint32(src[1])<<8 |
uint32(src[2])<<16 |
uint32(src[3])<<24)
}
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 (ref *digest) compress() {
var p0, p1, p2, p3, p4, p5, p6, p7 uint32
var p8, p9, pA, pB, pC, pD, pE, pF uint32
var t0, t1, t2, t3 uint32
var rk [448]uint32
for i := uintptr(0); i < 32; i++ {
rk[i] = decUInt32le(ref.b[(i * 4):])
//rk[i] = decUInt32be(ref.b[(i * 4):])
}
idx := uintptr(32)
for idx < 448 {
for s := uintptr(0); s < 4; s++ {
t0 = rk[idx-31]
t1 = rk[idx-30]
t2 = rk[idx-29]
t3 = rk[idx-32]
t0, t1, t2, t3 = aesr.Round32sle(t0, t1, t2, t3)
rk[idx+0] = t0 ^ rk[idx-4]
rk[idx+1] = t1 ^ rk[idx-3]
rk[idx+2] = t2 ^ rk[idx-2]
rk[idx+3] = t3 ^ rk[idx-1]
if idx == 32 {
rk[32] ^= ref.c[0]
rk[33] ^= ref.c[1]
rk[34] ^= ref.c[2]
rk[35] ^= uint32(^ref.c[3])
} else if idx == 440 {
rk[440] ^= ref.c[1]
rk[441] ^= ref.c[0]
rk[442] ^= ref.c[3]
rk[443] ^= uint32(^ref.c[2])
}
idx += 4
t0 = rk[idx-31]
t1 = rk[idx-30]
t2 = rk[idx-29]
t3 = rk[idx-32]
t0, t1, t2, t3 = aesr.Round32sle(t0, t1, t2, t3)
rk[idx+0] = t0 ^ rk[idx-4]
rk[idx+1] = t1 ^ rk[idx-3]
rk[idx+2] = t2 ^ rk[idx-2]
rk[idx+3] = t3 ^ rk[idx-1]
if idx == 164 {
rk[164] ^= ref.c[3]
rk[165] ^= ref.c[2]
rk[166] ^= ref.c[1]
rk[167] ^= uint32(^ref.c[0])
} else if idx == 316 {
rk[316] ^= ref.c[2]
rk[317] ^= ref.c[3]
rk[318] ^= ref.c[0]
rk[319] ^= uint32(^ref.c[1])
}
idx += 4
}
if idx != 448 {
for s := uintptr(0); s < 8; s++ {
rk[idx+0] = rk[idx-32] ^ rk[idx-7]
rk[idx+1] = rk[idx-31] ^ rk[idx-6]
rk[idx+2] = rk[idx-30] ^ rk[idx-5]
rk[idx+3] = rk[idx-29] ^ rk[idx-4]
idx += 4
}
}
}
p0 = ref.h[0x0]
p1 = ref.h[0x1]
p2 = ref.h[0x2]
p3 = ref.h[0x3]
p4 = ref.h[0x4]
p5 = ref.h[0x5]
p6 = ref.h[0x6]
p7 = ref.h[0x7]
p8 = ref.h[0x8]
p9 = ref.h[0x9]
pA = ref.h[0xA]
pB = ref.h[0xB]
pC = ref.h[0xC]
pD = ref.h[0xD]
pE = ref.h[0xE]
pF = ref.h[0xF]
idx = 0
for r := uint32(0); r < 14; r++ {
t0 = p4 ^ rk[idx+0]
t1 = p5 ^ rk[idx+1]
t2 = p6 ^ rk[idx+2]
t3 = p7 ^ rk[idx+3]
t0, t1, t2, t3 = aesr.Round32sle(t0, t1, t2, t3)
t0 ^= rk[idx+4]
t1 ^= rk[idx+5]
t2 ^= rk[idx+6]
t3 ^= rk[idx+7]
t0, t1, t2, t3 = aesr.Round32sle(t0, t1, t2, t3)
t0 ^= rk[idx+8]
t1 ^= rk[idx+9]
t2 ^= rk[idx+10]
t3 ^= rk[idx+11]
t0, t1, t2, t3 = aesr.Round32sle(t0, t1, t2, t3)
t0 ^= rk[idx+12]
t1 ^= rk[idx+13]
t2 ^= rk[idx+14]
t3 ^= rk[idx+15]
t0, t1, t2, t3 = aesr.Round32sle(t0, t1, t2, t3)
p0 ^= t0
p1 ^= t1
p2 ^= t2
p3 ^= t3
idx += 16
t0 = pC ^ rk[idx+0]
t1 = pD ^ rk[idx+1]
t2 = pE ^ rk[idx+2]
t3 = pF ^ rk[idx+3]
t0, t1, t2, t3 = aesr.Round32sle(t0, t1, t2, t3)
t0 ^= rk[idx+4]
t1 ^= rk[idx+5]
t2 ^= rk[idx+6]
t3 ^= rk[idx+7]
t0, t1, t2, t3 = aesr.Round32sle(t0, t1, t2, t3)
t0 ^= rk[idx+8]
t1 ^= rk[idx+9]
t2 ^= rk[idx+10]
t3 ^= rk[idx+11]
t0, t1, t2, t3 = aesr.Round32sle(t0, t1, t2, t3)
t0 ^= rk[idx+12]
t1 ^= rk[idx+13]
t2 ^= rk[idx+14]
t3 ^= rk[idx+15]
t0, t1, t2, t3 = aesr.Round32sle(t0, t1, t2, t3)
p8 ^= t0
p9 ^= t1
pA ^= t2
pB ^= t3
idx += 16
t0 = pC
pC = p8
p8 = p4
p4 = p0
p0 = t0
t0 = pD
pD = p9
p9 = p5
p5 = p1
p1 = t0
t0 = pE
pE = pA
pA = p6
p6 = p2
p2 = t0
t0 = pF
pF = pB
pB = p7
p7 = p3
p3 = t0
}
ref.h[0x0] ^= p0
ref.h[0x1] ^= p1
ref.h[0x2] ^= p2
ref.h[0x3] ^= p3
ref.h[0x4] ^= p4
ref.h[0x5] ^= p5
ref.h[0x6] ^= p6
ref.h[0x7] ^= p7
ref.h[0x8] ^= p8
ref.h[0x9] ^= p9
ref.h[0xA] ^= pA
ref.h[0xB] ^= pB
ref.h[0xC] ^= pC
ref.h[0xD] ^= pD
ref.h[0xE] ^= pE
ref.h[0xF] ^= pF
}
////////////////
var kInit = [16]uint32{
uint32(0x72FCCDD8), uint32(0x79CA4727),
uint32(0x128A077B), uint32(0x40D55AEC),
uint32(0xD1901A06), uint32(0x430AE307),
uint32(0xB29F5CD1), uint32(0xDF07FBFC),
uint32(0x8E45D73D), uint32(0x681AB538),
uint32(0xBDE86578), uint32(0xDD577E47),
uint32(0xE275EADE), uint32(0x502D9FCD),
uint32(0xB9357178), uint32(0x022A4B9A),
}