1
//! Code for a replay log for [`Introduce2`] messages.
2

            
3
use super::{ReplayLogType, MAGIC_LEN, OUTPUT_LEN, REPLAY_LOG_SUFFIX};
4
use crate::internal_prelude::*;
5
use hash::hash;
6
use tor_cell::relaycell::msg::Introduce2;
7

            
8
/// A [`ReplayLogType`] to indicate using [`Introduce2`] messages with [`IptLocalId`] names.
9
pub(crate) struct IptReplayLogType;
10

            
11
impl 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
40
    fn format_filename(name: &IptLocalId) -> String {
20
40
        format!("{name}{REPLAY_LOG_SUFFIX}")
21
40
    }
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
88
    fn parse_log_leafname(leaf: &OsStr) -> Result<IptLocalId, Cow<'static, str>> {
40
88
        let leaf = leaf.to_str().ok_or("not proper unicode")?;
41
88
        let lid = leaf.strip_suffix(REPLAY_LOG_SUFFIX).ok_or("not *.bin")?;
42
88
        let lid: IptLocalId = lid
43
88
            .parse()
44
88
            .map_err(|e: crate::InvalidIptLocalId| e.to_string())?;
45
88
        Ok(lid)
46
88
    }
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.
64
mod 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
6
    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
6
        let mut d = KangarooTwelve::default();
81
6
        let mut output = [0; OUTPUT_LEN];
82
6
        d.update(s);
83
6
        d.finalize_xof_into(&mut output);
84
6
        output
85
6
    }
86

            
87
    #[cfg(test)]
88
    mod test {
89
        use super::*;
90

            
91
        #[test]
92
2
        fn hash_basics() {
93
2
            let a = hash(b"123");
94
2
            let b = hash(b"123");
95
2
            let c = hash(b"1234");
96
2
            assert_eq!(a, b);
97
2
            assert_ne!(a, c);
98
2
        }
99
    }
100
}