diff --git a/hashf/hashf.go b/hashf/hashf.go index 38b9989..38ef9cc 100644 --- a/hashf/hashf.go +++ b/hashf/hashf.go @@ -5,29 +5,30 @@ package hashf import ( "crypto/sha1" "fmt" + "hash" + "hash/adler32" + "time" + "unsafe" + farm "github.com/dgryski/go-farm" - metro "github.com/dgryski/go-metro" "github.com/jzelinskie/whirlpool" "github.com/minio/blake2b-simd" - "hash" - "hash/adler32" "leb.io/aeshash" + "leb.io/hashes/skein" "leb.io/hashland/crapwow" "leb.io/hashland/gomap" "leb.io/hashland/jenkins" "leb.io/hashland/keccak" "leb.io/hashland/keccakpg" "leb.io/hashland/mahash" + "leb.io/hashland/metro" "leb.io/hashland/murmur3" "leb.io/hashland/nhash" "leb.io/hashland/nullhash" "leb.io/hashland/sbox" "leb.io/hashland/siphash" "leb.io/hashland/siphashpg" - "leb.io/hashland/skein" "leb.io/hashland/spooky" - "time" - "unsafe" ) // interfaces @@ -247,14 +248,12 @@ func Hashf(k []byte, seed uint64) uint64 { } */ +var t uint64 +var l uint64 +var b uint32 +var c uint32 + func Hashf(k []byte, seed uint64) (h uint64) { - /* - _, ok := HashFunctions[Hf2] - if !ok { - fmt.Printf("%q not found\n", Hf2) - panic("hashf") - } - */ switch Hf2 { case "perfecthash": fmt.Printf("k=%v\n", k) @@ -293,13 +292,13 @@ func Hashf(k []byte, seed uint64) (h uint64) { case "MaHash8v64": h = mahash.MaHash8v64(k) case "j364": - c, b := jenkins.Jenkins364(k, len(k), uint32(seed), uint32(seed)) + c, b = jenkins.Jenkins364(k, len(k), uint32(seed), uint32(seed)) h = uint64(b)<<32 | uint64(c) case "j332c": - c, _ := jenkins.Jenkins364(k, len(k), uint32(seed), uint32(seed)) + c, _ = jenkins.Jenkins364(k, len(k), uint32(seed), uint32(seed)) h = uint64(c) case "j332b": - _, b := jenkins.Jenkins364(k, len(k), uint32(seed), uint32(seed)) + _, b = jenkins.Jenkins364(k, len(k), uint32(seed), uint32(seed)) h = uint64(b) case "j232": h = uint64(jenkins.Hash232(k, uint32(seed))) @@ -308,11 +307,13 @@ func Hashf(k []byte, seed uint64) (h uint64) { case "j264l": h = uint64(jenkins.Hash264(k, seed) & 0xFFFFFFFF) case "j264h": - t := jenkins.Hash264(k, seed) - h = uint64((t >> 32) & 0xFFFFFFFF) + h = jenkins.Hash264(k, seed) + h = uint64((h >> 32) & 0xFFFFFFFF) + //h = uint64((t >> 32) & 0xFFFFFFFF) case "j264xor": - t := jenkins.Hash264(k, seed) - h = uint64(uint32(t&0xFFFFFFFF) ^ uint32((t>>32)&0xFFFFFFFF)) + h = jenkins.Hash264(k, seed) + h = uint64(uint32(h&0xFFFFFFFF) ^ uint32((h>>32)&0xFFFFFFFF)) + //h = uint64(uint32(t&0xFFFFFFFF) ^ uint32((t>>32)&0xFFFFFFFF)) case "spooky32": h = uint64(spooky.Hash32(k, uint32(seed))) case "spooky64": @@ -322,13 +323,12 @@ func Hashf(k []byte, seed uint64) (h uint64) { case "spooky128l": _, h = spooky.Hash128(k, seed) case "spooky128xor": - t, l := spooky.Hash128(k, seed) + t, l = spooky.Hash128(k, seed) h = t ^ l case "murmur332": m332.Reset() m332.Write(k) - t := m332.Sum32() - h = uint64(t) + h = uint64(m332.Sum32()) case "murmur364": m364.Reset() m364.Write(k) @@ -339,8 +339,7 @@ func Hashf(k []byte, seed uint64) (h uint64) { sipSeedSet(seed) h, _ = siphashpg.Siphash(k, seeds, siphashpg.Crounds, siphashpg.Drounds, false) case "FarmHash32": - t := farm.Fingerprint32(k) - h = uint64(t & 0xFFFFFFFF) + h = uint64(farm.Fingerprint32(k) & 0xFFFFFFFF) case "FarmHash64": h = farm.Fingerprint64(k) case "FarmHash128-high": @@ -348,7 +347,7 @@ func Hashf(k []byte, seed uint64) (h uint64) { case "FarmHash128-low": _, h = farm.Fingerprint128(k) case "FarmHash128-xor": - t, l := farm.Fingerprint128(k) + t, l = farm.Fingerprint128(k) h = t ^ l case "MetroHash64-1": h = metro.Hash64_1(k, uint32(seed)) @@ -359,14 +358,14 @@ func Hashf(k []byte, seed uint64) (h uint64) { case "MetroHash128-1l": _, h = metro.Hash128_1(k, uint32(seed)) case "MetroHash128-1xor": - t, l := metro.Hash128_1(k, uint32(seed)) + t, l = metro.Hash128_1(k, uint32(seed)) h = t ^ l case "MetroHash128-2h": h, _ = metro.Hash128_2(k, uint32(seed)) case "MetroHash128-2l": _, h = metro.Hash128_2(k, uint32(seed)) case "MetroHash128-2xor": - t, l := metro.Hash128_2(k, uint32(seed)) + t, l = metro.Hash128_2(k, uint32(seed)) h = t ^ l /* @@ -414,24 +413,22 @@ func Hashf(k []byte, seed uint64) (h uint64) { k643.Reset() k643.Write(k) fp8 = k643.Sum(fp8) - //fmt.Printf("keccak160xor: fp=%v\n", fp) + //fmt.Printf("keccak160xor: fp=%v\n", fp8) h = uint64(fp8[0])<<56 | uint64(fp8[1])<<48 | uint64(fp8[2])<<40 | uint64(fp8[3])<<32 | uint64(fp8[4])<<24 | uint64(fp8[5])<<16 | uint64(fp8[6])<<8 | uint64(fp8[7])<<0 case "keccakpg644": - fp := make([]byte, 8, 8) - fp = fp[0:0] + fp8 = fp8[0:0] k644.Reset() k644.Write(k) - fp = k644.Sum(fp) - //fmt.Printf("keccak160xor: fp=%v\n", fp) - h = uint64(fp[0])<<56 | uint64(fp[1])<<48 | uint64(fp[2])<<40 | uint64(fp[3])<<32 | uint64(fp[4])<<24 | uint64(fp[5])<<16 | uint64(fp[6])<<8 | uint64(fp[7])<<0 + fp8 = k644.Sum(fp8) + //fmt.Printf("keccak160xor: fp=%v\n", fp8) + h = uint64(fp8[0])<<56 | uint64(fp8[1])<<48 | uint64(fp8[2])<<40 | uint64(fp8[3])<<32 | uint64(fp8[4])<<24 | uint64(fp8[5])<<16 | uint64(fp8[6])<<8 | uint64(fp8[7])<<0 case "keccakpg648": - fp := make([]byte, 8, 8) - fp = fp[0:0] + fp8 = fp8[0:0] k648.Reset() k648.Write(k) - fp = k648.Sum(fp) + fp8 = k648.Sum(fp8) //fmt.Printf("keccak160xor: fp=%v\n", fp) - h = uint64(fp[0])<<56 | uint64(fp[1])<<48 | uint64(fp[2])<<40 | uint64(fp[3])<<32 | uint64(fp[4])<<24 | uint64(fp[5])<<16 | uint64(fp[6])<<8 | uint64(fp[7])<<0 + h = uint64(fp8[0])<<56 | uint64(fp8[1])<<48 | uint64(fp8[2])<<40 | uint64(fp8[3])<<32 | uint64(fp8[4])<<24 | uint64(fp8[5])<<16 | uint64(fp8[6])<<8 | uint64(fp8[7])<<0 case "keccakpg160": fp32 = fp32[0:0] k160.Reset() diff --git a/hashtable/hashtable.go b/hashtable/hashtable.go index 75b6f7d..eef9d12 100644 --- a/hashtable/hashtable.go +++ b/hashtable/hashtable.go @@ -146,10 +146,11 @@ func (ht *HashTable) Insert(ka []byte) { h := hashf.Hashf(k, ht.Seed) // jenkins.Hash232(k, 0) if ht.prime { idx = h % ht.Size + //fmt.Printf("idx=%d\n", idx) } else { idx = h & ht.SizeMask } - //fmt.Printf("index=%d\n", idx) + //fmt.Printf("k=%d, idx=%d, ht.Size=%d, h=%#016x\n", btoi(k), idx, ht.Size, h) cnt := 0 pass := 0 @@ -182,7 +183,7 @@ func (ht *HashTable) Insert(ka []byte) { break } if ht.oa { - //fmt.Printf("Insert: col idx=%d, len=%d, hash=0x%08x, key=%q\n", idx, len(ht.Buckets[idx]), h, ht.Buckets[idx][0].Key) + //fmt.Printf("Insert: OA col idx=%d, len=%d, hash=0x%08x, key=%q\n", idx, len(ht.Buckets[idx]), h, ht.Buckets[idx][0].Key) if cnt == 0 { ht.Probes++ } else { diff --git a/ht.go b/ht.go index bb4c47e..3a86b5f 100644 --- a/ht.go +++ b/ht.go @@ -31,6 +31,7 @@ import ( "leb.io/hashland/keccakpg" // remove "leb.io/hashland/nullhash" // remove "leb.io/hashland/siphash" // remove + "leb.io/hashland/spooky" ) func ReadFile(file string, cb func(line string)) int { @@ -216,7 +217,7 @@ func TestH(file string, lines int, hf2 string) (ht *HashTable) { // integers 0 to n func TestI(file string, lines int, hf2 string) (ht *HashTable) { //r = rand.New(rand.NewSource(int64(bseed))) - //fmt.Printf("ni=%d\n", *ni) + fmt.Printf("n=%d, ni=%d\n", *n, *ni) bs := make([]byte, 4, 4) if *dru { *n = -*n @@ -286,6 +287,31 @@ func TestL(file string, lines int, hf2 string) (ht *HashTable) { return } +// hash function mod test +func TestM(file string, lines int, hf2 string) (ht *HashTable) { + //r = rand.New(rand.NewSource(int64(bseed))) + fmt.Printf("n=%d, ni=%d\n", *n, *ni) + bs := make([]byte, 4, 4) + if *dru { + *n = -*n + } + hist := make([]int, *n) + ht = NewHashTable(*n, seed, *extra, *pd, *oa, *prime) // ??? @@@ + mod := uint64(*n) + start := time.Now() + for i := 0; i < *ni; i++ { + bs[0], bs[1], bs[2], bs[3] = byte(i), byte(i>>8), byte(i>>16), byte(i>>24) + h := Hashf(bs, ht.Seed) + hist[h%mod]++ + //fmt.Printf("i=%d, 0x%08x\n", i, i) + } + stop := time.Now() + ht.Dur = tdiff(start, stop) + fmt.Printf("expected=%d\n", *ni / *n) + fmt.Printf("hist=%v\n", hist) + return +} + func unhex(c byte) uint8 { switch { case '0' <= c && c <= '9': @@ -481,7 +507,11 @@ func benchmark32s(n int) { case 4: start = time.Now() for i := 0; i < n; i++ { - _ = aeshash.Hash(bs, 0) + //_ = aeshash.Hash(bs, 0) + //_, _ = jenkins.Jenkins364(bs, 0, 0, 0) + //_ = jenkins.Hash232(bs, 0) + //_, _ = jenkins.Jenkins364(bs, 0, 0, 0) + _ = spooky.Hash64(bs, 0) // sha1160.Reset() // sha1160.Write(bs) // fp20 = fp20[0:0] @@ -492,9 +522,6 @@ func benchmark32s(n int) { //k224.Write(bs) //_ = k224.Sum(nil) //bs[0], bs[1], bs[2], bs[3] = byte(i), byte(i>>8), byte(i>>16), byte(i>>24) - //_, _ = jenkins.Jenkins364(bs, 0, 0, 0) - //_ = jenkins.Hash232(bs, 0) - //_, _ = jenkins.Jenkins364(bs, 0, 0, 0) //_ = gomap.Hash64(bs, 0) //_ = nhf64.Hash64S(bs, 0) // chnage below too //nh.Reset() @@ -511,18 +538,19 @@ func benchmark32s(n int) { default: start = time.Now() for i := 0; i < n; i++ { + //_ = aeshash.Hash(bs, 0) + //_, _ = jenkins.Jenkins364(bs, 0, 0, 0) + _ = spooky.Hash64(bs, 0) // sha1160.Reset() // sha1160.Write(bs) // fp20 = fp20[0:0] // fp20 = sha1160.Sum(fp20) // _ = uint64(fp20[0])<<56 | uint64(fp20[1])<<48 | uint64(fp20[2])<<40 | uint64(fp20[3])<<32 | // uint64(fp20[4])<<24 | uint64(fp20[5])<<16 | uint64(fp20[6])<<8 | uint64(fp20[7])<<0 - _ = aeshash.Hash(bs, 0) //k224.Reset() //k224.Write(bs) //_ = k224.Sum(nil) //bs[0], bs[1], bs[2], bs[3], bs[4], bs[5], bs[6], bs[7] = byte(i), byte(i>>8), byte(i>>16), byte(i>>24), byte(i>>32), byte(i>>40), byte(i>>48), byte(i>>56) - //_, _ = jenkins.Jenkins364(bs, 0, 0, 0) //_ = aeshash.Hash(bs, 0) //_ = gomap.Hash64(bs, 0) //_ = siphash.Hash(0, 0, bs) @@ -672,6 +700,7 @@ var Tests = []Test{ {"TestJ", &J, TestJ, "one bit keys (does not read file)"}, {"TestK", &K, TestK, "read file of keys and print hashes"}, {"TestL", &L, TestL, "all possible 3 byte keys"}, + {"TestL", &M, TestM, "hash function mod test"}, } func runTestsWithFileAndHashes(file string, lines int, hf []string) { @@ -716,7 +745,7 @@ var file = flag.String("file", "", "words to read") var lines = flag.Int("lines", 0, "number of lines to read in file") var hf = flag.String("hf", "all", "hash function") var extra = flag.Int("e", 1, "extra bis in table size") -var prime = flag.Bool("p", false, "table size is primes and use mod") +var prime = flag.Bool("p", false, "table size is prime, use mod") var all = flag.Bool("a", false, "run all tests") var pd = flag.Bool("pd", false, "print duplicate hashes") var oa = flag.Bool("oa", false, "open addressing (no buckets)") @@ -760,17 +789,18 @@ var I = flag.Bool("I", false, "test I") var J = flag.Bool("J", false, "test J") var K = flag.Bool("K", false, "test K") var L = flag.Bool("L", false, "test L") +var M = flag.Bool("M", false, "test M") var S = flag.Bool("S", false, "test S") var letters = []string{"abcdefgh", "efghijkl", "ijklmnop", "mnopqrst", "qrstuvwx", "uvwxyz01"} // 262144 words -var TestPointers = []**bool{&A, &B, &C, &D, &E, &F, &G, &H, &I, &J, &K, &L} +var TestPointers = []**bool{&A, &B, &C, &D, &E, &F, &G, &H, &I, &J, &K, &L, &M} func allTestsOn() { - *A, *B, *C, *D, *E, *F, *G, *H, *I, *J, *L = true, true, true, true, true, true, true, true, true, true, true + *A, *B, *C, *D, *E, *F, *G, *H, *I, *J, *L, *M = true, true, true, true, true, true, true, true, true, true, true, true } func allTestsOff() { - *A, *B, *C, *D, *E, *F, *G, *H, *I, *J, *L = false, false, false, false, false, false, false, false, false, false, false + *A, *B, *C, *D, *E, *F, *G, *H, *I, *J, *L, *M = false, false, false, false, false, false, false, false, false, false, false, false } func main() { @@ -900,6 +930,7 @@ func main() { allTestsOff() *I, *J = true, true } + fmt.Printf("here\n") if *hf == "all" { runTestsWithFileAndHashes("", *lines, TestHashFunctions) } else { diff --git a/jenkins/jenkins_test.go b/jenkins/jenkins_test.go index 36d1549..594ea1e 100644 --- a/jenkins/jenkins_test.go +++ b/jenkins/jenkins_test.go @@ -2,15 +2,19 @@ package jenkins_test //import "flag" -import "fmt" -import "time" -import "unsafe" +import ( + "fmt" + "testing" + "time" + "unsafe" + + "leb.io/hashland/jenkins" + "leb.io/hrff" +) + //import "math" //import "math/rand" //import "runtime" -import "github.com/tildeleb/hashland/jenkins" -import "github.com/tildeleb/hrff" -import "testing" func stu(s string) []uint32 { //fmt.Printf("stu: s=%q\n", s) @@ -29,8 +33,8 @@ func stu(s string) []uint32 { } func tdiff(begin, end time.Time) time.Duration { - d := end.Sub(begin) - return d + d := end.Sub(begin) + return d } func TestCheck(t *testing.T) { @@ -65,7 +69,7 @@ func TestBasic(t *testing.T) { t.FailNow() } - b, c = 0xdeadbeef, 0xdeadbeef + b, c = 0xdeadbeef, 0xdeadbeef c, b = jenkins.HashString("", c, b) //fmt.Printf("%08x, %08x\n", c, b) // 9c093ccd bd5b7dde if c != 0x9c093ccd || b != 0xbd5b7dde { @@ -108,7 +112,6 @@ func TestHash32v(t *testing.T) { } } - func TestHash32(t *testing.T) { k := make([]byte, 4, 4) k = k[:] @@ -211,4 +214,4 @@ func main() { } -*/ \ No newline at end of file +*/ diff --git a/metro/README b/metro/README new file mode 100644 index 0000000..594ec0b --- /dev/null +++ b/metro/README @@ -0,0 +1,28 @@ +MetroHash + +This package is a mechanical translation of the reference C++ code for +MetroHash, available at https://github.com/jandrewrogers/MetroHash + +I claim no additional copyright over the original implementation. + +The MIT License (MIT) + +Copyright (c) 2015 J. Andrew Rogers + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/metro/gitlog.txt b/metro/gitlog.txt new file mode 100644 index 0000000..eebc9b2 --- /dev/null +++ b/metro/gitlog.txt @@ -0,0 +1,11 @@ +commit d5cb643948fbb1a699e6da1426f0dba75fe3bb8e +Author: Damian Gryski +Date: Mon Jun 8 05:49:25 2015 +0200 + + add official test vectors + +commit 07ea97f54349b2a3c8ff09373968078129b09b21 +Author: Damian Gryski +Date: Thu May 28 15:29:33 2015 +0200 + + inital import diff --git a/metro/metro128.go b/metro/metro128.go new file mode 100644 index 0000000..baacf87 --- /dev/null +++ b/metro/metro128.go @@ -0,0 +1,172 @@ +package metro + +import "encoding/binary" + +func Hash128_1(key []byte, seed uint32) (uint64, uint64) { + + const k0 uint64 = 0xC83A91E1 + const k1 uint64 = 0x8648DBDB + const k2 uint64 = 0x7BDEC03B + const k3 uint64 = 0x2F5870A5 + + ptr := key + + var v [4]uint64 + + v[0] = (uint64(seed)-k0)*k3 + uint64(len(ptr)) + v[1] = (uint64(seed)+k1)*k2 + uint64(len(ptr)) + + if len(ptr) >= 32 { + v[2] = (uint64(seed)+k0)*k2 + uint64(len(ptr)) + v[3] = (uint64(seed)-k1)*k3 + uint64(len(ptr)) + + for len(ptr) >= 32 { + v[0] += binary.LittleEndian.Uint64(ptr) * k0 + ptr = ptr[8:] + v[0] = rotate_right(v[0], 29) + v[2] + v[1] += binary.LittleEndian.Uint64(ptr) * k1 + ptr = ptr[8:] + v[1] = rotate_right(v[1], 29) + v[3] + v[2] += binary.LittleEndian.Uint64(ptr) * k2 + ptr = ptr[8:] + v[2] = rotate_right(v[2], 29) + v[0] + v[3] += binary.LittleEndian.Uint64(ptr) * k3 + ptr = ptr[8:] + v[3] = rotate_right(v[3], 29) + v[1] + } + + v[2] ^= rotate_right(((v[0]+v[3])*k0)+v[1], 26) * k1 + v[3] ^= rotate_right(((v[1]+v[2])*k1)+v[0], 26) * k0 + v[0] ^= rotate_right(((v[0]+v[2])*k0)+v[3], 26) * k1 + v[1] ^= rotate_right(((v[1]+v[3])*k1)+v[2], 30) * k0 + } + + if len(ptr) >= 16 { + v[0] += binary.LittleEndian.Uint64(ptr) * k2 + ptr = ptr[8:] + v[0] = rotate_right(v[0], 33) * k3 + v[1] += binary.LittleEndian.Uint64(ptr) * k2 + ptr = ptr[8:] + v[1] = rotate_right(v[1], 33) * k3 + v[0] ^= rotate_right((v[0]*k2)+v[1], 17) * k1 + v[1] ^= rotate_right((v[1]*k3)+v[0], 17) * k0 + } + + if len(ptr) >= 8 { + v[0] += binary.LittleEndian.Uint64(ptr) * k2 + ptr = ptr[8:] + v[0] = rotate_right(v[0], 33) * k3 + v[0] ^= rotate_right((v[0]*k2)+v[1], 20) * k1 + } + + if len(ptr) >= 4 { + v[1] += uint64(binary.LittleEndian.Uint32(ptr)) * k2 + ptr = ptr[4:] + v[1] = rotate_right(v[1], 33) * k3 + v[1] ^= rotate_right((v[1]*k3)+v[0], 18) * k0 + } + + if len(ptr) >= 2 { + v[0] += uint64(binary.LittleEndian.Uint16(ptr)) * k2 + ptr = ptr[2:] + v[0] = rotate_right(v[0], 33) * k3 + v[0] ^= rotate_right((v[0]*k2)+v[1], 24) * k1 + } + + if len(ptr) >= 1 { + v[1] += uint64(ptr[0]) * k2 + v[1] = rotate_right(v[1], 33) * k3 + v[1] ^= rotate_right((v[1]*k3)+v[0], 24) * k0 + } + + v[0] += rotate_right((v[0]*k0)+v[1], 13) + v[1] += rotate_right((v[1]*k1)+v[0], 37) + v[0] += rotate_right((v[0]*k2)+v[1], 13) + v[1] += rotate_right((v[1]*k3)+v[0], 37) + + return v[0], v[1] +} + +func Hash128_2(key []byte, seed uint32) (uint64, uint64) { + const k0 uint64 = 0xD6D018F5 + const k1 uint64 = 0xA2AA033B + const k2 uint64 = 0x62992FC1 + const k3 uint64 = 0x30BC5B29 + + ptr := key + + var v [4]uint64 + + v[0] = (uint64(seed)-k0)*k3 + uint64(len(ptr)) + v[1] = (uint64(seed)+k1)*k2 + uint64(len(ptr)) + + if len(ptr) >= 32 { + v[2] = (uint64(seed)+k0)*k2 + uint64(len(ptr)) + v[3] = (uint64(seed)-k1)*k3 + uint64(len(ptr)) + + for len(ptr) >= 32 { + v[0] += binary.LittleEndian.Uint64(ptr) * k0 + ptr = ptr[8:] + v[0] = rotate_right(v[0], 29) + v[2] + v[1] += binary.LittleEndian.Uint64(ptr) * k1 + ptr = ptr[8:] + v[1] = rotate_right(v[1], 29) + v[3] + v[2] += binary.LittleEndian.Uint64(ptr) * k2 + ptr = ptr[8:] + v[2] = rotate_right(v[2], 29) + v[0] + v[3] += binary.LittleEndian.Uint64(ptr) * k3 + ptr = ptr[8:] + v[3] = rotate_right(v[3], 29) + v[1] + } + + v[2] ^= rotate_right(((v[0]+v[3])*k0)+v[1], 33) * k1 + v[3] ^= rotate_right(((v[1]+v[2])*k1)+v[0], 33) * k0 + v[0] ^= rotate_right(((v[0]+v[2])*k0)+v[3], 33) * k1 + v[1] ^= rotate_right(((v[1]+v[3])*k1)+v[2], 33) * k0 + } + + if len(ptr) >= 16 { + v[0] += binary.LittleEndian.Uint64(ptr) * k2 + ptr = ptr[8:] + v[0] = rotate_right(v[0], 29) * k3 + v[1] += binary.LittleEndian.Uint64(ptr) * k2 + ptr = ptr[8:] + v[1] = rotate_right(v[1], 29) * k3 + v[0] ^= rotate_right((v[0]*k2)+v[1], 29) * k1 + v[1] ^= rotate_right((v[1]*k3)+v[0], 29) * k0 + } + + if len(ptr) >= 8 { + v[0] += binary.LittleEndian.Uint64(ptr) * k2 + ptr = ptr[8:] + v[0] = rotate_right(v[0], 29) * k3 + v[0] ^= rotate_right((v[0]*k2)+v[1], 29) * k1 + } + + if len(ptr) >= 4 { + v[1] += uint64(binary.LittleEndian.Uint32(ptr)) * k2 + ptr = ptr[4:] + v[1] = rotate_right(v[1], 29) * k3 + v[1] ^= rotate_right((v[1]*k3)+v[0], 25) * k0 + } + + if len(ptr) >= 2 { + v[0] += uint64(binary.LittleEndian.Uint16(ptr)) * k2 + ptr = ptr[2:] + v[0] = rotate_right(v[0], 29) * k3 + v[0] ^= rotate_right((v[0]*k2)+v[1], 30) * k1 + } + + if len(ptr) >= 1 { + v[1] += uint64(ptr[0]) * k2 + v[1] = rotate_right(v[1], 29) * k3 + v[1] ^= rotate_right((v[1]*k3)+v[0], 18) * k0 + } + + v[0] += rotate_right((v[0]*k0)+v[1], 33) + v[1] += rotate_right((v[1]*k1)+v[0], 33) + v[0] += rotate_right((v[0]*k2)+v[1], 33) + v[1] += rotate_right((v[1]*k3)+v[0], 33) + + return v[0], v[1] +} diff --git a/metro/metro64.go b/metro/metro64.go new file mode 100644 index 0000000..43b2a83 --- /dev/null +++ b/metro/metro64.go @@ -0,0 +1,162 @@ +package metro + +import "encoding/binary" + +func rotate_right(v uint64, k uint) uint64 { + return (v >> k) | (v << (64 - k)) +} + +func Hash64_1(key []byte, seed uint32) uint64 { + + const k0 uint64 = 0xC83A91E1 + const k1 uint64 = 0x8648DBDB + const k2 uint64 = 0x7BDEC03B + const k3 uint64 = 0x2F5870A5 + + ptr := key + + hash := (uint64(seed)+k2)*k0 + uint64(len(key)) + + if len(ptr) >= 32 { + v := [4]uint64{hash, hash, hash, hash} + + for len(ptr) >= 32 { + v[0] += binary.LittleEndian.Uint64(ptr) * k0 + ptr = ptr[8:] + v[0] = rotate_right(v[0], 29) + v[2] + v[1] += binary.LittleEndian.Uint64(ptr) * k1 + ptr = ptr[8:] + v[1] = rotate_right(v[1], 29) + v[3] + v[2] += binary.LittleEndian.Uint64(ptr) * k2 + ptr = ptr[8:] + v[2] = rotate_right(v[2], 29) + v[0] + v[3] += binary.LittleEndian.Uint64(ptr) * k3 + ptr = ptr[8:] + v[3] = rotate_right(v[3], 29) + v[1] + } + + v[2] ^= rotate_right(((v[0]+v[3])*k0)+v[1], 33) * k1 + v[3] ^= rotate_right(((v[1]+v[2])*k1)+v[0], 33) * k0 + v[0] ^= rotate_right(((v[0]+v[2])*k0)+v[3], 33) * k1 + v[1] ^= rotate_right(((v[1]+v[3])*k1)+v[2], 33) * k0 + hash += v[0] ^ v[1] + } + + if len(ptr) >= 16 { + v0 := hash + (binary.LittleEndian.Uint64(ptr) * k0) + ptr = ptr[8:] + v0 = rotate_right(v0, 33) * k1 + v1 := hash + (binary.LittleEndian.Uint64(ptr) * k1) + ptr = ptr[8:] + v1 = rotate_right(v1, 33) * k2 + v0 ^= rotate_right(v0*k0, 35) + v1 + v1 ^= rotate_right(v1*k3, 35) + v0 + hash += v1 + } + + if len(ptr) >= 8 { + hash += binary.LittleEndian.Uint64(ptr) * k3 + ptr = ptr[8:] + hash ^= rotate_right(hash, 33) * k1 + } + + if len(ptr) > 4 { + hash += uint64(binary.LittleEndian.Uint32(ptr)) * k3 + ptr = ptr[4:] + hash ^= rotate_right(hash, 15) * k1 + } + + if len(ptr) >= 2 { + hash += uint64(binary.LittleEndian.Uint16(ptr)) * k3 + ptr = ptr[2:] + hash ^= rotate_right(hash, 13) * k1 + } + + if len(ptr) >= 1 { + hash += uint64(ptr[0]) * k3 + hash ^= rotate_right(hash, 25) * k1 + } + + hash ^= rotate_right(hash, 33) + hash *= k0 + hash ^= rotate_right(hash, 33) + + return hash +} + +func Hash64_2(key []byte, seed uint32) uint64 { + const k0 uint64 = 0xD6D018F5 + const k1 uint64 = 0xA2AA033B + const k2 uint64 = 0x62992FC1 + const k3 uint64 = 0x30BC5B29 + + ptr := key + + hash := (uint64(seed)+k2)*k0 + uint64(len(key)) + + if len(ptr) >= 32 { + v := [4]uint64{hash, hash, hash, hash} + + for len(ptr) >= 32 { + v[0] += binary.LittleEndian.Uint64(ptr) * k0 + ptr = ptr[8:] + v[0] = rotate_right(v[0], 29) + v[2] + v[1] += binary.LittleEndian.Uint64(ptr) * k1 + ptr = ptr[8:] + v[1] = rotate_right(v[1], 29) + v[3] + v[2] += binary.LittleEndian.Uint64(ptr) * k2 + ptr = ptr[8:] + v[2] = rotate_right(v[2], 29) + v[0] + v[3] += binary.LittleEndian.Uint64(ptr) * k3 + ptr = ptr[8:] + v[3] = rotate_right(v[3], 29) + v[1] + } + + v[2] ^= rotate_right(((v[0]+v[3])*k0)+v[1], 30) * k1 + v[3] ^= rotate_right(((v[1]+v[2])*k1)+v[0], 30) * k0 + v[0] ^= rotate_right(((v[0]+v[2])*k0)+v[3], 30) * k1 + v[1] ^= rotate_right(((v[1]+v[3])*k1)+v[2], 30) * k0 + hash += v[0] ^ v[1] + } + + if len(ptr) >= 16 { + v0 := hash + (binary.LittleEndian.Uint64(ptr) * k2) + ptr = ptr[8:] + v0 = rotate_right(v0, 29) * k3 + v1 := hash + (binary.LittleEndian.Uint64(ptr) * k2) + ptr = ptr[8:] + v1 = rotate_right(v1, 29) * k3 + v0 ^= rotate_right(v0*k0, 34) + v1 + v1 ^= rotate_right(v1*k3, 34) + v0 + hash += v1 + } + + if len(ptr) >= 8 { + hash += binary.LittleEndian.Uint64(ptr) * k3 + ptr = ptr[8:] + hash ^= rotate_right(hash, 36) * k1 + } + + if len(ptr) >= 4 { + hash += uint64(binary.LittleEndian.Uint32(ptr)) * k3 + ptr = ptr[4:] + hash ^= rotate_right(hash, 15) * k1 + } + + if len(ptr) >= 2 { + hash += uint64(binary.LittleEndian.Uint16(ptr)) * k3 + ptr = ptr[2:] + hash ^= rotate_right(hash, 15) * k1 + } + + if len(ptr) >= 1 { + hash += uint64(ptr[0]) * k3 + hash ^= rotate_right(hash, 23) * k1 + } + + hash ^= rotate_right(hash, 28) + hash *= k0 + hash ^= rotate_right(hash, 29) + + return hash +} diff --git a/metro/metro_test.go b/metro/metro_test.go new file mode 100644 index 0000000..65e4644 --- /dev/null +++ b/metro/metro_test.go @@ -0,0 +1,58 @@ +package metro + +import ( + "encoding/binary" + "encoding/hex" + "testing" +) + +// From src/testvector.h + +var key63 = []byte("012345678901234567890123456789012345678901234567890123456789012") + +func Test64(t *testing.T) { + + tests := []struct { + f func([]byte, uint32) uint64 + which int + seed uint32 + want string + }{ + {Hash64_1, 1, 0, "658F044F5C730E40"}, + {Hash64_2, 2, 0, "073CAAB960623211"}, + {Hash64_1, 1, 1, "AE49EBB0A856537B"}, + {Hash64_2, 2, 1, "CF518E9CF58402C0"}, + } + + for _, tt := range tests { + want, _ := hex.DecodeString(tt.want) + want64 := binary.LittleEndian.Uint64(want) + if got := tt.f(key63, tt.seed); got != want64 { + t.Errorf("Hash64_%d(%q, %d)=%x, want %x\n", tt.which, key63, tt.seed, got, want64) + } + } +} + +func Test128(t *testing.T) { + + tests := []struct { + f func([]byte, uint32) (uint64, uint64) + which int + seed uint32 + want string + }{ + {Hash128_1, 1, 0, "ED9997ED9D0A8B0FF3F266399477788F"}, + {Hash128_2, 2, 0, "7BBA6FE119CF35D45507EDF3505359AB"}, + {Hash128_1, 1, 1, "DDA6BA67F7DE755EFDF6BEABECCFD1F4"}, + {Hash128_2, 2, 1, "2DA6AF149A5CDBC12B09DB0846D69EF0"}, + } + + for _, tt := range tests { + want, _ := hex.DecodeString(tt.want) + want64a := binary.LittleEndian.Uint64(want) + want64b := binary.LittleEndian.Uint64(want[8:]) + if gota, gotb := tt.f(key63, tt.seed); gota != want64a || gotb != want64b { + t.Errorf("Hash128_%d(%q, %d)=(%x, %x), want (%x, %x)\n", tt.which, key63, tt.seed, gota, gotb, want64a, want64b) + } + } +} diff --git a/skein/skein.go b/skein/skein.go deleted file mode 100644 index cfbf2cc..0000000 --- a/skein/skein.go +++ /dev/null @@ -1,476 +0,0 @@ -// Copyright (C) 2011 Werner Dittmann -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// -// Authors: Werner Dittmann -// - -// This package implements the Skein hash and Skein MAC algorithms as defined -// if the Skein V1.3 specification. Skein is one of the five SHA-3 candidate -// algorithms that advance to the third (and final) round of the SHA-3 -// selection. -// -// The implementation in this package supports: -// - All three state sizes of Skein and Threefish: 256, 512, and 1024 bits -// - Skein MAC -// - Variable length of hash and MAC input and output - even in numbers of bits -// - Full message length as defined in the Skein paper (2^96 -1 bytes, not just a meager 4 GiB :-) ) -// - Tested with the official test vectors that are part of the NIST CD (except Tree hashes) -// -// The implementation does not support tree hashing. -package skein - -import ( - "encoding/binary" - "hash" - // "crypto/threefish" - //"leb/habu/crypto/threefish" - "leb.io/hashland/threefish" - "strconv" -) - -var schema = [4]byte{83, 72, 65, 51} // "SHA3" - -const ( - normal = iota - zeroedState - chainedState - chainedConfig -) - -const ( - Skein256 = 256 - Skein512 = 512 - Skein1024 = 1024 -) - -const ( - maxSkeinStateWords = Skein1024 / 64 -) - -var nullStateWords [maxSkeinStateWords]uint64 - -type Skein struct { - cipherStateWords, - outputBytes, - hashSize, - bytesFilled int - config *skeinConfiguration - cipher *threefish.Cipher - ubiParameters *ubiTweak - inputBuffer []byte - cipherInput []uint64 - state []uint64 -} - -type stateSizeError int - -func (s stateSizeError) Error() string { - return "crypto/skein: invalid Skein state size " + strconv.Itoa(int(s)) -} - -type outputSizeError int - -func (s outputSizeError) Error() string { - return "crypto/skein: invalid Skein output size " + strconv.Itoa(int(s)) -} - -// Convenience functions to make it easier to create new hashes - -// Create a new Skein hash instance - 256bit (32 byte) hash size. -// This Skein hash uses the 512bit state length -// -func New256() hash.Hash { - h, _ := New(Skein512, 256) // Ignore error - we use correct sizes here - return h -} - -// The following section implement the hash.Hash interface methods - -// Reset resets the hash to one with zero bytes written. -func (s *Skein) Reset() { - s.initialize() -} - -// Size return the hash size in bytes if the hash size measured in bits is a multiple of 8. -// -// If the bit size is not a multiple of 8 then Size returns 0. -// -func (s *Skein) Size() int { - i := s.getHashSize() - if (i & 0x7) != 0 { - return 0 - } - return i / 8 -} - -// Write adds more data to the current hash. -// It never returns an error. -// -// In this implementation it's just a thin wrapper and calls Update() -// -func (s *Skein) Write(p []byte) (nn int, err error) { - s.Update(p) - nn = len(p) - return -} - -// Sum returns the current hash, without changing the -// underlying hash state. -// TODO: discuss if this makes sense for Skein - Skein works with 64bit int internally -func (s *Skein) Sum(b []byte) []byte { - return s.finalIntern() -} - -// BlockSize returns the hash's underlying block size. -// The Write method must be able to accept any amount -// of data, but it may operate more efficiently if all writes -// are a multiple of the block size. -func (s *Skein) BlockSize() int { - return s.getHashSize() / 8 -} - -// Initializes the Skein hash instance. -// -// stateSize -// The Skein state size of the hash in bits. Supported values -// are 256, 512, and 1024 -// outputSize -// The output size of the hash in bits. Output size must greater -// than zero. -// -func New(stateSize, outputSize int) (*Skein, error) { - if stateSize != 256 && stateSize != 512 && stateSize != 1024 { - return nil, stateSizeError(stateSize) - } - if outputSize <= 0 { - return nil, outputSizeError(outputSize) - } - s := new(Skein) - s.setup(stateSize, outputSize) - s.config = newSkeinConfiguration(s) - s.config.setSchema(schema[:]) // "SHA3" - s.config.setVersion(1) - s.config.generateConfiguration() - s.initialize() - return s, nil -} - -// Initializes the Skein hash instance for use with a key and tree. -// -// stateSize -// The internal state size of the hash in bits. Supported values -// are 256, 512, and 1024 -// outputSize -// The output size of the hash in bits. Output size must greater -// than zero. -// treeInfo -// Not yet supported. -// key -// The key for a message authenication code (MAC) -// -func NewExtended(stateSize, outputSize, treeInfo int, key []byte) (*Skein, error) { - if stateSize != 256 && stateSize != 512 && stateSize != 1024 { - return nil, stateSizeError(stateSize) - } - if outputSize <= 0 { - return nil, outputSizeError(outputSize) - } - s := new(Skein) - s.setup(stateSize, outputSize) - // compute the initial chaining state values, based on key - if len(key) > 0 { // do we have a key? - s.outputBytes = s.cipherStateWords * 8 - s.ubiParameters.startNewBlockType(uint64(Key)) - s.Update(key) // hash the key - s.finalPad() // computes new Skein state - } - s.outputBytes = (outputSize + 7) / 8 // re-compute here - s.config = newSkeinConfiguration(s) - s.config.setSchema(schema[:]) // "SHA3" - s.config.setVersion(1) - - s.initializeConf(chainedConfig) - return s, nil -} - -// Initialize the internal variables -// -func (s *Skein) setup(stateSize, outputSize int) { - s.cipherStateWords = stateSize / 64 - - s.hashSize = outputSize - s.outputBytes = (outputSize + 7) / 8 - - // Figure out which cipher we need based on - // the state size - s.cipher, _ = threefish.NewSize(stateSize) - - // Allocate buffers - s.inputBuffer = make([]byte, s.cipherStateWords*8) - s.cipherInput = make([]uint64, s.cipherStateWords) - s.state = make([]uint64, s.cipherStateWords) - - // Allocate tweak - s.ubiParameters = newUbiTweak() -} - -// Initialize with state variables provided by application. -// -// Applications may use this method if they provide their own Skein -// state before starting the Skein processing. The number of long (words) -// of the external state must conform the to number of state variables -// this Skein instance requires (state size bits / 64). -// -// After copying the external state to Skein the functions enables -// hash processing, thus an application can call {@code update}. The -// Skein MAC implementation uses this function to restore the state for -// a given state size, key, and output size combination. -// -// externalState -// The state to use. -// -func (s *Skein) initializeWithState(externalState []uint64) { - // Copy an external saved state value to internal state - copy(s.state, externalState) - // Set up tweak for message block - s.ubiParameters.startNewBlockType(uint64(Message)) - // Reset bytes filled - s.bytesFilled = 0 -} - -// Standard internal initialize function. -// -func (s *Skein) initialize() { - // Copy the configuration value to the state - for i := 0; i < len(s.state); i++ { - s.state[i] = s.config.configValue[i] - } - // Set up tweak for message block - s.ubiParameters.startNewBlockType(uint64(Message)) - s.bytesFilled = 0 -} - -// Internal initialization function that sets up the state variables -// in several ways. Used during set-up of MAC key hash for example. -// -func (s *Skein) initializeConf(initializationType int) { - switch initializationType { - case normal: - s.initialize() // Normal initialization - case zeroedState: - copy(s.state, nullStateWords[:]) // Start with a all zero state - case chainedState: - // Keep the state as it is and do nothing - case chainedConfig: - // Generate a chained configuration - s.config.generateConfigurationState(s.state) - s.initialize() - } - s.bytesFilled = 0 -} - -// Process (encrypt) one block with Threefish and update internal -// context variables. -// -func (s *Skein) processBlock(bytes int) { - s.cipher.SetKey(s.state) // state is the key - s.ubiParameters.addBytesProcessed(bytes) // Update tweak - s.cipher.SetTweak(s.ubiParameters.getTweak()) - - s.cipher.Encrypt64(s.state, s.cipherInput) - - // Feed-forward input with state - for i := 0; i < len(s.cipherInput); i++ { - s.state[i] ^= s.cipherInput[i] - } -} - -type statusError int - -func (s statusError) Error() string { - return "crypto/skein: partial byte only on last data block" -} - -type lengthError int - -func (s lengthError) Error() string { - return "crypto/skein: length of input buffer does not match bit length: " + strconv.Itoa(int(s)) -} - -// Update the hash with a message bit string. -// -// Skein can handle data not only as bytes but also as bit strings of -// arbitrary length (up to its maximum design size). -// -// array -// The byte array that holds the bit string. The array must be big -// enough to hold all bits. -// numBits -// Number of bits to hash. -// -func (s *Skein) UpdateBits(input []byte, numBits int) error { - - if s.ubiParameters.isBitPad() { - return statusError(0) - } - if (numBits+7)/8 != len(input) { - return lengthError(numBits) - } - s.Update(input) - - // if number of bits is a multiple of bytes - that's easy - if (numBits & 0x7) == 0 { - return nil - } - // Mask partial byte and set BitPad flag before doFinal() - mask := byte(1 << (7 - uint(numBits&7))) // partial byte bit mask - s.inputBuffer[s.bytesFilled-1] = byte((s.inputBuffer[s.bytesFilled-1] & (0 - mask)) | mask) - s.ubiParameters.setBitPad(true) - return nil -} - -// Update Skein digest with the next part of the message. -// -// input -// Byte slice that contains data to hash. -// -func (s *Skein) Update(input []byte) { - - // Fill input buffer - for i := 0; i < len(input); i++ { - // Do a transform if the input buffer is filled - if s.bytesFilled == s.cipherStateWords*8 { - // Copy input buffer to cipher input buffer - for i := 0; i < s.cipherStateWords; i++ { - s.cipherInput[i] = binary.LittleEndian.Uint64(s.inputBuffer[i*8 : i*8+8]) - } - // Process the block - s.processBlock(s.bytesFilled) - - // Clear first flag, which will be set - // by Initialize() if this is the first transform - s.ubiParameters.setFirstBlock(false) - - // Reset buffer fill count - s.bytesFilled = 0 - } - s.inputBuffer[s.bytesFilled] = input[i] - s.bytesFilled++ - } -} - -// Finalize Skein digest and return the hash. -// -// This method resets the Skein digest after it computed the digest. An -// application may reuse this Skein context to compute another digest. -// -func (s *Skein) DoFinal() (hash []byte) { - hash = s.finalIntern() - s.Reset() - return -} - -func (s *Skein) finalIntern() (hash []byte) { - // Pad leftover space in input buffer with zeros - // and copy to cipher input buffer - for i := s.bytesFilled; i < len(s.inputBuffer); i++ { - s.inputBuffer[i] = 0 - } - for i := 0; i < s.cipherStateWords; i++ { - s.cipherInput[i] = binary.LittleEndian.Uint64(s.inputBuffer[i*8 : i*8+8]) - } - // Do final message block - s.ubiParameters.setFinalBlock(true) - s.processBlock(s.bytesFilled) - - // Clear cipher input - copy(s.cipherInput, nullStateWords[:]) - - hash = make([]byte, s.outputBytes) - oldState := make([]uint64, s.cipherStateWords) - - // Save current state of hash, we need this to compute the output hash - copy(oldState, s.state) - - stateBytes := s.cipherStateWords * 8 - for i := 0; i < s.outputBytes; i += s.cipherStateWords * 8 { - s.ubiParameters.startNewBlockType(uint64(Out)) - s.ubiParameters.setFinalBlock(true) - s.processBlock(8) - - // Output a chunk of the hash - outputSize := s.outputBytes - i - if outputSize > stateBytes { - outputSize = stateBytes - } - - // The new state create by processBlock() is (part of) the hash - s.putBytes(s.state, hash[i:i+outputSize]) - - // Restore current state of hash to compute next hash output - copy(s.state, oldState) - - // Increment counter, Skein performs a Counter Mode threefish to compute hash output - s.cipherInput[0]++ - } - // at this point the internal state (s.state) is unchanged - return -} - -// Return the Skein output hash size as number of bits -func (s *Skein) getHashSize() int { - return s.hashSize -} - -// Return the number of Skein state words -func (s *Skein) getNumberCipherStateWords() int { - return s.cipherStateWords -} - -// Internal function that performs a final block processing -// and returns the resulting data. Used during set-up of -// MAC key hash. -// -func (s *Skein) finalPad() { - - // Pad left over space in input buffer with zeros - // and copy to cipher input buffer - for i := s.bytesFilled; i < len(s.inputBuffer); i++ { - s.inputBuffer[i] = 0 - } - for i := 0; i < s.cipherStateWords; i++ { - s.cipherInput[i] = binary.LittleEndian.Uint64(s.inputBuffer[i*8 : i*8+8]) - } - // Do final message block - s.ubiParameters.setFinalBlock(true) - s.processBlock(s.bytesFilled) -} - -// Disassmble an array of words into a byte array. -// -// input -// The input array. -// output -// The byte output array. -// byteCount -// The number of bytes to disassemble, arbitrary length. -// -func (s *Skein) putBytes(input []uint64, output []byte) { - var j uint - for i := 0; i < len(output); i++ { - output[i] = byte(input[i/8] >> j) - j = (j + 8) & 63 - } -} diff --git a/skein/skeinConfiguration.go b/skein/skeinConfiguration.go deleted file mode 100644 index 9253f5a..0000000 --- a/skein/skeinConfiguration.go +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright (C) 2011 Werner Dittmann -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// -// Authors: Werner Dittmann -// -package skein - -import ( - // "crypto/threefish" - //"leb/habu/crypto/threefish" - "leb.io/hashland/threefish" -) - -type skeinConfiguration struct { - numStateWords int - configValue []uint64 - // Set the state size for the configuration - configString []uint64 -} - -func newSkeinConfiguration(sk *Skein) *skeinConfiguration { - s := new(skeinConfiguration) - s.numStateWords = sk.getNumberCipherStateWords() - s.configValue = make([]uint64, s.numStateWords) - s.configString = make([]uint64, s.numStateWords) - s.configString[1] = uint64(sk.getHashSize()) - return s -} - -func (c *skeinConfiguration) generateConfiguration() { - - tweak := newUbiTweak() - - // Initialize the tweak value - tweak.startNewBlockType(uint64(Config)) - tweak.setFinalBlock(true) - tweak.setBitsProcessed(32) - - cipher, _ := threefish.NewSize(c.numStateWords * 64) - cipher.SetTweak(tweak.getTweak()) - cipher.Encrypt64(c.configValue, c.configString) - - c.configValue[0] ^= c.configString[0] - c.configValue[1] ^= c.configString[1] - c.configValue[2] ^= c.configString[2] -} - -func (c *skeinConfiguration) generateConfigurationState(initialState []uint64) { - - tweak := newUbiTweak() - - // Initialize the tweak value - tweak.startNewBlockType(uint64(Config)) - tweak.setFinalBlock(true) - tweak.setBitsProcessed(32) - - cipher, _ := threefish.New64(initialState, tweak.getTweak()) - cipher.Encrypt64(c.configValue, c.configString) - - c.configValue[0] ^= c.configString[0] - c.configValue[1] ^= c.configString[1] - c.configValue[2] ^= c.configString[2] -} - -func (c *skeinConfiguration) setSchema(schema []byte) { - - n := c.configString[0] - - // Clear the schema bytes - n &^= 0xffffffff - // Set schema bytes - n = uint64(schema[3]) << 24 - n |= uint64(schema[2]) << 16 - n |= uint64(schema[1]) << 8 - n |= uint64(schema[0]) - - c.configString[0] = n -} - -func (c *skeinConfiguration) setVersion(version int) { - c.configString[0] &^= uint64(0x03) << 32 - c.configString[0] |= uint64(version) << 32 -} - -func (c *skeinConfiguration) setTreeLeafSize(size byte) { - c.configString[2] &^= uint64(0xff) - c.configString[2] |= uint64(size) -} - -func (c *skeinConfiguration) setTreeFanOutSize(size byte) { - c.configString[2] &^= uint64(0xff) << 8 - c.configString[2] |= uint64(size) << 8 -} - -func (c *skeinConfiguration) setMaxTreeHeight(height byte) { - c.configString[2] &^= uint64(0xff) << 16 - c.configString[2] |= uint64(height) << 16 -} diff --git a/skein/skeinMac.go b/skein/skeinMac.go deleted file mode 100644 index 48312c3..0000000 --- a/skein/skeinMac.go +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright (C) 2011 Werner Dittmann -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// -// Authors: Werner Dittmann -// -package skein - -type SkeinMac struct { - skein *Skein - stateSave []uint64 -} - -// Initializes a Skein MAC context. -// -// Initializes the context with this data and saves the resulting Skein -// state variables for further use. -// -// Applications call the normal Skein functions to update the MAC and -// get the final result. -// -// stateSize -// Which Skein state size to use. Supported values -// are 256, 512, and 1024 -// outputSize -// Number of MAC hash bits to compute -// key -// The key bytes -// -func NewMac(stateSize, outputSize int, key []byte) (s *SkeinMac, err error) { - s = new(SkeinMac) - s.skein, err = NewExtended(stateSize, outputSize, 0, key) - if err != nil { - return nil, err - } - s.stateSave = make([]uint64, s.skein.cipherStateWords) - copy(s.stateSave, s.skein.state) - return s, nil -} - -// Update Skein MAC with the next part of the message. -// -// input -// Byte slice that contains data to hash. -// -func (s *SkeinMac) Update(input []byte) { - s.skein.Update(input) -} - -// Update the MAC with a message bit string. -// -// Skein can handle data not only as bytes but also as bit strings of -// arbitrary length (up to its maximum design size). -// -// input -// Byte slice that contains data to hash. The length of the byte slice -// must match the formula: (numBits + 7) / 8. -// numBits -// Number of bits to hash. -// -func (s *SkeinMac) UpdateBits(input []byte, numBits int) error { - return s.skein.UpdateBits(input, numBits) -} - -// Finalize Skein MAC and return the hash. -// -// This method resets the Skein MAC after it computed the MAC. An -// application may reuse this Skein MAC context to compute another -// MAC with the same key and sizes. -// -func (s *SkeinMac) DoFinal() (hash []byte) { - hash = s.skein.DoFinal() - s.Reset() - return -} - -// Resets a Skein context for further use. -// -// Restores the saved chaining variables to reset the Skein context. -// Thus applications can reuse the same setup to process several -// messages. This saves a complete Skein initialization cycle. -// -func (s *SkeinMac) Reset() { - s.skein.initializeWithState(s.stateSave) -} diff --git a/skein/skein_test.go b/skein/skein_test.go deleted file mode 100644 index f247e38..0000000 --- a/skein/skein_test.go +++ /dev/null @@ -1,304 +0,0 @@ -// Copyright (C) 2011 Werner Dittmann -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// -// Authors: Werner Dittmann -// -package skein - -import ( - "bufio" - "bytes" - "encoding/hex" - "fmt" - "os" - "strings" - "testing" -) - -type katResult struct { - stateSize int - hashBitLength int - msgLength int - msg []byte - msgFill int - result []byte - resultFill int - macKeyLen int - macKey []byte - macKeyFill int - restOfLine []byte -} - -func TestSkein(t *testing.T) { - scanner, e := newKatScanner("../../../../data/skein_golden_kat.txt") - if e != nil { - fmt.Printf("Error: %s\n", e) - t.Error("Cannot open test vector file") - return - } - if !checkKATVectors(scanner) { - t.Fail() - } -} - -func checkKATVectors(ks *katScanner) bool { - kr := new(katResult) - - var tree, mac, normal int - - for ks.fillResult(kr) { - if strings.Contains(string(kr.restOfLine), "Tree") { - tree++ - continue - } - if strings.Contains(string(kr.restOfLine), "MAC") { - skein, _ := NewMac(kr.stateSize, kr.hashBitLength, kr.macKey) - skein.UpdateBits(kr.msg, kr.msgLength) - hash := skein.DoFinal() - if ret := bytes.Compare(hash, kr.result); ret != 0 { - fmt.Printf("%d-%d-%d-%s\n", kr.stateSize, kr.hashBitLength, - kr.msgLength, string(kr.restOfLine)) - fmt.Printf("Computed mac:\n%s\n", hex.EncodeToString(hash)) - fmt.Printf("Expected mac:\n%s\n", hex.EncodeToString(kr.result)) - return false - } - // do it second time with same instance to check if context was - // reset correctly - skein.UpdateBits(kr.msg, kr.msgLength) - hash = skein.DoFinal() - if ret := bytes.Compare(hash, kr.result); ret != 0 { - fmt.Printf("%d-%d-%d-%s\n", kr.stateSize, kr.hashBitLength, - kr.msgLength, string(kr.restOfLine)) - fmt.Printf("Computed mac after reset:\n%s\n", hex.EncodeToString(hash)) - fmt.Printf("Expected mac:\n%s\n", hex.EncodeToString(kr.result)) - return false - } - mac++ - continue - } - skein, _ := New(kr.stateSize, kr.hashBitLength) - skein.UpdateBits(kr.msg, kr.msgLength) - hash := skein.DoFinal() - if ret := bytes.Compare(hash, kr.result); ret != 0 { - fmt.Printf("%d-%d-%d-%s\n", kr.stateSize, kr.hashBitLength, - kr.msgLength, string(kr.restOfLine)) - fmt.Printf("Computed hash:\n%s\n", hex.EncodeToString(hash)) - fmt.Printf("Expected result:\n%s\n", hex.EncodeToString(kr.result)) - return false - } - // do it second time with same instance to check if context was reset - // correctly - skein.UpdateBits(kr.msg, kr.msgLength) - hash = skein.DoFinal() - if ret := bytes.Compare(hash, kr.result); ret != 0 { - fmt.Printf("%d-%d-%d-%s\n", kr.stateSize, kr.hashBitLength, - kr.msgLength, string(kr.restOfLine)) - fmt.Printf("Computed hash after reset:\n%s\n", hex.EncodeToString(hash)) - fmt.Printf("Expected result:\n%s\n", hex.EncodeToString(kr.result)) - return false - } - normal++ - } - fmt.Printf("(Tree: %d), mac: %d, normal: %d, Skein tests total: %d\n", - tree, mac, normal, mac+normal) - return true -} - -/* - * Scanner for KAT file that fills in the KAT test vectors and - * expected results. - * - */ -const Start = 0 -const MessageLine = 1 -const Result = 2 -const MacKeyHeader = 3 -const MacKey = 4 -const Done = 5 - -type katScanner struct { - buf *bufio.Reader - state int -} - -func newKatScanner(name string) (*katScanner, error) { - r, e := os.Open(name) - if e != nil { - return nil, e - } - bufio := bufio.NewReader(r) - return &katScanner{bufio, Start}, nil -} - -/** - * Fill in data from KAT file, one complete element at a time. - * - * @param kr The resulting KAT data - * @return - */ -func (s *katScanner) fillResult(kr *katResult) bool { - - dataFound := false - - for s.state != Done { - line, err := s.buf.ReadString('\n') - if err != nil { - break - } - s.parseLines(line, kr) - dataFound = true - } - s.state = Start - return dataFound -} - -func (s *katScanner) parseLines(line string, kr *katResult) { - // fmt.Printf("Line: %s", line) - - line = strings.TrimSpace(line) - - if len(line) <= 1 { - return - } - - if strings.HasPrefix(line, "Message") { - s.state = MessageLine - return - } - if strings.HasPrefix(line, "Result") { - s.state = Result - return - } - if strings.HasPrefix(line, "MAC") { - s.state = MacKeyHeader - } - if strings.HasPrefix(line, "------") { - s.state = Done - return - } - switch s.state { - case Start: - if strings.HasPrefix(line, ":Skein-") { - s.parseHeaderLine(line, kr) - } else { - fmt.Printf("Wrong format found") - os.Exit(1) - } - case MessageLine: - s.parseMessageLine(line, kr) - case Result: - s.parseResultLine(line, kr) - case MacKey: - s.parseMacKeyLine(line, kr) - case MacKeyHeader: - s.parseMacKeyHeaderLine(line, kr) - } -} - -func (s *katScanner) parseHeaderLine(line string, kr *katResult) { - var rest string - - ret, err := fmt.Sscanf(line, ":Skein-%d: %d-bit hash, msgLen = %d%s", - &kr.stateSize, &kr.hashBitLength, &kr.msgLength, &rest) - if err != nil { - fmt.Printf("state size: %d, bit length: %d, msg length: %d, rest: %s, ret: %d\n", - kr.stateSize, kr.hashBitLength, kr.msgLength, rest, ret) - } - - idx := strings.Index(line, rest) - kr.restOfLine = make([]byte, len(line)-idx) - copy(kr.restOfLine[:], line[idx:]) - - if kr.msgLength > 0 { - if (kr.msgLength % 8) != 0 { - kr.msg = make([]byte, (kr.msgLength>>3)+1) - } else { - kr.msg = make([]byte, kr.msgLength>>3) - } - } - if (kr.hashBitLength % 8) != 0 { - kr.result = make([]byte, (kr.hashBitLength>>3)+1) - } else { - kr.result = make([]byte, kr.hashBitLength>>3) - } - kr.msgFill = 0 - kr.resultFill = 0 - kr.macKeyFill = 0 -} - -func (s *katScanner) parseMessageLine(line string, kr *katResult) { - var d [16]int - - if strings.Contains(line, "(none)") { - return - } - ret, err := fmt.Sscanf(line, "%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x", - &d[0], &d[1], &d[2], &d[3], &d[4], &d[5], &d[6], &d[7], &d[8], &d[9], &d[10], &d[11], &d[12], &d[13], &d[14], &d[15]) - - for i := 0; i < ret; i++ { - kr.msg[kr.msgFill] = byte(d[i]) - kr.msgFill++ - } - if err != nil && ret <= 0 { - fmt.Printf("msg: %s, ret: %d, %s \n", hex.EncodeToString(kr.msg), ret, err) - } -} - -func (s *katScanner) parseResultLine(line string, kr *katResult) { - var d [16]int - - ret, err := fmt.Sscanf(line, "%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x", - &d[0], &d[1], &d[2], &d[3], &d[4], &d[5], &d[6], &d[7], &d[8], &d[9], &d[10], &d[11], &d[12], &d[13], &d[14], &d[15]) - - for i := 0; i < ret; i++ { - kr.result[kr.resultFill] = byte(d[i]) - kr.resultFill++ - } - if err != nil && ret <= 0 { - fmt.Printf("result: %s, ret: %d, %s \n", hex.EncodeToString(kr.result)) - } -} - -func (s *katScanner) parseMacKeyLine(line string, kr *katResult) { - var d [16]int - - if strings.Contains(line, "(none)") { - return - } - ret, err := fmt.Sscanf(line, "%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x", - &d[0], &d[1], &d[2], &d[3], &d[4], &d[5], &d[6], &d[7], &d[8], &d[9], &d[10], &d[11], &d[12], &d[13], &d[14], &d[15]) - - for i := 0; i < ret; i++ { - kr.macKey[kr.macKeyFill] = byte(d[i]) - kr.macKeyFill++ - } - if err != nil && ret <= 0 { - fmt.Printf("macKey: %s, ret: %d, %s \n", hex.EncodeToString(kr.macKey), ret, err) - } -} - -func (s *katScanner) parseMacKeyHeaderLine(line string, kr *katResult) { - var rest string - - ret, err := fmt.Sscanf(line, "MAC key = %d%s", &kr.macKeyLen, &rest) - - if ret > 0 { - kr.macKey = make([]byte, kr.macKeyLen) - } - if err != nil && ret <= 0 { - fmt.Printf("macKeyLen: %d, %s\n", kr.macKeyLen, err) - } - s.state = MacKey -} diff --git a/skein/ubiTweak.go b/skein/ubiTweak.go deleted file mode 100644 index e610fb3..0000000 --- a/skein/ubiTweak.go +++ /dev/null @@ -1,213 +0,0 @@ -// Copyright (C) 2011 Werner Dittmann -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// -// Authors: Werner Dittmann -// -package skein - -const ( - Key int = 0 - Config int = 4 - Personalization int = 8 - PublicKey int = 12 - KeyIdentifier int = 16 - Nonce int = 20 - Message int = 48 - Out int = 63 -) - -const t1FlagFinal = uint64(1) << 63 -const t1FlagFirst = uint64(1) << 62 -const t1FlagBitPad = uint64(1) << 55 - -type ubiTweak struct { - tweak [2]uint64 -} - -func newUbiTweak() *ubiTweak { - return new(ubiTweak) -} - -/** - * Get status of the first block flag. - */ -func (u *ubiTweak) isFirstBlock() bool { - return (u.tweak[1] & t1FlagFirst) != 0 -} - -/** - * Sets status of the first block flag. - */ -func (u *ubiTweak) setFirstBlock(value bool) { - if value { - u.tweak[1] |= t1FlagFirst - } else { - u.tweak[1] &^= t1FlagFirst - } -} - -/** - * Gets status of the final block flag. - */ -func (u *ubiTweak) isFinalBlock() bool { - return (u.tweak[1] & t1FlagFinal) != 0 -} - -/** - * Sets status of the final block flag. - */ -func (u *ubiTweak) setFinalBlock(value bool) { - if value { - u.tweak[1] |= t1FlagFinal - } else { - u.tweak[1] &^= t1FlagFinal - } -} - -/** - * Gets status of the final block flag. - */ -func (u *ubiTweak) isBitPad() bool { - return (u.tweak[1] & t1FlagBitPad) != 0 -} - -/** - * Sets status of the final block flag. - */ -func (u *ubiTweak) setBitPad(value bool) { - if value { - u.tweak[1] |= t1FlagBitPad - } else { - u.tweak[1] &^= t1FlagBitPad - } -} - -/** - * Gets the current tree level. - */ -func (u *ubiTweak) getTreeLevel() byte { - return byte((u.tweak[1] >> 48) & 0x7f) -} - -/** - * Set the current tree level. - * - * @param value - * the tree level - */ -func (u *ubiTweak) setTreeLevel(value int) { - u.tweak[1] &^= uint64(0x7f) << 48 - u.tweak[1] |= uint64(value) << 48 -} - -/** - * Gets the number of bytes processed so far, inclusive. - * - * @return - * Number of processed bytes. - */ -func (u *ubiTweak) getBitsProcessed() (low, high uint64) { - low = u.tweak[0] - high = u.tweak[1] & 0xffffffff - return -} - -/** - * Set the number of bytes processed so far - * - * @param value - * The number of bits to set - low 64 bits - */ -func (u *ubiTweak) setBitsProcessed(value uint64) { - u.tweak[0] = value - u.tweak[1] &= 0xffffffff00000000 -} - -/** - * Add number of processed bytes. - * - * Adds the integer value to the 96-bit field of processed - * bytes. - * - * @param value - * Number of processed bytes. - */ -func (u *ubiTweak) addBytesProcessed(value int) { - const len = 3 - carry := uint64(value) - - var words [len]uint64 - - words[0] = u.tweak[0] & 0xffffffff - words[1] = (u.tweak[0] >> 32) & 0xffffffff - words[2] = u.tweak[1] & 0xffffffff - - for i := 0; i < len; i++ { - carry += words[i] - words[i] = carry - carry >>= 32 - } - u.tweak[0] = words[0] & 0xffffffff - u.tweak[0] |= (words[1] & 0xffffffff) << 32 - u.tweak[1] |= words[2] & 0xffffffff -} - -/** - * Get the current UBI block type. - */ -func (u *ubiTweak) getBlockType() uint64 { - return (u.tweak[1] >> 56) & 0x3f -} - -/** - * Set the current UBI block type. - * - * @param value - * Block type - */ -func (u *ubiTweak) setBlockType(value uint64) { - u.tweak[1] = value << 56 -} - -/** - * Starts a new UBI block type by setting BitsProcessed to zero, setting - * the first flag, and setting the block type. - * - * @param type - * The UBI block type of the new block - */ -func (u *ubiTweak) startNewBlockType(t uint64) { - u.setBitsProcessed(0) - u.setBlockType(t) - u.setFirstBlock(true) -} - -/** - * @return the tweak - */ -func (u *ubiTweak) getTweak() []uint64 { - return u.tweak[:] -} - -/** - * @param word0 - * the lower word of the tweak - * @param word1 - * the upper word of the tweak - */ -func (u *ubiTweak) setTweak(tw []uint64) { - u.tweak[0] = tw[0] - u.tweak[1] = tw[1] -}