tiny_crypto/hash/
mod.rs

1//! Cryptographic Hash Functions
2//! 
3
4use std::marker::PhantomData;
5
6/// The hash function trait with multiple updates.
7pub trait Hasher {
8    fn new() -> Self;
9    fn update(&mut self, input: &[u8]);
10    fn finalize(self) -> Vec<u8>;
11}
12
13/// The hash function trait with one-shot hash call.
14pub trait OnceHasher {
15    type Hasher: Hasher;
16
17    fn hash(&self, input: &[u8]) -> Vec<u8>;
18    fn new_hasher(&self) -> Self::Hasher {
19        Self::Hasher::new()
20    }
21}
22
23/// The type wraps a Hahser to a OnceHasher
24pub struct OnceWrapper<H: Hasher>(PhantomData<H>);
25
26impl<H: Hasher> OnceHasher for OnceWrapper<H> {
27    type Hasher = H;
28
29    fn hash(&self, input: &[u8]) -> Vec<u8> {
30        let mut hasher = H::new();
31        hasher.update(input);
32        hasher.finalize()
33    }
34}
35
36/// A macro to call hash with variadic arguments.
37#[macro_export]
38macro_rules! hash {
39    ($ohasher:ident, $($arg:expr),+) => {
40        {
41            use $crate::hash::{Hasher, OnceHasher};
42            let mut hasher = $crate::hash::$ohasher.new_hasher();
43            $(hasher.update($arg);)+
44            hasher.finalize()
45        }
46    };
47}
48
49/// A macro to call hash with variadic arguments and encode to text.
50#[macro_export]
51macro_rules! hash_and_encode {
52    ($ohasher:ident, $encoder:ident, $($arg:expr),+) => {
53        {
54            use $crate::hash::{Hasher, OnceHasher};
55            use $crate::encoding::{Encoder};
56            let mut hasher = $crate::hash::$ohasher.new_hasher();
57            $(hasher.update($arg);)+
58            $crate::encoding::$encoder.to_text(&hasher.finalize())
59        }
60    };
61}
62
63mod sha1;
64pub use sha1::Sha1;
65
66/// SHA1 is a constant Sha1 instance with OnceHasher trait.
67pub const SHA1: OnceWrapper<Sha1> = OnceWrapper(PhantomData);
68
69/// A macro to call sha1 hash with variadic arguments.
70#[macro_export]
71macro_rules! sha1 {
72    ($($arg:expr),+) => {
73        $crate::hash!(SHA1, $($arg),+)
74    };
75}
76
77/// A macro to call sha1 hash with variadic arguments and encode to hex string.
78#[macro_export]
79macro_rules! sha1_hex {
80    ($($arg:expr),+) => {
81        $crate::hash_and_encode!(SHA1, HEX, $($arg),+)
82    };
83}
84
85/// A macro to call sha1 hash with variadic arguments and encode to base64 string.
86#[macro_export]
87macro_rules! sha1_base64 {
88    ($($arg:expr),+) => {
89        $crate::hash_and_encode!(SHA1, BASE64, $($arg),+)
90    };
91}
92
93#[cfg(test)]
94mod tests {
95
96    use super::*;
97    use hex_literal::hex;
98    use rstest::*;
99
100    #[rstest]
101    #[case(Sha1::new())]
102    fn update_finalize(#[case] mut hasher: impl Hasher) {
103        // let mut hasher = Sha1::new();
104        let data1 = r#"{"nickName":"Band","gender":1,"language":"zh_CN","city":"Guangzhou","province":"Guangdong","country":"CN","avatarUrl":"http://wx.qlogo.cn/mmopen/vi_32/1vZvI39NWFQ9XM4LtQpFrQJ1xlgZxx3w7bQxKARol6503Iuswjjn6nIGBiaycAjAtpujxyzYsrztuuICqIM5ibXQ/0"}"#;
105        let data2 = "HyVFkGl5F5OQWJZZaNzBBg==";
106        hasher.update(data1.as_bytes());
107        hasher.update(data2.as_bytes());
108        let result = hasher.finalize();
109        assert_eq!(result[..], hex!("75e81ceda165f4ffa64f4068af58c64b8f54b88c"));
110    }
111
112    #[rstest]
113    #[case(SHA1)]
114    fn hash(#[case] hasher: impl OnceHasher) {
115        let data = r#"{"nickName":"Band","gender":1,"language":"zh_CN","city":"Guangzhou","province":"Guangdong","country":"CN","avatarUrl":"http://wx.qlogo.cn/mmopen/vi_32/1vZvI39NWFQ9XM4LtQpFrQJ1xlgZxx3w7bQxKARol6503Iuswjjn6nIGBiaycAjAtpujxyzYsrztuuICqIM5ibXQ/0"}HyVFkGl5F5OQWJZZaNzBBg=="#;
116        let result = hasher.hash(data.as_bytes());
117        assert_eq!(result[..], hex!("75e81ceda165f4ffa64f4068af58c64b8f54b88c"));
118    }
119
120    #[test]
121    fn macro_test() {
122        let data1 = r#"{"nickName":"Band","gender":1,"language":"zh_CN","city":"Guangzhou","province":"Guangdong","country":"CN","avatarUrl":"http://wx.qlogo.cn/mmopen/vi_32/1vZvI39NWFQ9XM4LtQpFrQJ1xlgZxx3w7bQxKARol6503Iuswjjn6nIGBiaycAjAtpujxyzYsrztuuICqIM5ibXQ/0"}"#;
123        let data2 = "HyVFkGl5F5OQWJZZaNzBBg==";
124        let result = crate::sha1!(data1.as_bytes(), data2.as_bytes());
125        assert_eq!(result[..], hex!("75e81ceda165f4ffa64f4068af58c64b8f54b88c"));
126        let result = crate::sha1_hex!(data1.as_bytes(), data2.as_bytes());
127        assert_eq!(result, "75e81ceda165f4ffa64f4068af58c64b8f54b88c");
128        let result = crate::sha1_base64!(data1.as_bytes(), data2.as_bytes());
129        assert_eq!(result, "degc7aFl9P+mT0Bor1jGS49UuIw=");
130    }
131}