tor_hsservice/replay/
ipt.rs

1//! Code for a replay log for [`Introduce2`] messages.
2
3use super::{ReplayLogType, MAGIC_LEN, OUTPUT_LEN, REPLAY_LOG_SUFFIX};
4use crate::internal_prelude::*;
5use hash::hash;
6use tor_cell::relaycell::msg::Introduce2;
7
8/// A [`ReplayLogType`] to indicate using [`Introduce2`] messages with [`IptLocalId`] names.
9pub(crate) struct IptReplayLogType;
10
11impl ReplayLogType for IptReplayLogType {
12    type Name = IptLocalId;
13    type Message = Introduce2;
14
15    // It would be better to specifically say that this is a IPT replay log here, but for backwards
16    // compatability we should keep this as-is.
17    const MAGIC: &'static [u8; MAGIC_LEN] = b"<tor hss replay Kangaroo12>\n\0\0\0\0";
18
19    fn format_filename(name: &IptLocalId) -> String {
20        format!("{name}{REPLAY_LOG_SUFFIX}")
21    }
22
23    fn transform_message(message: &Introduce2) -> [u8; OUTPUT_LEN] {
24        // This line here is really subtle!  The decision of _what object_
25        // to check for replays is critical to making sure that the
26        // introduction point cannot do replays by modifying small parts of
27        // the replayed object.  So we don't check the header; instead, we
28        // check the encrypted body.  This in turn works only because the
29        // encryption format is non-malleable: modifying the encrypted
30        // message has negligible probability of making a message that can
31        // be decrypted.
32        //
33        // (Ancient versions of onion services used a malleable encryption
34        // format here, which made replay detection even harder.
35        // Fortunately, we don't have that problem in the current protocol)
36        hash(message.encrypted_body())
37    }
38
39    fn parse_log_leafname(leaf: &OsStr) -> Result<IptLocalId, Cow<'static, str>> {
40        let leaf = leaf.to_str().ok_or("not proper unicode")?;
41        let lid = leaf.strip_suffix(REPLAY_LOG_SUFFIX).ok_or("not *.bin")?;
42        let lid: IptLocalId = lid
43            .parse()
44            .map_err(|e: crate::InvalidIptLocalId| e.to_string())?;
45        Ok(lid)
46    }
47}
48
49/// Implementation code for pre-hashing our inputs.
50///
51/// We do this because we don't actually want to record the entirety of each
52/// encrypted introduction request.
53///
54/// We aren't terribly concerned about collision resistance: accidental
55/// collision don't matter, since we are okay with a false-positive rate.
56/// Intentional collisions are also okay, since the only impact of generating
57/// one would be that you could make an introduce2 message _of your own_ get
58/// rejected.
59///
60/// The impact of preimages is also not so bad. If somebody can reconstruct the
61/// original message, they still get an encrypted object, and need the
62/// `KP_hss_ntor` key to do anything with it. A second preimage attack just
63/// gives another message we won't accept.
64mod hash {
65    use super::OUTPUT_LEN;
66
67    /// Compute a hash from a given bytestring.
68    ///
69    /// We only keep 128 bits; see note above in the module documentation about why
70    /// this is okay.
71    pub(super) fn hash(s: &[u8]) -> [u8; OUTPUT_LEN] {
72        /// If we change OUTPUT_LEN, this function will need to change.
73        const _: () = assert!(OUTPUT_LEN == 16);
74
75        // I'm choosing kangaroo-twelve for its speed. This doesn't affect
76        // compatibility, so it's okay to use something a bit odd, since we can
77        // change it later if we want.
78        use digest::{ExtendableOutput, Update};
79        use k12::KangarooTwelve;
80        let mut d = KangarooTwelve::default();
81        let mut output = [0; OUTPUT_LEN];
82        d.update(s);
83        d.finalize_xof_into(&mut output);
84        output
85    }
86
87    #[cfg(test)]
88    mod test {
89        use super::*;
90
91        #[test]
92        fn hash_basics() {
93            let a = hash(b"123");
94            let b = hash(b"123");
95            let c = hash(b"1234");
96            assert_eq!(a, b);
97            assert_ne!(a, c);
98        }
99    }
100}