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::pow::ProofOfWork;
8
use crate::relaycell::{
9
    extend::{CcRequest, CircRequestExt, SubprotocolRequest},
10
    extlist::ExtList,
11
};
12
use caret::caret_int;
13
use tor_bytes::{EncodeError, EncodeResult, Error, Readable, Reader, Result, Writeable, Writer};
14
use tor_hscrypto::RendCookie;
15
use tor_linkspec::EncodedLinkSpec;
16

            
17
caret_int! {
18
    /// An enumeration value to identify a type of onion key.
19
    ///
20
    /// Corresponds to `ONION_KEY_TYPE` in section 3.3 of
21
    /// rend-spec-v3.txt \[PROCESS_INTRO].
22
    //
23
    // TODO this shouldn't live here.  It ought to be in some more general crate.
24
    // But it should then also be usable in the netdoc parser.  In particular, it ought
25
    // to be able to handle the *textual* values in `hsdesc/inner.rs`, and maybe
26
    // the ad-hocery in the routerdesc parsing too.
27
    struct OnionKeyType(u8) {
28
        NTOR = 0x01,
29
    }
30
}
31

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

            
49
impl Readable for OnionKey {
50
62
    fn take_from(r: &mut Reader<'_>) -> Result<Self> {
51
62
        let kind: OnionKeyType = r.take_u8()?.into();
52
63
        r.read_nested_u16len(|r_inner| match kind {
53
62
            OnionKeyType::NTOR => Ok(OnionKey::NtorOnionKey(r_inner.extract()?)),
54
            _ => Err(Error::InvalidMessage(
55
                format!("Unrecognized onion key type {kind}").into(),
56
            )),
57
62
        })
58
62
    }
59
}
60

            
61
impl Writeable for OnionKey {
62
172
    fn write_onto<B: Writer + ?Sized>(&self, w: &mut B) -> EncodeResult<()> {
63
172
        match self {
64
172
            OnionKey::NtorOnionKey(key) => {
65
172
                w.write_u8(OnionKeyType::NTOR.into());
66
172
                let mut w_inner = w.write_nested_u16len();
67
172
                w_inner.write(key)?;
68
172
                w_inner.finish()?;
69
            }
70
        }
71
172
        Ok(())
72
172
    }
73
}
74

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

            
111
impl Readable for IntroduceHandshakePayload {
112
62
    fn take_from(r: &mut Reader<'_>) -> Result<Self> {
113
62
        let cookie = r.extract()?;
114
62
        let extensions = r.extract()?;
115
62
        let onion_key = r.extract()?;
116
62
        let n_link_specifiers = r.take_u8()?;
117
62
        let link_specifiers = r.extract_n(n_link_specifiers.into())?;
118
62
        Ok(Self {
119
62
            cookie,
120
62
            extensions,
121
62
            onion_key,
122
62
            link_specifiers,
123
62
        })
124
62
    }
125
}
126

            
127
impl Writeable for IntroduceHandshakePayload {
128
172
    fn write_onto<B: Writer + ?Sized>(&self, w: &mut B) -> EncodeResult<()> {
129
172
        w.write(&self.cookie)?;
130
172
        w.write(&self.extensions)?;
131
172
        w.write(&self.onion_key)?;
132
172
        w.write_u8(
133
172
            self.link_specifiers
134
172
                .len()
135
172
                .try_into()
136
172
                .map_err(|_| EncodeError::BadLengthValue)?,
137
        );
138
516
        self.link_specifiers.iter().try_for_each(|ls| w.write(ls))?;
139

            
140
172
        Ok(())
141
172
    }
142
}
143

            
144
impl IntroduceHandshakePayload {
145
    /// Construct a new [`IntroduceHandshakePayload`]
146
5332
    pub fn new(
147
5332
        cookie: RendCookie,
148
5332
        onion_key: OnionKey,
149
5332
        link_specifiers: Vec<EncodedLinkSpec>,
150
5332
        proof_of_work: Option<ProofOfWork>,
151
5332
    ) -> Self {
152
5332
        let mut extensions = ExtList::default();
153
5332
        if let Some(proof_of_work) = proof_of_work {
154
            extensions.push(proof_of_work.into());
155
5332
        }
156
5332
        Self {
157
5332
            cookie,
158
5332
            extensions,
159
5332
            onion_key,
160
5332
            link_specifiers,
161
5332
        }
162
5332
    }
163

            
164
    /// Return the rendezvous cookie specified in this handshake payload.
165
    pub fn cookie(&self) -> &RendCookie {
166
        &self.cookie
167
    }
168

            
169
    /// Return the provided onion key for the specified rendezvous point
170
    pub fn onion_key(&self) -> &OnionKey {
171
        &self.onion_key
172
    }
173

            
174
    /// Return the provided link specifiers for the specified rendezvous point.
175
    pub fn link_specifiers(&self) -> &[EncodedLinkSpec] {
176
        &self.link_specifiers[..]
177
    }
178

            
179
    /// Return the proof-of-work extension for this request.
180
    pub fn proof_of_work_extension(&self) -> Option<&ProofOfWork> {
181
        self.extensions.get_proof_of_work()
182
    }
183

            
184
    /// Return the cc request extension for this request.
185
    pub fn cc_request_extension(&self) -> Option<&CcRequest> {
186
        self.extensions.get_cc_request()
187
    }
188

            
189
    /// Return the subprotocol request extension for this request.
190
    pub fn subprotocol_request_extension(&self) -> Option<&SubprotocolRequest> {
191
        self.extensions.get_subprotocol_request()
192
    }
193

            
194
    /// Add a new extension `ext` to the list of extensions in this payload.
195
5270
    pub fn add_extension(&mut self, ext: CircRequestExt) {
196
5270
        self.extensions.push(ext);
197
5270
    }
198
}