aboutsummaryrefslogtreecommitdiff
path: root/crates/tor-cell/src/relaycell/conflux.rs
blob: 9c87f3c78e945b26ba313a72ca6a4be60f4f1ed6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
//! Encoding and decoding for relay messages related to conflux.

use super::msg::{Body, empty_body};

use amplify::Getters;
use caret::caret_int;
use derive_deftly::Deftly;
use rand::{CryptoRng, Rng, RngExt};

use tor_bytes::{EncodeResult, Error, Readable, Reader, Result, Writeable, Writer};
use tor_llcrypto::util::ct::CtByteArray;
use tor_memquota::derive_deftly_template_HasMemoryCost;

/// The supported CONFLUX_LINK version.
const CONFLUX_LINK_VERSION: u8 = 1;

/// The length of the nonce from a v1 CONFLUX_LINK message, in bytes.
const V1_LINK_NONCE_LEN: usize = 32;

/// Helper macro for implementing wrapper types over [`Link`]
macro_rules! impl_link_wrapper {
    ($wrapper:ty) => {
        impl $wrapper {
            /// Get the version of this message.
            pub fn version(&self) -> u8 {
                self.0.version
            }

            /// Get the [`V1LinkPayload`] of this message.
            pub fn payload(&self) -> &V1LinkPayload {
                &self.0.payload
            }
        }
    };
}

/// A `CONFLUX_LINK` message.
#[derive(Debug, Clone, Deftly)]
#[derive_deftly(HasMemoryCost)]
pub struct ConfluxLink(Link);

impl ConfluxLink {
    /// Create a new v1 `CONFLUX_LINK` message.
    pub fn new(payload: V1LinkPayload) -> Self {
        let link = Link {
            version: CONFLUX_LINK_VERSION,
            payload,
        };

        Self(link)
    }
}

impl_link_wrapper!(ConfluxLink);

impl Body for ConfluxLink {
    fn decode_from_reader(r: &mut Reader<'_>) -> Result<Self> {
        Link::decode_from_reader(r).map(Self)
    }

    fn encode_onto<W: Writer + ?Sized>(self, w: &mut W) -> EncodeResult<()> {
        self.0.encode_onto(w)
    }
}

/// A `CONFLUX_LINKED` message.
#[derive(Debug, Clone, Deftly)]
#[derive_deftly(HasMemoryCost)]
pub struct ConfluxLinked(Link);

impl ConfluxLinked {
    /// Create a new v1 `CONFLUX_LINKED` message.
    pub fn new(payload: V1LinkPayload) -> Self {
        let link = Link {
            version: CONFLUX_LINK_VERSION,
            payload,
        };

        Self(link)
    }
}

impl_link_wrapper!(ConfluxLinked);

impl Body for ConfluxLinked {
    fn decode_from_reader(r: &mut Reader<'_>) -> Result<Self> {
        Link::decode_from_reader(r).map(Self)
    }

    fn encode_onto<W: Writer + ?Sized>(self, w: &mut W) -> EncodeResult<()> {
        self.0.encode_onto(w)
    }
}

/// A message body shared by [`ConfluxLink`] and [`ConfluxLinked`].
#[derive(Debug, Clone, Deftly)]
#[derive_deftly(HasMemoryCost)]
struct Link {
    /// The circuit linking mechanism version.
    ///
    /// Currently, 0x1 is the only recognized version.
    version: u8,
    /// The v1 payload.
    ///
    // TODO: this will need to be an enum over all supported payload versions,
    // if we ever move on from v1.
    payload: V1LinkPayload,
}

/// The nonce type of a [`V1LinkPayload`].
#[derive(Debug, Clone, Copy, Deftly, PartialEq, Eq)]
#[derive_deftly(HasMemoryCost)]
pub struct V1Nonce(CtByteArray<V1_LINK_NONCE_LEN>);

impl V1Nonce {
    /// Create a random `V1Nonce` to put in a LINK cell.
    pub fn new<R: Rng + CryptoRng>(rng: &mut R) -> V1Nonce {
        let mut nonce = [0_u8; V1_LINK_NONCE_LEN];
        rng.fill(&mut nonce[..]);
        Self(nonce.into())
    }
}

impl Readable for V1Nonce {
    fn take_from(r: &mut Reader<'_>) -> Result<Self> {
        Ok(Self(Readable::take_from(r)?))
    }
}

impl Writeable for V1Nonce {
    fn write_onto<W: Writer + ?Sized>(&self, w: &mut W) -> EncodeResult<()> {
        self.0.write_onto(w)
    }
}

/// The v1 payload of a v1 [`ConfluxLink`] or [`ConfluxLinked`] message.
#[derive(Debug, Clone, Deftly, Getters)]
#[derive_deftly(HasMemoryCost)]
pub struct V1LinkPayload {
    /// Random 256-bit secret, for associating two circuits together.
    nonce: V1Nonce,
    /// The last sequence number sent.
    last_seqno_sent: u64,
    /// The last sequence number received.
    last_seqno_recv: u64,
    /// The desired UX properties.
    desired_ux: V1DesiredUx,
}

impl V1LinkPayload {
    /// Create a new `V1LinkPayload`.
    pub fn new(nonce: V1Nonce, desired_ux: V1DesiredUx) -> Self {
        Self {
            nonce,
            // NOTE: the two sequence number fields are 0 on the initial link.
            // We need to support setting these for reattachment/resumption
            // (see [CONFLUX_SET_MANAGEMENT] and [RESUMPTION]).
            last_seqno_sent: 0,
            last_seqno_recv: 0,
            desired_ux,
        }
    }

    /// Set the last sequence number sent.
    pub fn set_last_seqno_sent(&mut self, seqno: u64) {
        self.last_seqno_sent = seqno;
    }

    /// Set the last sequence number received.
    pub fn set_last_seqno_recv(&mut self, seqno: u64) {
        self.last_seqno_recv = seqno;
    }
}

caret_int! {
    /// The UX properties specified in a `V1LinkPayload`.
    #[derive(Deftly)]
    #[derive_deftly(HasMemoryCost)]
    pub struct V1DesiredUx(u8) {
        /// The sender has no preference.
        NO_OPINION = 0x0,
        /// Use MinRTT scheduling.
        MIN_LATENCY = 0x1,
        /// The low memory version of MIN_LATENCY.
        LOW_MEM_LATENCY = 0x2,
        /// Use LowRTT Scheduling.
        HIGH_THROUGHPUT = 0x3,
        /// The low memory version of HIGH_THROUGHPUT.
        LOW_MEM_THROUGHPUT = 0x4,
    }
}

impl Body for Link {
    fn decode_from_reader(r: &mut Reader<'_>) -> Result<Self> {
        let version = r.take_u8()?;
        if version != CONFLUX_LINK_VERSION {
            return Err(Error::InvalidMessage(
                "Unrecognized CONFLUX_LINK/CONFLUX_LINKED version.".into(),
            ));
        }

        let payload = V1LinkPayload::decode_from_reader(r)?;

        Ok(Self { version, payload })
    }

    fn encode_onto<W: Writer + ?Sized>(self, w: &mut W) -> EncodeResult<()> {
        w.write(&self.version)?;
        self.payload.encode_onto(w)?;
        Ok(())
    }
}

impl Body for V1LinkPayload {
    fn decode_from_reader(r: &mut Reader<'_>) -> Result<Self> {
        let nonce = r.extract()?;
        let last_seqno_sent = r.take_u64()?;
        let last_seqno_recv = r.take_u64()?;
        let desired_ux = r.take_u8()?.into();

        Ok(V1LinkPayload {
            nonce,
            last_seqno_sent,
            last_seqno_recv,
            desired_ux,
        })
    }

    fn encode_onto<W: Writer + ?Sized>(self, w: &mut W) -> EncodeResult<()> {
        let V1LinkPayload {
            nonce,
            last_seqno_sent,
            last_seqno_recv,
            desired_ux,
        } = self;

        w.write(&nonce)?;
        w.write_u64(last_seqno_sent);
        w.write_u64(last_seqno_recv);
        w.write_u8(desired_ux.into());

        Ok(())
    }
}

/// A `CONFLUX_SWITCH` message, sent from a sending endpoint when switching leg
/// in an already linked circuit construction.
#[derive(Clone, Debug, Deftly, Getters)]
#[derive_deftly(HasMemoryCost)]
pub struct ConfluxSwitch {
    /// The relative sequence number.
    #[getter(as_copy)]
    seqno: u32,
}

impl ConfluxSwitch {
    /// Create a new v1 `CONFLUX_SWITCH` message.
    pub fn new(seqno: u32) -> Self {
        Self { seqno }
    }
}

impl Body for ConfluxSwitch {
    fn decode_from_reader(r: &mut Reader<'_>) -> Result<Self> {
        let seqno = r.take_u32()?;
        Ok(Self { seqno })
    }

    fn encode_onto<W: Writer + ?Sized>(self, w: &mut W) -> EncodeResult<()> {
        w.write(&self.seqno)?;
        Ok(())
    }
}

empty_body! {
    /// A `CONFLUX_LINKED_ACK` message.
    pub struct ConfluxLinkedAck {}
}