trezor_crypto/
hasher.rs

1use core::{fmt, mem};
2
3pub const DIGEST_LEN: usize = 32;
4
5pub trait HashingAlgorithm {
6    #[doc(hidden)]
7    fn hasher_type() -> sys::HasherType;
8}
9
10trait HashingAlgorithmExt: HashingAlgorithm {
11    fn init() -> sys::Hasher {
12        let mut hasher;
13        unsafe {
14            hasher = mem::zeroed();
15            sys::hasher_Init(&mut hasher, Self::hasher_type());
16        };
17        hasher
18    }
19    fn digest(data: &[u8]) -> [u8; DIGEST_LEN] {
20        let mut out = [0; DIGEST_LEN];
21        unsafe {
22            sys::hasher_Raw(
23                Self::hasher_type(),
24                data.as_ptr(),
25                data.len() as u64,
26                out.as_mut_ptr(),
27            )
28        }
29        out
30    }
31}
32
33impl<T> HashingAlgorithmExt for T where T: HashingAlgorithm {}
34
35macro_rules! hashing_algo {
36    ($name:ident, $ty:expr) => {
37        #[derive(Clone, Copy, Debug)]
38        pub struct $name;
39        impl HashingAlgorithm for $name {
40            fn hasher_type() -> sys::HasherType {
41                $ty
42            }
43        }
44    };
45}
46hashing_algo!(Blake, sys::HasherType_HASHER_BLAKE);
47hashing_algo!(Blake2b, sys::HasherType_HASHER_BLAKE2B);
48hashing_algo!(Blake2bPersonal, sys::HasherType_HASHER_BLAKE2B_PERSONAL);
49hashing_algo!(BlakeRipemd, sys::HasherType_HASHER_BLAKE_RIPEMD);
50hashing_algo!(Blaked, sys::HasherType_HASHER_BLAKED);
51hashing_algo!(GroestldTrunc, sys::HasherType_HASHER_GROESTLD_TRUNC);
52hashing_algo!(Sha2, sys::HasherType_HASHER_SHA2);
53hashing_algo!(Sha2Ripemd, sys::HasherType_HASHER_SHA2_RIPEMD);
54hashing_algo!(Sha2d, sys::HasherType_HASHER_SHA2D);
55hashing_algo!(Sha3, sys::HasherType_HASHER_SHA3);
56hashing_algo!(Sha3k, sys::HasherType_HASHER_SHA3K);
57hashing_algo!(NoHash, 0);
58
59pub struct Hasher {
60    hasher: sys::Hasher,
61}
62
63impl Hasher {
64    #[inline]
65    fn new(hasher: sys::Hasher) -> Self {
66        Self { hasher }
67    }
68    pub fn init<H: HashingAlgorithm>() -> Self {
69        Self::new(H::init())
70    }
71    pub fn update<D: AsRef<[u8]>>(&mut self, data: D) {
72        let data = data.as_ref();
73        unsafe { sys::hasher_Update(&mut self.hasher, data.as_ptr(), data.len() as u64) }
74    }
75    pub fn reset(&mut self) {
76        unsafe { sys::hasher_Reset(&mut self.hasher) }
77    }
78    pub fn finish(&mut self) -> Digest {
79        let mut out = [0; DIGEST_LEN];
80        unsafe {
81            sys::hasher_Final(&mut self.hasher, out.as_mut_ptr());
82        }
83        Digest::from_bytes(out)
84    }
85}
86
87impl fmt::Debug for Hasher {
88    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
89        // TODO add hasher type
90        f.debug_struct("Hasher").finish()
91    }
92}
93
94pub fn digest<H: HashingAlgorithm, D: AsRef<[u8]>>(data: D) -> Digest {
95    Digest::from_bytes(H::digest(data.as_ref()))
96}
97
98#[derive(Clone, Copy, PartialEq, Eq)]
99pub struct Digest {
100    bytes: [u8; DIGEST_LEN],
101}
102
103impl Digest {
104    #[inline]
105    pub fn from_bytes(bytes: [u8; DIGEST_LEN]) -> Self {
106        Self { bytes }
107    }
108    pub fn as_bytes(&self) -> &[u8; 32] {
109        &self.bytes
110    }
111}
112
113impl AsRef<[u8]> for Digest {
114    fn as_ref(&self) -> &[u8] {
115        self.as_bytes()
116    }
117}
118
119impl fmt::Debug for Digest {
120    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
121        f.debug_tuple("Digest")
122            .field(&hex::encode(&self.bytes))
123            .finish()
124    }
125}
126
127#[cfg(test)]
128mod tests {
129    use super::*;
130
131    fn sha3_test_vector(input: impl AsRef<[u8]>, expected_hex: impl AsRef<str>) {
132        let digest = digest::<Sha3, _>(input);
133        assert_eq!(
134            hex::encode(digest.as_bytes()),
135            expected_hex.as_ref().to_lowercase()
136        )
137    }
138
139    #[test]
140    fn sha3_256_test_vectors() {
141        sha3_test_vector(
142            b"abc",
143            "3a985da74fe225b2045c172d6bd390bd855f086e3e9d525b46bfe24511431532",
144        );
145        sha3_test_vector(
146            b"",
147            "a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a",
148        );
149        sha3_test_vector(
150            b"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
151            "41c0dba2a9d6240849100376a8235e2c82e1b9998a999e21db32dd97496d3376",
152        );
153        sha3_test_vector(
154            b"abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu",
155            "916f6061fe879741ca6469b43971dfdb28b1a32dc36cb3254e812be27aad1d18"
156        );
157    }
158
159    #[test]
160    fn sha3_256_multi_threaded() {
161        let mut children = Vec::new();
162        for _ in 0..10 {
163            children.push(std::thread::spawn(|| {
164                sha3_test_vector(
165                    b"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
166                    "41c0dba2a9d6240849100376a8235e2c82e1b9998a999e21db32dd97496d3376",
167                );
168            }))
169        }
170        for child in children {
171            child.join().unwrap();
172        }
173    }
174}