digest_hash/
endian.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 byteorder;
10use byteorder::ByteOrder;
11use digest;
12use digest::generic_array::GenericArray;
13
14use std::fmt;
15use std::fmt::Debug;
16use std::marker;
17use std::mem;
18
19macro_rules! endian_methods {
20    (
21        $t:ty,
22        $size:expr,
23        $input_method:ident,
24        $chain_method:ident,
25        $bo_func:ident
26    ) => {
27        fn $input_method(&mut self, n: $t) {
28            let mut buf: [u8; $size]
29                         = unsafe { mem::uninitialized() };
30            <Self::ByteOrder>::$bo_func(&mut buf, n);
31            self.input(&buf);
32        }
33
34        fn $chain_method(mut self, n: $t) -> Self
35        where Self: Sized {
36            self.$input_method(n);
37            self
38        }
39    }
40}
41
42/// Extends `digest::Input` to provide primitives for type-safe hashing.
43///
44/// `EndianInput` provides methods to process machine-independent values
45/// of bit widths larger than 8 bit. The values are serialized with the
46/// byte order which is defined in the associated type `ByteOrder`.
47pub trait EndianInput: digest::Input {
48    /// The byte order this implementation provides.
49    ///
50    /// This type binding determines the "endianness" of how integer
51    /// and floating-point values are serialized by this implementation
52    /// towards computation of the digest.
53    type ByteOrder: ByteOrder;
54
55    /// Feeds an unsigned 8-bit value into the digest function.
56    ///
57    /// This method is agnostic to the byte order, and is only provided
58    /// for completeness.
59    fn input_u8(&mut self, n: u8) {
60        self.input(&[n]);
61    }
62
63    /// Feeds a signed 8-bit value into the digest function.
64    ///
65    /// This method is agnostic to the byte order, and is only provided
66    /// for completeness.
67    fn input_i8(&mut self, n: i8) {
68        self.input(&[n as u8]);
69    }
70
71    /// Digest an unsigned 8-bit value in a chained manner.
72    ///
73    /// This method is agnostic to the byte order, and is only provided
74    /// for completeness.
75    fn chain_u8(self, n: u8) -> Self
76    where
77        Self: Sized,
78    {
79        self.chain(&[n])
80    }
81
82    /// Digest a signed 8-bit value in a chained manner.
83    ///
84    /// This method is agnostic to the byte order, and is only provided
85    /// for completeness.
86    fn chain_i8(self, n: i8) -> Self
87    where
88        Self: Sized,
89    {
90        self.chain(&[n as u8])
91    }
92
93    for_all_mi_words!(endian_methods!);
94}
95
96/// An adapter to provide digest functions with endian-awareness.
97#[derive(Clone)]
98pub struct Endian<D, Bo> {
99    inner: D,
100    phantom: marker::PhantomData<Bo>,
101}
102
103/// A type alias for `Endian` specialized for big endian byte order.
104pub type BigEndian<D> = Endian<D, byteorder::BigEndian>;
105
106/// A type alias for `Endian` specialized for little endian byte order.
107pub type LittleEndian<D> = Endian<D, byteorder::LittleEndian>;
108
109/// A type alias for `Endian` specialized for network byte order.
110///
111/// Network byte order is defined by [RFC 1700][rfc1700] to be big-endian,
112/// and is referred to in several protocol specifications.
113/// This type is an alias of `BigEndian`.
114///
115/// [rfc1700]: https://tools.ietf.org/html/rfc1700
116pub type NetworkEndian<D> = BigEndian<D>;
117
118impl<D, Bo> Endian<D, Bo>
119where
120    Bo: ByteOrder,
121{
122    /// Returns a string describing the byte order used by this
123    /// `Endian` type instance.
124    ///
125    /// This is mainly used for debugging purposes. The user
126    /// should not rely on any particular output.
127    pub fn byte_order_str() -> &'static str {
128        // Do a bit of runtime testing.
129        let mut buf = [0u8; 4];
130        Bo::write_u32(&mut buf, 0x01020304);
131        let le = byteorder::LittleEndian::read_u32(&buf);
132        match le {
133            0x01020304 => "LittleEndian",
134            0x04030201 => "BigEndian",
135            _ => "unknown byte order",
136        }
137    }
138}
139
140impl<D, Bo> Debug for Endian<D, Bo>
141where
142    D: Debug,
143    Bo: ByteOrder,
144{
145    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
146        f.debug_struct(Self::byte_order_str())
147            .field("digest", &self.inner)
148            .finish()
149    }
150}
151
152impl<D, Bo> EndianInput for Endian<D, Bo>
153where
154    D: digest::Input,
155    Bo: ByteOrder,
156{
157    type ByteOrder = Bo;
158}
159
160impl<D, Bo> digest::Input for Endian<D, Bo>
161where
162    D: digest::Input,
163{
164    fn input<B: AsRef<[u8]>>(&mut self, data: B) {
165        self.inner.input(data)
166    }
167}
168
169impl<D, Bo> digest::BlockInput for Endian<D, Bo>
170where
171    D: digest::BlockInput,
172{
173    type BlockSize = D::BlockSize;
174}
175
176impl<D, Bo> digest::FixedOutput for Endian<D, Bo>
177where
178    D: digest::FixedOutput,
179{
180    type OutputSize = D::OutputSize;
181
182    fn fixed_result(self) -> GenericArray<u8, Self::OutputSize> {
183        self.inner.fixed_result()
184    }
185}
186
187impl<D, Bo> digest::Reset for Endian<D, Bo>
188where
189    D: digest::Reset,
190{
191    fn reset(&mut self) {
192        self.inner.reset()
193    }
194}
195
196impl<D, Bo> Endian<D, Bo>
197where
198    D: digest::Input,
199    D: Default,
200    Bo: ByteOrder,
201{
202    /// Constructs an instance of an endian-aware hasher.
203    ///
204    /// # Examples
205    ///
206    /// ```
207    /// # extern crate digest_hash;
208    /// # extern crate sha2;
209    /// # use sha2::Sha256;
210    /// # fn main() {
211    /// let hasher = digest_hash::BigEndian::<Sha256>::new();
212    /// # }
213    /// ```
214    pub fn new() -> Self {
215        Endian {
216            inner: D::default(),
217            phantom: marker::PhantomData,
218        }
219    }
220}
221
222impl<D, Bo> Default for Endian<D, Bo>
223where
224    D: digest::Input,
225    D: Default,
226    Bo: ByteOrder,
227{
228    fn default() -> Self {
229        Self::new()
230    }
231}
232
233impl<D, Bo> From<D> for Endian<D, Bo>
234where
235    D: digest::Input,
236    Bo: ByteOrder,
237{
238    fn from(digest: D) -> Self {
239        Endian {
240            inner: digest,
241            phantom: marker::PhantomData,
242        }
243    }
244}
245
246impl<D, Bo> Endian<D, Bo> {
247    /// Consumes self and returns the underlying digest implementation.
248    pub fn into_inner(self) -> D {
249        self.inner
250    }
251}
252
253#[cfg(test)]
254mod tests {
255    use EndianInput;
256    use {BigEndian, LittleEndian, NetworkEndian};
257
258    use testmocks::conv_with;
259    use testmocks::MockDigest;
260
261    use std::mem;
262    use std::{f32, f64};
263
264    #[test]
265    fn default_works() {
266        let _ = NetworkEndian::<MockDigest>::default();
267    }
268
269    macro_rules! test_endian_debug {
270        (
271            $test:ident,
272            $Endian:ident
273        ) => {
274            #[test]
275            fn $test() {
276                assert_eq!($Endian::<MockDigest>::byte_order_str(), stringify!($Endian));
277                let hasher = $Endian::<MockDigest>::new();
278                let repr = format!("{:?}", hasher);
279                assert!(repr.starts_with(stringify!($Endian)));
280                assert!(repr.contains("MockDigest"));
281                assert!(!repr.contains("PhantomData"));
282            }
283        };
284    }
285
286    test_endian_debug!(debug_be, BigEndian);
287    test_endian_debug!(debug_le, LittleEndian);
288
289    macro_rules! test_endian_input {
290        (
291            $test:ident,
292            $Endian:ident,
293            $input_method:ident,
294            $chain_method:ident,
295            $val:expr,
296            $to_endian_bits:expr
297        ) => {
298            #[test]
299            fn $test() {
300                let val_bits = conv_with($val, $to_endian_bits);
301                let expected = bytes_from_endian!(val_bits);
302
303                let mut hasher = $Endian::<MockDigest>::new();
304                hasher.$input_method($val);
305                let output = hasher.into_inner().bytes;
306                assert_eq!(output, expected);
307
308                let hasher = $Endian::<MockDigest>::new();
309                let output = hasher.$chain_method($val).into_inner().bytes;
310                assert_eq!(output, expected);
311            }
312        };
313    }
314
315    macro_rules! test_byte_input {
316        (
317            $be_test:ident,
318            $le_test:ident,
319            $input_method:ident,
320            $chain_method:ident,
321            $val:expr
322        ) => {
323            test_endian_input!(
324                $be_test,
325                BigEndian,
326                $input_method,
327                $chain_method,
328                $val,
329                |v| v
330            );
331            test_endian_input!(
332                $le_test,
333                LittleEndian,
334                $input_method,
335                $chain_method,
336                $val,
337                |v| v
338            );
339        };
340    }
341
342    macro_rules! test_word_input {
343        (
344            $be_test:ident,
345            $le_test:ident,
346            $input_method:ident,
347            $chain_method:ident,
348            $val:expr
349        ) => {
350            test_word_input!(
351                $be_test,
352                $le_test,
353                $input_method,
354                $chain_method,
355                $val,
356                |v| v
357            );
358        };
359
360        (
361            $be_test:ident,
362            $le_test:ident,
363            $input_method:ident,
364            $chain_method:ident,
365            $val:expr,
366            $conv:expr
367        ) => {
368            test_endian_input!(
369                $be_test,
370                BigEndian,
371                $input_method,
372                $chain_method,
373                $val,
374                |v| conv_with(v, $conv).to_be()
375            );
376            test_endian_input!(
377                $le_test,
378                LittleEndian,
379                $input_method,
380                $chain_method,
381                $val,
382                |v| conv_with(v, $conv).to_le()
383            );
384        };
385    }
386
387    macro_rules! test_float_input {
388        (
389            $be_test:ident,
390            $le_test:ident,
391            $input_method:ident,
392            $chain_method:ident,
393            $val:expr
394        ) => {
395            test_word_input!(
396                $be_test,
397                $le_test,
398                $input_method,
399                $chain_method,
400                $val,
401                |v| v.to_bits()
402            );
403        };
404    }
405
406    test_byte_input!(u8_be_input, u8_le_input, input_u8, chain_u8, 0xA5u8);
407    test_byte_input!(i8_be_input, i8_le_input, input_i8, chain_i8, -128i8);
408    test_word_input!(u16_be_input, u16_le_input, input_u16, chain_u16, 0xA55Au16);
409    test_word_input!(i16_be_input, i16_le_input, input_i16, chain_i16, -0x7FFEi16);
410    test_word_input!(
411        u32_be_input,
412        u32_le_input,
413        input_u32,
414        chain_u32,
415        0xA0B0_C0D0u32
416    );
417    test_word_input!(
418        i32_be_input,
419        i32_le_input,
420        input_i32,
421        chain_i32,
422        -0x7F01_02FDi32
423    );
424    test_word_input!(
425        u64_be_input,
426        u64_le_input,
427        input_u64,
428        chain_u64,
429        0xA0B0_C0D0_0102_0304u64
430    );
431    test_word_input!(
432        i64_be_input,
433        i64_le_input,
434        input_i64,
435        chain_i64,
436        -0x7F01_0203_0405_FFFDi64
437    );
438    test_float_input!(
439        f32_be_input,
440        f32_le_input,
441        input_f32,
442        chain_f32,
443        f32::consts::PI
444    );
445    test_float_input!(
446        f64_be_input,
447        f64_le_input,
448        input_f64,
449        chain_f64,
450        f64::consts::PI
451    );
452}