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

            
7
use super::ext::{decl_extension_group, ExtGroup, ExtList, UnrecognizedExt};
8
use caret::caret_int;
9
use tor_bytes::{EncodeError, EncodeResult, Error, Readable, Reader, Result, Writeable, Writer};
10
use tor_hscrypto::RendCookie;
11
use tor_linkspec::EncodedLinkSpec;
12

            
13
caret_int! {
14
    /// Type code for an extension in an [`IntroduceHandshakePayload`].
15
    #[derive(Ord,PartialOrd)]
16
    pub struct IntroPayloadExtType(u8) {
17
    }
18
}
19

            
20
decl_extension_group! {
21
    /// An extension to an [`IntroduceHandshakePayload`].
22
    ///
23
    /// (Currently, no extensions of this type are recognized.)
24
    #[derive(Debug,Clone)]
25
    enum IntroPayloadExt [ IntroPayloadExtType ] {
26
    }
27
}
28

            
29
caret_int! {
30
    /// An enumeration value to identify a type of onion key.
31
    ///
32
    /// Corresponds to `ONION_KEY_TYPE` in section 3.3 of
33
    /// rend-spec-v3.txt \[PROCESS_INTRO].
34
    //
35
    // TODO this shouldn't live here.  It ought to be in some more general crate.
36
    // But it should then also be usable in the netdoc parser.  In particular, it ought
37
    // to be able to handle the *textual* values in `hsdesc/inner.rs`, and maybe
38
    // the ad-hocery in the routerdesc parsing too.
39
    struct OnionKeyType(u8) {
40
        NTOR = 0x01,
41
    }
42
}
43

            
44
/// An onion key provided in an IntroduceHandshakePayload.
45
///
46
/// Corresponds to `ONION_KEY` in the spec.
47
//
48
// TODO: Is there a logical type somewhere else to coalesce this with?
49
// Currently there is no wrapper around curve25519::PublicKey when it's used as
50
// an Ntor key, nor is there (yet) a generic onion key enum.  tor-linkspec might be
51
// the logical place for those.  See arti#893.
52
#[derive(Clone, Debug)]
53
#[non_exhaustive]
54
pub enum OnionKey {
55
    /// A key usable with the ntor or ntor-v3 handshake.
56
    NtorOnionKey(tor_llcrypto::pk::curve25519::PublicKey),
57
    // There is no "unknown" variant for this type, since we don't support any
58
    // other key types yet.
59
}
60

            
61
impl Readable for OnionKey {
62
    fn take_from(r: &mut Reader<'_>) -> Result<Self> {
63
        let kind: OnionKeyType = r.take_u8()?.into();
64
        r.read_nested_u16len(|r_inner| match kind {
65
            OnionKeyType::NTOR => Ok(OnionKey::NtorOnionKey(r_inner.extract()?)),
66
            _ => Err(Error::InvalidMessage(
67
                format!("Unrecognized onion key type {kind}").into(),
68
            )),
69
        })
70
    }
71
}
72

            
73
impl Writeable for OnionKey {
74
    fn write_onto<B: Writer + ?Sized>(&self, w: &mut B) -> EncodeResult<()> {
75
        match self {
76
            OnionKey::NtorOnionKey(key) => {
77
                w.write_u8(OnionKeyType::NTOR.into());
78
                let mut w_inner = w.write_nested_u16len();
79
                w_inner.write(key)?;
80
                w_inner.finish()?;
81
            }
82
        }
83
        Ok(())
84
    }
85
}
86

            
87
/// The plaintext of the encrypted portion of an INTRODUCE{1,2} message.
88
///
89
/// This is not a RelayMsg itself; it is instead used as the payload for an
90
/// `hs-ntor` handshake, which is passed to the onion service in `Introduce[12]`
91
/// message.
92
///
93
/// This payload is sent from a client to the onion service to tell it how to reach
94
/// the client's chosen rendezvous point.
95
///
96
/// This corresponds to the "decrypted payload" in section 3.3 of
97
/// rend-spec-v3.txt, **excluding the PAD field**.
98
///
99
/// The user of this type is expected to discard, or generate, appropriate
100
/// padding, as required.
101
#[derive(Clone, Debug)]
102
pub struct IntroduceHandshakePayload {
103
    /// The rendezvous cookie to use at the rendezvous point.
104
    ///
105
    /// (`RENDEZVOUS_COOKIE` in the spec.)
106
    cookie: RendCookie,
107
    /// A list of extensions to this payload.
108
    ///
109
    /// (`N_EXTENSIONS`, `EXT_FIELD_TYPE`, `EXT_FIELD_LEN`, and `EXT_FIELD` in
110
    /// the spec.)
111
    extensions: ExtList<IntroPayloadExt>,
112
    /// The onion key to use when extending a circuit to the rendezvous point.
113
    ///
114
    /// (`ONION_KEY_TYPE`, `ONION_KEY_LEN`, and `ONION_KEY` in the spec. This
115
    /// represents `KP_ntor` for the rendezvous point.)
116
    onion_key: OnionKey,
117
    /// A list of link specifiers to identify the rendezvous point.
118
    ///
119
    /// (`NSPEC`, `LSTYPE`, `LSLEN`, and `LSPEC` in the spec.)
120
    link_specifiers: Vec<EncodedLinkSpec>,
121
}
122

            
123
impl Readable for IntroduceHandshakePayload {
124
    fn take_from(r: &mut Reader<'_>) -> Result<Self> {
125
        let cookie = r.extract()?;
126
        let extensions = r.extract()?;
127
        let onion_key = r.extract()?;
128
        let n_link_specifiers = r.take_u8()?;
129
        let link_specifiers = r.extract_n(n_link_specifiers.into())?;
130
        Ok(Self {
131
            cookie,
132
            extensions,
133
            onion_key,
134
            link_specifiers,
135
        })
136
    }
137
}
138

            
139
impl Writeable for IntroduceHandshakePayload {
140
    fn write_onto<B: Writer + ?Sized>(&self, w: &mut B) -> EncodeResult<()> {
141
        w.write(&self.cookie)?;
142
        w.write(&self.extensions)?;
143
        w.write(&self.onion_key)?;
144
        w.write_u8(
145
            self.link_specifiers
146
                .len()
147
                .try_into()
148
                .map_err(|_| EncodeError::BadLengthValue)?,
149
        );
150
        self.link_specifiers.iter().try_for_each(|ls| w.write(ls))?;
151

            
152
        Ok(())
153
    }
154
}
155

            
156
impl IntroduceHandshakePayload {
157
    /// Construct a new [`IntroduceHandshakePayload`]
158
    pub fn new(
159
        cookie: RendCookie,
160
        onion_key: OnionKey,
161
        link_specifiers: Vec<EncodedLinkSpec>,
162
    ) -> Self {
163
        let extensions = ExtList::default();
164
        Self {
165
            cookie,
166
            extensions,
167
            onion_key,
168
            link_specifiers,
169
        }
170
    }
171

            
172
    /// Return the rendezvous cookie specified in this handshake payload.
173
    pub fn cookie(&self) -> &RendCookie {
174
        &self.cookie
175
    }
176

            
177
    /// Return the provided onion key for the specified rendezvous point
178
    pub fn onion_key(&self) -> &OnionKey {
179
        &self.onion_key
180
    }
181

            
182
    /// Return the provided link specifiers for the specified rendezvous point.
183
    pub fn link_specifiers(&self) -> &[EncodedLinkSpec] {
184
        &self.link_specifiers[..]
185
    }
186
}