ore_rs/primitives/
hash.rs

1use crate::primitives::{AesBlock, Hash, HashKey};
2use aes::cipher::{generic_array::GenericArray, BlockEncrypt, KeyInit};
3use aes::Aes128;
4use zeroize::ZeroizeOnDrop;
5
6#[derive(ZeroizeOnDrop)]
7pub struct Aes128Z2Hash {
8    cipher: Aes128,
9}
10
11impl Hash for Aes128Z2Hash {
12    fn new(key: &HashKey) -> Self {
13        let key_array = GenericArray::from_slice(key);
14        let cipher = Aes128::new(key_array);
15        Self { cipher }
16    }
17
18    fn hash(&self, data: &[u8]) -> u8 {
19        /*
20         * Slice size is not known at compile time so we assert here
21         * We could do this with compile checks but this would require an additional
22         * copy (and doesn't entirely avoid runtime checks anyway)
23         * See https://stackoverflow.com/questions/38168956/take-slice-of-certain-length-known-at-compile-time
24         */
25        assert_eq!(data.len(), 16);
26        // Can we clone into GenericArray directly? Are we doing an extra copy?
27        let mut output = [0u8; 16];
28        output.clone_from_slice(data);
29        let block = GenericArray::from_mut_slice(&mut output);
30        self.cipher.encrypt_block(block);
31        output[0] & 1u8
32    }
33
34    // TODO: this mutates - see how much a copy effects performance (clone_from_slice)
35    fn hash_all(&self, data: &mut [AesBlock]) -> Vec<u8> {
36        self.cipher.encrypt_blocks(data);
37
38        let mut vec = Vec::with_capacity(data.len());
39        for &mut block in data {
40            // Output is Z2 (1-bit)
41            vec.push(block[0] & 1u8);
42        }
43
44        vec
45    }
46}
47
48#[cfg(test)]
49mod tests {
50    use super::*;
51    use hex_literal::hex;
52
53    fn init_hash() -> Aes128Z2Hash {
54        let key: [u8; 16] = hex!("00010203 04050607 08090a0b 0c0d0e0f");
55        let key_array = GenericArray::from_slice(&key);
56        Hash::new(key_array)
57    }
58
59    #[test]
60    fn hash_test_1() {
61        let hash = init_hash();
62        let input: [u8; 16] = hex!("00010203 04050607 08090a0b 0c0d0eaa");
63
64        assert_eq!(1u8, hash.hash(&input));
65    }
66
67    #[test]
68    fn hash_test_2() {
69        let hash = init_hash();
70        let input: [u8; 16] = hex!("00010203 04050607 08090a0b 0c0d0e0f");
71
72        assert_eq!(0u8, hash.hash(&input));
73    }
74
75    #[test]
76    #[should_panic(expected = "assertion failed")]
77    fn hash_test_input_too_small() {
78        let hash = init_hash();
79        let input: [u8; 8] = hex!("00010203 04050607");
80
81        assert_eq!(0u8, hash.hash(&input));
82    }
83
84    #[test]
85    #[should_panic(expected = "assertion failed")]
86    fn hash_test_input_too_large() {
87        let hash = init_hash();
88        let input: [u8; 24] = hex!("00010203 04050607 ffffffff bbbbbbbb cccccccc abababab");
89
90        hash.hash(&input);
91    }
92}