1
//! Mid-level cryptographic operations used in the onion service protocol.
2
use tor_llcrypto::d::Sha3_256;
3
use tor_llcrypto::util::ct::CtByteArray;
4

            
5
use digest::Digest;
6

            
7
/// The length of the MAC returned by [`hs_mac`].
8
pub const HS_MAC_LEN: usize = 32;
9

            
10
/// Compute the lightweight MAC function used in the onion service protocol.
11
///
12
/// (See rend-spec-v3 section 0.3 `MAC`.)
13
///
14
/// This is not a great MAC; KMAC wasn't defined at the time that the HSv3
15
/// design was written. Please don't use this MAC in new protocols.
16
4395
pub fn hs_mac(key: &[u8], msg: &[u8]) -> CtByteArray<HS_MAC_LEN> {
17
    // rend-spec-v3 says: "Instantiate H with SHA3-256... Instantiate MAC(key=k,
18
    // message=m) with H(k_len | k | m), where k_len is htonll(len(k))."
19

            
20
4395
    let mut hasher = Sha3_256::new();
21
4395
    let klen = key.len() as u64;
22
4395
    hasher.update(klen.to_be_bytes());
23
4395
    hasher.update(key);
24
4395
    hasher.update(msg);
25
4395
    let a: [u8; HS_MAC_LEN] = hasher.finalize().into();
26
4395
    a.into()
27
4395
}
28

            
29
/// Reference to a slice of bytes usable to compute the [`hs_mac`] function.
30
#[derive(Copy, Clone, derive_more::From)]
31
pub struct HsMacKey<'a>(&'a [u8]);
32

            
33
impl<'a> tor_llcrypto::traits::ShortMac<HS_MAC_LEN> for HsMacKey<'a> {
34
41
    fn mac(&self, input: &[u8]) -> CtByteArray<HS_MAC_LEN> {
35
41
        hs_mac(self.0, input)
36
41
    }
37

            
38
123
    fn validate(&self, input: &[u8], mac: &[u8; HS_MAC_LEN]) -> subtle::Choice {
39
        use subtle::ConstantTimeEq;
40
123
        let m = hs_mac(self.0, input);
41
123
        m.as_ref().ct_eq(mac)
42
123
    }
43
}
44

            
45
#[cfg(test)]
46
mod test {
47
    // @@ begin test lint list maintained by maint/add_warning @@
48
    #![allow(clippy::bool_assert_comparison)]
49
    #![allow(clippy::clone_on_copy)]
50
    #![allow(clippy::dbg_macro)]
51
    #![allow(clippy::mixed_attributes_style)]
52
    #![allow(clippy::print_stderr)]
53
    #![allow(clippy::print_stdout)]
54
    #![allow(clippy::single_char_pattern)]
55
    #![allow(clippy::unwrap_used)]
56
    #![allow(clippy::unchecked_time_subtraction)]
57
    #![allow(clippy::useless_vec)]
58
    #![allow(clippy::needless_pass_by_value)]
59
    #![allow(clippy::string_slice)] // See arti#2571
60
    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
61

            
62
    use super::*;
63
    use hex_literal::hex;
64

            
65
    /// Helper: just call Sha3_256 and return the result as a CtByteArray.
66
    fn d(s: &[u8]) -> CtByteArray<32> {
67
        let a: [u8; 32] = Sha3_256::digest(s).into();
68
        a.into()
69
    }
70

            
71
    #[test]
72
    fn mac_from_definition() {
73
        assert_eq!(hs_mac(b"", b""), d(&[0; 8]));
74
        assert_eq!(
75
            hs_mac(b"hello", b"world"),
76
            d(b"\0\0\0\0\0\0\0\x05helloworld")
77
        );
78
        assert_eq!(
79
            hs_mac(b"helloworl", b"d"),
80
            d(b"\0\0\0\0\0\0\0\x09helloworld")
81
        );
82
    }
83

            
84
    #[test]
85
    fn mac_testvec() {
86
        // From C Tor; originally generated in Python.
87
        let msg = b"i am in a library somewhere using my computer";
88
        let key = b"i'm from the past talking to the future.";
89

            
90
        assert_eq!(
91
            hs_mac(key, msg).as_ref(),
92
            &hex!("753fba6d87d49497238a512a3772dd291e55f7d1cd332c9fb5c967c7a10a13ca")
93
        );
94
    }
95
}