digest_hash/
hash.rs

1// Copyright 2017 Mikhail Zabaluev <mikhail.zabaluev@gmail.com>
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9use digest::generic_array::{ArrayLength, GenericArray};
10use EndianInput;
11
12use std::borrow::{Cow, ToOwned};
13use std::mem;
14use std::rc::Rc;
15use std::sync::Arc;
16
17/// A cryptographically hashable type.
18///
19/// This trait is similar to `std::hash::Hash`, with some differences:
20///
21/// - The choice of provided trait implementations discourages hashing of
22///   machine-dependent types, or types without an unambiguous byte stream
23///   representation.
24/// - The standard sequential byte containers are transparent to hashing.
25/// - The intended recipients of data for hashing are cryptographic hash
26///   functions that implement traits defined in crate `digest`.
27///
28/// This trait can be implemented for a user-defined type to provide it with
29/// a cryptographically stable representation for secure hashing.
30pub trait Hash {
31    /// Feeds this value into the given digest function.
32    ///
33    /// For multi-byte data member values, the byte order is imposed by the
34    /// implementation of `EndianInput` that the digest function provides.
35    fn hash<H>(&self, digest: &mut H)
36    where
37        H: EndianInput;
38}
39
40macro_rules! impl_hash_for {
41    {
42        ($self:ident: &$t:ty, $digest:ident)
43        $body:block
44    } => {
45        impl Hash for $t {
46            fn hash<H>(&$self, $digest: &mut H)
47                where H: EndianInput
48            $body
49        }
50    }
51}
52
53macro_rules! impl_hash_for_mi_word {
54    ($t:ty, $_size:expr, $input:ident, $_chain:ident, $_bo_func:ident) => {
55        impl_hash_for! {
56            (self: &$t, digest) {
57                digest.$input(*self);
58            }
59        }
60    };
61}
62
63for_all_mi_words!(impl_hash_for_mi_word!);
64
65impl Hash for u8 {
66    fn hash<H>(&self, digest: &mut H)
67    where
68        H: EndianInput,
69    {
70        digest.input_u8(*self);
71    }
72}
73
74impl Hash for i8 {
75    fn hash<H>(&self, digest: &mut H)
76    where
77        H: EndianInput,
78    {
79        digest.input_i8(*self);
80    }
81}
82
83impl<N> Hash for GenericArray<u8, N>
84where
85    N: ArrayLength<u8>,
86{
87    fn hash<H>(&self, digest: &mut H)
88    where
89        H: EndianInput,
90    {
91        digest.input(self.as_slice());
92    }
93}
94
95impl<N> Hash for GenericArray<i8, N>
96where
97    N: ArrayLength<i8>,
98{
99    fn hash<H>(&self, digest: &mut H)
100    where
101        H: EndianInput,
102    {
103        let bytes: &[u8] = unsafe { mem::transmute(self.as_slice()) };
104        digest.input(bytes);
105    }
106}
107
108impl<'a, T: ?Sized> Hash for &'a T
109where
110    T: Hash,
111{
112    fn hash<H>(&self, digest: &mut H)
113    where
114        H: EndianInput,
115    {
116        (*self).hash(digest);
117    }
118}
119
120macro_rules! impl_hash_for_gen_pointer {
121    ($Ptr:ident<$T:ident>) => {
122        impl<$T: ?Sized> Hash for $Ptr<$T>
123        where
124            $T: Hash,
125        {
126            fn hash<H>(&self, digest: &mut H)
127            where
128                H: EndianInput,
129            {
130                (**self).hash(digest);
131            }
132        }
133    };
134}
135
136impl_hash_for_gen_pointer!(Box<T>);
137impl_hash_for_gen_pointer!(Rc<T>);
138impl_hash_for_gen_pointer!(Arc<T>);
139
140impl<'a, B: ?Sized> Hash for Cow<'a, B>
141where
142    B: Hash,
143    B: ToOwned,
144    B::Owned: Hash,
145{
146    fn hash<H>(&self, digest: &mut H)
147    where
148        H: EndianInput,
149    {
150        match *self {
151            Cow::Borrowed(b) => b.hash(digest),
152            Cow::Owned(ref v) => v.hash(digest),
153        }
154    }
155}
156
157#[cfg(test)]
158mod tests {
159    use Hash;
160
161    use testmocks::conv_with;
162    use testmocks::{Hashable, MockDigest};
163    use {BigEndian, LittleEndian};
164
165    use std::borrow::Cow;
166    use std::mem;
167    use std::{f32, f64};
168
169    macro_rules! test_endian_hash {
170        (   $test:ident,
171            $Endian:ident,
172            $val:expr,
173            $to_endian_bits:expr) => {
174            #[test]
175            fn $test() {
176                let mut hasher = $Endian::<MockDigest>::new();
177                ($val).hash(&mut hasher);
178                let output = hasher.into_inner().bytes;
179                let val_bits = conv_with($val, $to_endian_bits);
180                let expected = bytes_from_endian!(val_bits);
181                assert_eq!(output, expected);
182            }
183        };
184    }
185
186    macro_rules! test_byte_hash {
187        (   $be_test:ident,
188            $le_test:ident,
189            $val:expr) => {
190            test_endian_hash!($be_test, BigEndian, $val, |v| v);
191            test_endian_hash!($le_test, LittleEndian, $val, |v| v);
192        };
193    }
194
195    macro_rules! test_word_hash {
196        (   $be_test:ident,
197            $le_test:ident,
198            $val:expr) => {
199            test_word_hash!($be_test, $le_test, $val, |v| v);
200        };
201
202        (   $be_test:ident,
203            $le_test:ident,
204            $val:expr,
205            $conv:expr) => {
206            test_endian_hash!($be_test, BigEndian, $val, |v| conv_with(v, $conv).to_be());
207            test_endian_hash!($le_test, LittleEndian, $val, |v| conv_with(v, $conv)
208                .to_le());
209        };
210    }
211
212    macro_rules! test_float_hash {
213        (   $be_test:ident,
214            $le_test:ident,
215            $val:expr) => {
216            test_word_hash!($be_test, $le_test, $val, |v| v.to_bits());
217        };
218    }
219
220    test_byte_hash!(u8_be_hash, u8_le_hash, 0xA5u8);
221    test_byte_hash!(i8_be_hash, i8_le_hash, -128i8);
222    test_word_hash!(u16_be_hash, u16_le_hash, 0xA55Au16);
223    test_word_hash!(i16_be_hash, i16_le_hash, -0x7FFEi16);
224    test_word_hash!(u32_be_hash, u32_le_hash, 0xA0B0_C0D0u32);
225    test_word_hash!(i32_be_hash, i32_le_hash, -0x7F01_02FDi32);
226    test_word_hash!(u64_be_hash, u64_le_hash, 0xA0B0_C0D0_0102_0304u64);
227    test_word_hash!(i64_be_hash, i64_le_hash, -0x7F01_0203_0405_FFFDi64);
228    test_float_hash!(f32_be_hash, f32_le_hash, f32::consts::PI);
229    test_float_hash!(f64_be_hash, f64_le_hash, f64::consts::PI);
230
231    macro_rules! test_generic_array_hash {
232        ($test:ident, $bt:ty) => {
233            #[test]
234            fn $test() {
235                use digest::generic_array::typenum::consts::U4;
236                use digest::generic_array::GenericArray;
237
238                let array =
239                    GenericArray::<$bt, U4>::from_exact_iter((0..4).map(|n| n as $bt)).unwrap();
240                let mut hasher = BigEndian::<MockDigest>::new();
241                array.hash(&mut hasher);
242                let output = hasher.into_inner().bytes;
243                assert_eq!(output, [0, 1, 2, 3]);
244            }
245        };
246    }
247
248    test_generic_array_hash!(generic_array_u8_hash, u8);
249    test_generic_array_hash!(generic_array_i8_hash, i8);
250
251    #[test]
252    fn custom_be_hash() {
253        let v = Hashable {
254            foo: 0x0102,
255            bar: 0x03040506,
256        };
257        let mut hasher = BigEndian::<MockDigest>::new();
258        v.hash(&mut hasher);
259        let output = hasher.into_inner().bytes;
260        assert_eq!(output, [0x01, 0x02, 0x03, 0x04, 0x05, 0x06]);
261    }
262
263    #[test]
264    fn custom_le_hash() {
265        let v = Hashable {
266            foo: 0x0102,
267            bar: 0x03040506,
268        };
269        let mut hasher = LittleEndian::<MockDigest>::new();
270        v.hash(&mut hasher);
271        let output = hasher.into_inner().bytes;
272        assert_eq!(output, [0x02, 0x01, 0x06, 0x05, 0x04, 0x03]);
273    }
274
275    fn test_generic_impl<T: Hash>(v: &T, expected: &[u8]) {
276        let mut hasher = BigEndian::<MockDigest>::new();
277        v.hash(&mut hasher);
278        let output = hasher.into_inner().bytes;
279        assert_eq!(output, expected);
280    }
281
282    #[test]
283    fn ref_hash() {
284        let v = Hashable {
285            foo: 0x0102,
286            bar: 0x03040506,
287        };
288        test_generic_impl(&v, &[0x01, 0x02, 0x03, 0x04, 0x05, 0x06]);
289    }
290
291    #[test]
292    fn box_hash() {
293        let v = Box::new(Hashable {
294            foo: 0x0102,
295            bar: 0x03040506,
296        });
297        test_generic_impl(&v, &[0x01, 0x02, 0x03, 0x04, 0x05, 0x06]);
298    }
299
300    #[test]
301    fn rc_hash() {
302        use std::rc::Rc;
303
304        let v = Rc::new(Hashable {
305            foo: 0x0102,
306            bar: 0x03040506,
307        });
308        test_generic_impl(&v, &[0x01, 0x02, 0x03, 0x04, 0x05, 0x06]);
309    }
310
311    #[test]
312    fn arc_hash() {
313        use std::sync::Arc;
314
315        let v = Arc::new(Hashable {
316            foo: 0x0102,
317            bar: 0x03040506,
318        });
319        test_generic_impl(&v, &[0x01, 0x02, 0x03, 0x04, 0x05, 0x06]);
320    }
321
322    #[test]
323    fn cow_borrowed_hash() {
324        let borrowed = &Hashable {
325            foo: 0x0102,
326            bar: 0x03040506,
327        };
328        let cow = Cow::Borrowed(borrowed);
329        let mut hasher = BigEndian::<MockDigest>::new();
330        cow.hash(&mut hasher);
331        let output = hasher.into_inner().bytes;
332        assert_eq!(output, &[0x01, 0x02, 0x03, 0x04, 0x05, 0x06]);
333    }
334
335    #[test]
336    fn cow_owned_hash() {
337        let owned = Hashable {
338            foo: 0x0102,
339            bar: 0x03040506,
340        };
341        let cow = Cow::Owned::<Hashable>(owned);
342        let mut hasher = BigEndian::<MockDigest>::new();
343        cow.hash(&mut hasher);
344        let output = hasher.into_inner().bytes;
345        assert_eq!(output, &[0x01, 0x02, 0x03, 0x04, 0x05, 0x06]);
346    }
347}