]> git.lizzy.rs Git - rust.git/blob - src/libcore/hash/sip.rs
Rollup merge of #27039 - bluss:siphash-tests, r=alexcrichton
[rust.git] / src / libcore / hash / sip.rs
1 // Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 //! An implementation of SipHash 2-4.
12
13 use prelude::*;
14 use super::Hasher;
15
16 /// An implementation of SipHash 2-4.
17 ///
18 /// See: http://131002.net/siphash/
19 ///
20 /// Consider this as a main "general-purpose" hash for all hashtables: it
21 /// runs at good speed (competitive with spooky and city) and permits
22 /// strong _keyed_ hashing. Key your hashtables from a strong RNG,
23 /// such as `rand::Rng`.
24 ///
25 /// Although the SipHash algorithm is considered to be cryptographically
26 /// strong, this implementation has not been reviewed for such purposes.
27 /// As such, all cryptographic uses of this implementation are strongly
28 /// discouraged.
29 #[stable(feature = "rust1", since = "1.0.0")]
30 pub struct SipHasher {
31     k0: u64,
32     k1: u64,
33     length: usize, // how many bytes we've processed
34     v0: u64,      // hash state
35     v1: u64,
36     v2: u64,
37     v3: u64,
38     tail: u64, // unprocessed bytes le
39     ntail: usize,  // how many bytes in tail are valid
40 }
41
42 // sadly, these macro definitions can't appear later,
43 // because they're needed in the following defs;
44 // this design could be improved.
45
46 macro_rules! u8to64_le {
47     ($buf:expr, $i:expr) =>
48     ($buf[0+$i] as u64 |
49      ($buf[1+$i] as u64) << 8 |
50      ($buf[2+$i] as u64) << 16 |
51      ($buf[3+$i] as u64) << 24 |
52      ($buf[4+$i] as u64) << 32 |
53      ($buf[5+$i] as u64) << 40 |
54      ($buf[6+$i] as u64) << 48 |
55      ($buf[7+$i] as u64) << 56);
56     ($buf:expr, $i:expr, $len:expr) =>
57     ({
58         let mut t = 0;
59         let mut out = 0;
60         while t < $len {
61             out |= ($buf[t+$i] as u64) << t*8;
62             t += 1;
63         }
64         out
65     });
66 }
67
68 macro_rules! rotl {
69     ($x:expr, $b:expr) =>
70     (($x << $b) | ($x >> (64_i32.wrapping_sub($b))))
71 }
72
73 macro_rules! compress {
74     ($v0:expr, $v1:expr, $v2:expr, $v3:expr) =>
75     ({
76         $v0 = $v0.wrapping_add($v1); $v1 = rotl!($v1, 13); $v1 ^= $v0;
77         $v0 = rotl!($v0, 32);
78         $v2 = $v2.wrapping_add($v3); $v3 = rotl!($v3, 16); $v3 ^= $v2;
79         $v0 = $v0.wrapping_add($v3); $v3 = rotl!($v3, 21); $v3 ^= $v0;
80         $v2 = $v2.wrapping_add($v1); $v1 = rotl!($v1, 17); $v1 ^= $v2;
81         $v2 = rotl!($v2, 32);
82     })
83 }
84
85 impl SipHasher {
86     /// Creates a new `SipHasher` with the two initial keys set to 0.
87     #[inline]
88     #[stable(feature = "rust1", since = "1.0.0")]
89     pub fn new() -> SipHasher {
90         SipHasher::new_with_keys(0, 0)
91     }
92
93     /// Creates a `SipHasher` that is keyed off the provided keys.
94     #[inline]
95     #[stable(feature = "rust1", since = "1.0.0")]
96     pub fn new_with_keys(key0: u64, key1: u64) -> SipHasher {
97         let mut state = SipHasher {
98             k0: key0,
99             k1: key1,
100             length: 0,
101             v0: 0,
102             v1: 0,
103             v2: 0,
104             v3: 0,
105             tail: 0,
106             ntail: 0,
107         };
108         state.reset();
109         state
110     }
111
112     #[inline]
113     fn reset(&mut self) {
114         self.length = 0;
115         self.v0 = self.k0 ^ 0x736f6d6570736575;
116         self.v1 = self.k1 ^ 0x646f72616e646f6d;
117         self.v2 = self.k0 ^ 0x6c7967656e657261;
118         self.v3 = self.k1 ^ 0x7465646279746573;
119         self.ntail = 0;
120     }
121
122     #[inline]
123     fn write(&mut self, msg: &[u8]) {
124         let length = msg.len();
125         self.length += length;
126
127         let mut needed = 0;
128
129         if self.ntail != 0 {
130             needed = 8 - self.ntail;
131             if length < needed {
132                 self.tail |= u8to64_le!(msg, 0, length) << 8*self.ntail;
133                 self.ntail += length;
134                 return
135             }
136
137             let m = self.tail | u8to64_le!(msg, 0, needed) << 8*self.ntail;
138
139             self.v3 ^= m;
140             compress!(self.v0, self.v1, self.v2, self.v3);
141             compress!(self.v0, self.v1, self.v2, self.v3);
142             self.v0 ^= m;
143
144             self.ntail = 0;
145         }
146
147         // Buffered tail is now flushed, process new input.
148         let len = length - needed;
149         let end = len & (!0x7);
150         let left = len & 0x7;
151
152         let mut i = needed;
153         while i < end {
154             let mi = u8to64_le!(msg, i);
155
156             self.v3 ^= mi;
157             compress!(self.v0, self.v1, self.v2, self.v3);
158             compress!(self.v0, self.v1, self.v2, self.v3);
159             self.v0 ^= mi;
160
161             i += 8;
162         }
163
164         self.tail = u8to64_le!(msg, i, left);
165         self.ntail = left;
166     }
167 }
168
169 #[stable(feature = "rust1", since = "1.0.0")]
170 impl Hasher for SipHasher {
171     #[inline]
172     fn write(&mut self, msg: &[u8]) {
173         self.write(msg)
174     }
175
176     #[inline]
177     fn finish(&self) -> u64 {
178         let mut v0 = self.v0;
179         let mut v1 = self.v1;
180         let mut v2 = self.v2;
181         let mut v3 = self.v3;
182
183         let b: u64 = ((self.length as u64 & 0xff) << 56) | self.tail;
184
185         v3 ^= b;
186         compress!(v0, v1, v2, v3);
187         compress!(v0, v1, v2, v3);
188         v0 ^= b;
189
190         v2 ^= 0xff;
191         compress!(v0, v1, v2, v3);
192         compress!(v0, v1, v2, v3);
193         compress!(v0, v1, v2, v3);
194         compress!(v0, v1, v2, v3);
195
196         v0 ^ v1 ^ v2 ^ v3
197     }
198 }
199
200 #[stable(feature = "rust1", since = "1.0.0")]
201 impl Clone for SipHasher {
202     #[inline]
203     fn clone(&self) -> SipHasher {
204         SipHasher {
205             k0: self.k0,
206             k1: self.k1,
207             length: self.length,
208             v0: self.v0,
209             v1: self.v1,
210             v2: self.v2,
211             v3: self.v3,
212             tail: self.tail,
213             ntail: self.ntail,
214         }
215     }
216 }
217
218 #[stable(feature = "rust1", since = "1.0.0")]
219 impl Default for SipHasher {
220     fn default() -> SipHasher {
221         SipHasher::new()
222     }
223 }