1
//! Types related to binding messages to specific circuits
2

            
3
#[cfg(feature = "hs-service")]
4
use tor_hscrypto::ops::HsMacKey;
5
use zeroize::Zeroizing;
6

            
7
/// Number of bytes of circuit binding material negotiated per circuit hop.
8
pub(crate) const CIRC_BINDING_LEN: usize = 20;
9

            
10
/// Cryptographic information used to bind a message to a specific circuit.
11
///
12
/// This information is used in some of our protocols (currently only the onion
13
/// services protocol) to prove that a given message was referring to a specific
14
/// hop on a specific circuit, and was not replayed from another circuit.
15
///
16
/// In `tor-spec` and `rend-spec`, this value is called `KH`.
17
#[derive(Clone)]
18
pub struct CircuitBinding(
19
    // We use a Box here to avoid  moves that would bypass the zeroize-on-drop
20
    // semantics.
21
    //
22
    // (This is not super-critical, since the impact of leaking one of these
23
    // keys is slight, but it's best not to leak them at all.)
24
    Box<Zeroizing<[u8; CIRC_BINDING_LEN]>>,
25
);
26

            
27
impl From<[u8; CIRC_BINDING_LEN]> for CircuitBinding {
28
194
    fn from(value: [u8; CIRC_BINDING_LEN]) -> Self {
29
194
        Self(Box::new(Zeroizing::new(value)))
30
194
    }
31
}
32

            
33
impl TryFrom<&[u8]> for CircuitBinding {
34
    type Error = crate::Error;
35

            
36
194
    fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
37
194
        let value: &[u8; CIRC_BINDING_LEN] = &value
38
194
            .try_into()
39
194
            .or(Err(Self::Error::InvalidKDFOutputLength))?;
40
194
        Ok(Self::from(*value))
41
194
    }
42
}
43

            
44
impl CircuitBinding {
45
    /// Return a view of this key suitable for computing the MAC function used
46
    /// to authenticate onion services' ESTABLISH_INTRODUCE messages.
47
    ///
48
    /// Note that this is not a general-purpose MAC; please avoid adding new
49
    /// users of it.  See notes on [`hs_mac`](tor_hscrypto::ops::hs_mac) for
50
    /// more information.
51
    #[cfg(feature = "hs-service")]
52
    pub fn hs_mac(&self) -> HsMacKey<'_> {
53
        HsMacKey::from(self.dangerously_into_bytes())
54
    }
55

            
56
    /// Return a view of this key as a byte-slice.
57
    ///
58
    /// This is potentially dangerous, since we don't want to expose this
59
    /// information: We only want to use it as a MAC key.
60
    #[cfg(feature = "hs-service")]
61
    fn dangerously_into_bytes(&self) -> &[u8] {
62
        &(**self.0)[..]
63
    }
64
}