about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorDamian Gryski <damian@gryski.com>2012-07-28 23:35:37 +0200
committerGraydon Hoare <graydon@mozilla.com>2012-07-30 10:46:01 -0700
commit04f2073beab68e4b0fc604e5b2bb2a344865d0a2 (patch)
tree581a51ba7cc92826f7141355603c20f0c66daa04 /src
parent082d8314da6b6b99854f0a70f5ea8e27f2602f79 (diff)
core::hash -- add a hash::streaming interface and associated siphash implementation.
Diffstat (limited to 'src')
-rw-r--r--src/libcore/hash.rs205
1 files changed, 194 insertions, 11 deletions
diff --git a/src/libcore/hash.rs b/src/libcore/hash.rs
index 072c3fe70fd..9c85473e761 100644
--- a/src/libcore/hash.rs
+++ b/src/libcore/hash.rs
@@ -32,7 +32,7 @@ pure fn hash_bytes_keyed(buf: &[const u8], k0: u64, k1: u64) -> u64 {
 
     #macro([#rotl(x,b), (x << b) | (x >> (64 - b))]);
 
-    #macro([#compress(), {
+    #macro([#compress(v0,v1,v2,v3), {
         v0 += v1; v1 = #rotl(v1, 13); v1 ^= v0; v0 = #rotl(v0, 32);
         v2 += v3; v3 = #rotl(v3, 16); v3 ^= v2;
         v0 += v3; v3 = #rotl(v3, 21); v3 ^= v0;
@@ -47,8 +47,8 @@ pure fn hash_bytes_keyed(buf: &[const u8], k0: u64, k1: u64) -> u64 {
     while i < end {
         let m = #u8to64_le(buf, i);
         v3 ^= m;
-        #compress();
-        #compress();
+        #compress(v0,v1,v2,v3);
+        #compress(v0,v1,v2,v3);
         v0 ^= m;
         i += 8;
     }
@@ -64,19 +64,182 @@ pure fn hash_bytes_keyed(buf: &[const u8], k0: u64, k1: u64) -> u64 {
     if left > 6 { b |= buf[i + 6] as u64 << 48; }
 
     v3 ^= b;
-    #compress();
-    #compress();
+    #compress(v0,v1,v2,v3);
+    #compress(v0,v1,v2,v3);
     v0 ^= b;
 
     v2 ^= 0xff;
-    #compress();
-    #compress();
-    #compress();
-    #compress();
+
+    #compress(v0,v1,v2,v3);
+    #compress(v0,v1,v2,v3);
+    #compress(v0,v1,v2,v3);
+    #compress(v0,v1,v2,v3);
 
     ret v0 ^ v1 ^ v2 ^ v3;
 }
 
+
+iface streaming {
+    fn input(~[u8]);
+    fn input_str(~str);
+    fn result() -> ~[u8];
+    fn result_str() -> ~str;
+    fn reset();
+}
+
+fn siphash(key0 : u64, key1 : u64) -> streaming {
+    type sipstate = {
+        k0 : u64,
+        k1 : u64,
+        mut length : uint, // how many bytes we've processed
+        mut v0 : u64,      // hash state
+        mut v1 : u64,
+        mut v2 : u64,
+        mut v3 : u64,
+        tail : ~[mut u8]/8, // unprocessed bytes
+        mut ntail : uint,   //  how many bytes in tail are valid
+    };
+
+    fn add_input(st : sipstate, msg : ~[u8]) {
+        let length = vec::len(msg);
+        st.length += length;
+
+        let mut needed = 0u;
+
+        if st.ntail != 0 {
+            needed = 8 - st.ntail;
+
+            if length < needed {
+
+                let mut t = 0;
+                while t < length {
+                    st.tail[st.ntail+t] = msg[t];
+                    t += 1;
+                }
+                st.ntail += length;
+
+                ret;
+            }
+
+            let mut t = 0;
+            while t < needed {
+                st.tail[st.ntail+t] = msg[t];
+                t += 1;
+            }
+
+            let m = #u8to64_le(st.tail, 0);
+
+            st.v3 ^= m;
+            #compress(st.v0, st.v1, st.v2, st.v3);
+            #compress(st.v0, st.v1, st.v2, st.v3);
+            st.v0 ^= m;
+
+            st.ntail = 0;
+        }
+
+        let len = length - needed;
+        let end = len & (!0x7);
+        let left = len & 0x7;
+
+        let mut i = needed;
+        while i < end {
+            let mi = #u8to64_le(msg, i);
+
+            st.v3 ^= mi;
+            #compress(st.v0, st.v1, st.v2, st.v3);
+            #compress(st.v0, st.v1, st.v2, st.v3);
+            st.v0 ^= mi;
+
+            i += 8;
+        }
+
+        let mut t = 0u;
+        while t < left {
+            st.tail[t] = msg[i+t];
+            t += 1
+        }
+        st.ntail = left;
+    }
+
+    fn mk_result(st : sipstate) -> ~[u8] {
+
+        let mut v0 = st.v0;
+        let mut v1 = st.v1;
+        let mut v2 = st.v2;
+        let mut v3 = st.v3;
+
+        let mut b : u64 = (st.length as u64 & 0xff) << 56;
+
+        if st.ntail > 0 { b |= st.tail[0] as u64 <<  0; }
+        if st.ntail > 1 { b |= st.tail[1] as u64 <<  8; }
+        if st.ntail > 2 { b |= st.tail[2] as u64 << 16; }
+        if st.ntail > 3 { b |= st.tail[3] as u64 << 24; }
+        if st.ntail > 4 { b |= st.tail[4] as u64 << 32; }
+        if st.ntail > 5 { b |= st.tail[5] as u64 << 40; }
+        if st.ntail > 6 { b |= st.tail[6] as u64 << 48; }
+
+        v3 ^= b;
+        #compress(v0, v1, v2, v3);
+        #compress(v0, v1, v2, v3);
+        v0 ^= b;
+
+        v2 ^= 0xff;
+        #compress(v0, v1, v2, v3);
+        #compress(v0, v1, v2, v3);
+        #compress(v0, v1, v2, v3);
+        #compress(v0, v1, v2, v3);
+
+        let h = v0 ^ v1 ^ v2 ^ v3;
+
+        ret ~[
+            (h >> 0) as u8,
+            (h >> 8) as u8,
+            (h >> 16) as u8,
+            (h >> 24) as u8,
+            (h >> 32) as u8,
+            (h >> 40) as u8,
+            (h >> 48) as u8,
+            (h >> 56) as u8,
+        ];
+    }
+
+   impl of streaming for sipstate {
+        fn reset() {
+            self.length = 0;
+            self.v0 = self.k0 ^ 0x736f6d6570736575;
+            self.v1 = self.k1 ^ 0x646f72616e646f6d;
+            self.v2 = self.k0 ^ 0x6c7967656e657261;
+            self.v3 = self.k1 ^ 0x7465646279746573;
+            self.ntail = 0;
+        }
+        fn input(msg: ~[u8]) { add_input(self, msg); }
+        fn input_str(msg: ~str) { add_input(self, str::bytes(msg)); }
+        fn result() -> ~[u8] { ret mk_result(self); }
+        fn result_str() -> ~str {
+            let r = mk_result(self);
+            let mut s = ~"";
+            for vec::each(r) |b| { s += uint::to_str(b as uint, 16u); }
+            ret s;
+        }
+    }
+
+    let st = {
+        k0 : key0,
+        k1 : key1,
+        mut length : 0u,
+        mut v0 : 0u64,
+        mut v1 : 0u64,
+        mut v2 : 0u64,
+        mut v3 : 0u64,
+        tail : ~[mut 0u8,0,0,0,0,0,0,0]/8,
+        mut ntail : 0u,
+    };
+
+    let sh = st as streaming;
+    sh.reset();
+    ret sh;
+}
+
 #[test]
 fn test_siphash() {
     let vecs : [[u8]/8]/64 = [
@@ -150,14 +313,34 @@ fn test_siphash() {
     let k1 = 0x_0f_0e_0d_0c_0b_0a_09_08_u64;
     let mut buf : ~[u8] = ~[];
     let mut t = 0;
+    let stream_inc = siphash(k0,k1);
+    let stream_full = siphash(k0,k1);
+
+    fn to_hex_str(r:[u8]/8) -> ~str {
+        let mut s = ~"";
+        for vec::each(r) |b| { s += uint::to_str(b as uint, 16u); }
+        ret s;
+    }
+
     while t < 64 {
         #debug("siphash test %?", t);
         let vec = #u8to64_le(vecs[t], 0);
         let out = hash_bytes_keyed(buf, k0, k1);
         #debug("got %?, expected %?", out, vec);
         assert vec == out;
+
+        stream_full.reset();
+        stream_full.input(buf);
+        let f = stream_full.result_str();
+        let i = stream_inc.result_str();
+        let v = to_hex_str(vecs[t]);
+        #debug["%d: (%s) => inc=%s full=%s", t, v, i, f];
+
+        assert f == i && f == v;
+
         buf += ~[t as u8];
+        stream_inc.input(~[t as u8]);
+
         t += 1;
     }
-
-}
\ No newline at end of file
+}