tor_cell/relaycell/hs/
intro_payload.rs

1//! The encrypted portion of an INTRODUCE{1,2} message.
2//!
3//! (This is as described as the "decrypted plaintext" in section 3.3 of
4//! rend-spec-v3.txt.  It tells the onion service how to find the rendezvous
5//! point, and how to handshake with the client there.)
6
7use super::pow::ProofOfWork;
8use crate::relaycell::{extend::CircRequestExt, extlist::ExtList};
9use caret::caret_int;
10use tor_bytes::{EncodeError, EncodeResult, Error, Readable, Reader, Result, Writeable, Writer};
11use tor_hscrypto::RendCookie;
12use tor_linkspec::EncodedLinkSpec;
13
14caret_int! {
15    /// An enumeration value to identify a type of onion key.
16    ///
17    /// Corresponds to `ONION_KEY_TYPE` in section 3.3 of
18    /// rend-spec-v3.txt \[PROCESS_INTRO].
19    //
20    // TODO this shouldn't live here.  It ought to be in some more general crate.
21    // But it should then also be usable in the netdoc parser.  In particular, it ought
22    // to be able to handle the *textual* values in `hsdesc/inner.rs`, and maybe
23    // the ad-hocery in the routerdesc parsing too.
24    struct OnionKeyType(u8) {
25        NTOR = 0x01,
26    }
27}
28
29/// An onion key provided in an IntroduceHandshakePayload.
30///
31/// Corresponds to `ONION_KEY` in the spec.
32//
33// TODO: Is there a logical type somewhere else to coalesce this with?
34// Currently there is no wrapper around curve25519::PublicKey when it's used as
35// an Ntor key, nor is there (yet) a generic onion key enum.  tor-linkspec might be
36// the logical place for those.  See arti#893.
37#[derive(Clone, Debug)]
38#[non_exhaustive]
39pub enum OnionKey {
40    /// A key usable with the ntor or ntor-v3 handshake.
41    NtorOnionKey(tor_llcrypto::pk::curve25519::PublicKey),
42    // There is no "unknown" variant for this type, since we don't support any
43    // other key types yet.
44}
45
46impl Readable for OnionKey {
47    fn take_from(r: &mut Reader<'_>) -> Result<Self> {
48        let kind: OnionKeyType = r.take_u8()?.into();
49        r.read_nested_u16len(|r_inner| match kind {
50            OnionKeyType::NTOR => Ok(OnionKey::NtorOnionKey(r_inner.extract()?)),
51            _ => Err(Error::InvalidMessage(
52                format!("Unrecognized onion key type {kind}").into(),
53            )),
54        })
55    }
56}
57
58impl Writeable for OnionKey {
59    fn write_onto<B: Writer + ?Sized>(&self, w: &mut B) -> EncodeResult<()> {
60        match self {
61            OnionKey::NtorOnionKey(key) => {
62                w.write_u8(OnionKeyType::NTOR.into());
63                let mut w_inner = w.write_nested_u16len();
64                w_inner.write(key)?;
65                w_inner.finish()?;
66            }
67        }
68        Ok(())
69    }
70}
71
72/// The plaintext of the encrypted portion of an INTRODUCE{1,2} message.
73///
74/// This is not a RelayMsg itself; it is instead used as the payload for an
75/// `hs-ntor` handshake, which is passed to the onion service in `Introduce[12]`
76/// message.
77///
78/// This payload is sent from a client to the onion service to tell it how to reach
79/// the client's chosen rendezvous point.
80///
81/// This corresponds to the "decrypted payload" in section 3.3 of
82/// rend-spec-v3.txt, **excluding the PAD field**.
83///
84/// The user of this type is expected to discard, or generate, appropriate
85/// padding, as required.
86#[derive(Clone, Debug)]
87pub struct IntroduceHandshakePayload {
88    /// The rendezvous cookie to use at the rendezvous point.
89    ///
90    /// (`RENDEZVOUS_COOKIE` in the spec.)
91    cookie: RendCookie,
92    /// A list of extensions to this payload.
93    ///
94    /// (`N_EXTENSIONS`, `EXT_FIELD_TYPE`, `EXT_FIELD_LEN`, and `EXT_FIELD` in
95    /// the spec.)
96    extensions: ExtList<CircRequestExt>,
97    /// The onion key to use when extending a circuit to the rendezvous point.
98    ///
99    /// (`ONION_KEY_TYPE`, `ONION_KEY_LEN`, and `ONION_KEY` in the spec. This
100    /// represents `KP_ntor` for the rendezvous point.)
101    onion_key: OnionKey,
102    /// A list of link specifiers to identify the rendezvous point.
103    ///
104    /// (`NSPEC`, `LSTYPE`, `LSLEN`, and `LSPEC` in the spec.)
105    link_specifiers: Vec<EncodedLinkSpec>,
106}
107
108impl Readable for IntroduceHandshakePayload {
109    fn take_from(r: &mut Reader<'_>) -> Result<Self> {
110        let cookie = r.extract()?;
111        let extensions = r.extract()?;
112        let onion_key = r.extract()?;
113        let n_link_specifiers = r.take_u8()?;
114        let link_specifiers = r.extract_n(n_link_specifiers.into())?;
115        Ok(Self {
116            cookie,
117            extensions,
118            onion_key,
119            link_specifiers,
120        })
121    }
122}
123
124impl Writeable for IntroduceHandshakePayload {
125    fn write_onto<B: Writer + ?Sized>(&self, w: &mut B) -> EncodeResult<()> {
126        w.write(&self.cookie)?;
127        w.write(&self.extensions)?;
128        w.write(&self.onion_key)?;
129        w.write_u8(
130            self.link_specifiers
131                .len()
132                .try_into()
133                .map_err(|_| EncodeError::BadLengthValue)?,
134        );
135        self.link_specifiers.iter().try_for_each(|ls| w.write(ls))?;
136
137        Ok(())
138    }
139}
140
141impl IntroduceHandshakePayload {
142    /// Construct a new [`IntroduceHandshakePayload`]
143    pub fn new(
144        cookie: RendCookie,
145        onion_key: OnionKey,
146        link_specifiers: Vec<EncodedLinkSpec>,
147        proof_of_work: Option<ProofOfWork>,
148    ) -> Self {
149        let mut extensions = ExtList::default();
150        if let Some(proof_of_work) = proof_of_work {
151            extensions.push(proof_of_work.into());
152        }
153        Self {
154            cookie,
155            extensions,
156            onion_key,
157            link_specifiers,
158        }
159    }
160
161    /// Return the rendezvous cookie specified in this handshake payload.
162    pub fn cookie(&self) -> &RendCookie {
163        &self.cookie
164    }
165
166    /// Return the provided onion key for the specified rendezvous point
167    pub fn onion_key(&self) -> &OnionKey {
168        &self.onion_key
169    }
170
171    /// Return the provided link specifiers for the specified rendezvous point.
172    pub fn link_specifiers(&self) -> &[EncodedLinkSpec] {
173        &self.link_specifiers[..]
174    }
175}