1use tor_llcrypto::d::Sha3_256;
3use tor_llcrypto::util::ct::CtByteArray;
4
5use digest::Digest;
6
7pub const HS_MAC_LEN: usize = 32;
9
10pub fn hs_mac(key: &[u8], msg: &[u8]) -> CtByteArray<HS_MAC_LEN> {
17 let mut hasher = Sha3_256::new();
21 let klen = key.len() as u64;
22 hasher.update(klen.to_be_bytes());
23 hasher.update(key);
24 hasher.update(msg);
25 let a: [u8; HS_MAC_LEN] = hasher.finalize().into();
26 a.into()
27}
28
29#[derive(Copy, Clone, derive_more::From)]
31pub struct HsMacKey<'a>(&'a [u8]);
32
33impl<'a> tor_llcrypto::traits::ShortMac<HS_MAC_LEN> for HsMacKey<'a> {
34 fn mac(&self, input: &[u8]) -> CtByteArray<HS_MAC_LEN> {
35 hs_mac(self.0, input)
36 }
37
38 fn validate(&self, input: &[u8], mac: &[u8; HS_MAC_LEN]) -> subtle::Choice {
39 use subtle::ConstantTimeEq;
40 let m = hs_mac(self.0, input);
41 m.as_ref().ct_eq(mac)
42 }
43}
44
45#[cfg(test)]
46mod test {
47 #![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_duration_subtraction)]
57 #![allow(clippy::useless_vec)]
58 #![allow(clippy::needless_pass_by_value)]
59 use super::*;
62 use hex_literal::hex;
63
64 fn d(s: &[u8]) -> CtByteArray<32> {
66 let a: [u8; 32] = Sha3_256::digest(s).into();
67 a.into()
68 }
69
70 #[test]
71 fn mac_from_definition() {
72 assert_eq!(hs_mac(b"", b""), d(&[0; 8]));
73 assert_eq!(
74 hs_mac(b"hello", b"world"),
75 d(b"\0\0\0\0\0\0\0\x05helloworld")
76 );
77 assert_eq!(
78 hs_mac(b"helloworl", b"d"),
79 d(b"\0\0\0\0\0\0\0\x09helloworld")
80 );
81 }
82
83 #[test]
84 fn mac_testvec() {
85 let msg = b"i am in a library somewhere using my computer";
87 let key = b"i'm from the past talking to the future.";
88
89 assert_eq!(
90 hs_mac(key, msg).as_ref(),
91 &hex!("753fba6d87d49497238a512a3772dd291e55f7d1cd332c9fb5c967c7a10a13ca")
92 );
93 }
94}