tor_cell/relaycell/
conflux.rs

1//! Encoding and decoding for relay messages related to conflux.
2
3use super::msg::{empty_body, Body};
4
5use amplify::Getters;
6use caret::caret_int;
7use derive_deftly::Deftly;
8use rand::{CryptoRng, Rng, RngCore};
9
10use tor_bytes::{EncodeResult, Error, Readable, Reader, Result, Writeable, Writer};
11use tor_llcrypto::util::ct::CtByteArray;
12use tor_memquota::derive_deftly_template_HasMemoryCost;
13
14/// The supported CONFLUX_LINK version.
15const CONFLUX_LINK_VERSION: u8 = 1;
16
17/// The length of the nonce from a v1 CONFLUX_LINK message, in bytes.
18const V1_LINK_NONCE_LEN: usize = 32;
19
20/// Helper macro for implementing wrapper types over [`Link`]
21macro_rules! impl_link_wrapper {
22    ($wrapper:ty) => {
23        impl $wrapper {
24            /// Get the version of this message.
25            pub fn version(&self) -> u8 {
26                self.0.version
27            }
28
29            /// Get the [`V1LinkPayload`] of this message.
30            pub fn payload(&self) -> &V1LinkPayload {
31                &self.0.payload
32            }
33        }
34    };
35}
36
37/// A `CONFLUX_LINK` message.
38#[derive(Debug, Clone, Deftly)]
39#[derive_deftly(HasMemoryCost)]
40pub struct ConfluxLink(Link);
41
42impl ConfluxLink {
43    /// Create a new v1 `CONFLUX_LINK` message.
44    pub fn new(payload: V1LinkPayload) -> Self {
45        let link = Link {
46            version: CONFLUX_LINK_VERSION,
47            payload,
48        };
49
50        Self(link)
51    }
52}
53
54impl_link_wrapper!(ConfluxLink);
55
56impl Body for ConfluxLink {
57    fn decode_from_reader(r: &mut Reader<'_>) -> Result<Self> {
58        Link::decode_from_reader(r).map(Self)
59    }
60
61    fn encode_onto<W: Writer + ?Sized>(self, w: &mut W) -> EncodeResult<()> {
62        self.0.encode_onto(w)
63    }
64}
65
66/// A `CONFLUX_LINKED` message.
67#[derive(Debug, Clone, Deftly)]
68#[derive_deftly(HasMemoryCost)]
69pub struct ConfluxLinked(Link);
70
71impl ConfluxLinked {
72    /// Create a new v1 `CONFLUX_LINKED` message.
73    pub fn new(payload: V1LinkPayload) -> Self {
74        let link = Link {
75            version: CONFLUX_LINK_VERSION,
76            payload,
77        };
78
79        Self(link)
80    }
81}
82
83impl_link_wrapper!(ConfluxLinked);
84
85impl Body for ConfluxLinked {
86    fn decode_from_reader(r: &mut Reader<'_>) -> Result<Self> {
87        Link::decode_from_reader(r).map(Self)
88    }
89
90    fn encode_onto<W: Writer + ?Sized>(self, w: &mut W) -> EncodeResult<()> {
91        self.0.encode_onto(w)
92    }
93}
94
95/// A message body shared by [`ConfluxLink`] and [`ConfluxLinked`].
96#[derive(Debug, Clone, Deftly)]
97#[derive_deftly(HasMemoryCost)]
98struct Link {
99    /// The circuit linking mechanism version.
100    ///
101    /// Currently, 0x1 is the only recognized version.
102    version: u8,
103    /// The v1 payload.
104    ///
105    // TODO: this will need to be an enum over all supported payload versions,
106    // if we ever move on from v1.
107    payload: V1LinkPayload,
108}
109
110/// The nonce type of a [`V1LinkPayload`].
111#[derive(Debug, Clone, Copy, Deftly, PartialEq, Eq)]
112#[derive_deftly(HasMemoryCost)]
113pub struct V1Nonce(CtByteArray<V1_LINK_NONCE_LEN>);
114
115impl V1Nonce {
116    /// Create a random `V1Nonce` to put in a LINK cell.
117    pub fn new<R: RngCore + CryptoRng>(rng: &mut R) -> V1Nonce {
118        let mut nonce = [0_u8; V1_LINK_NONCE_LEN];
119        rng.fill(&mut nonce[..]);
120        Self(nonce.into())
121    }
122}
123
124impl Readable for V1Nonce {
125    fn take_from(r: &mut Reader<'_>) -> Result<Self> {
126        Ok(Self(Readable::take_from(r)?))
127    }
128}
129
130impl Writeable for V1Nonce {
131    fn write_onto<W: Writer + ?Sized>(&self, w: &mut W) -> EncodeResult<()> {
132        self.0.write_onto(w)
133    }
134}
135
136/// The v1 payload of a v1 [`ConfluxLink`] or [`ConfluxLinked`] message.
137#[derive(Debug, Clone, Deftly, Getters)]
138#[derive_deftly(HasMemoryCost)]
139pub struct V1LinkPayload {
140    /// Random 256-bit secret, for associating two circuits together.
141    nonce: V1Nonce,
142    /// The last sequence number sent.
143    last_seqno_sent: u64,
144    /// The last sequence number received.
145    last_seqno_recv: u64,
146    /// The desired UX properties.
147    desired_ux: V1DesiredUx,
148}
149
150impl V1LinkPayload {
151    /// Create a new `V1LinkPayload`.
152    pub fn new(nonce: V1Nonce, desired_ux: V1DesiredUx) -> Self {
153        Self {
154            nonce,
155            // NOTE: the two sequence number fields are 0 on the initial link.
156            // We need to support setting these for reattachment/resumption
157            // (see [CONFLUX_SET_MANAGEMENT] and [RESUMPTION]).
158            last_seqno_sent: 0,
159            last_seqno_recv: 0,
160            desired_ux,
161        }
162    }
163
164    /// Set the last sequence number sent.
165    pub fn set_last_seqno_sent(&mut self, seqno: u64) {
166        self.last_seqno_sent = seqno;
167    }
168
169    /// Set the last sequence number received.
170    pub fn set_last_seqno_recv(&mut self, seqno: u64) {
171        self.last_seqno_recv = seqno;
172    }
173}
174
175caret_int! {
176    /// The UX properties specified in a `V1LinkPayload`.
177    #[derive(Deftly)]
178    #[derive_deftly(HasMemoryCost)]
179    pub struct V1DesiredUx(u8) {
180        /// The sender has no preference.
181        NO_OPINION = 0x0,
182        /// Use MinRTT scheduling.
183        MIN_LATENCY = 0x1,
184        /// The low memory version of MIN_LATENCY.
185        LOW_MEM_LATENCY = 0x2,
186        /// Use LowRTT Scheduling.
187        HIGH_THROUGHPUT = 0x3,
188        /// The low memory version of HIGH_THROUGHPUT.
189        LOW_MEM_THROUGHPUT = 0x4,
190    }
191}
192
193impl Body for Link {
194    fn decode_from_reader(r: &mut Reader<'_>) -> Result<Self> {
195        let version = r.take_u8()?;
196        if version != CONFLUX_LINK_VERSION {
197            return Err(Error::InvalidMessage(
198                "Unrecognized CONFLUX_LINK/CONFLUX_LINKED version.".into(),
199            ));
200        }
201
202        let payload = V1LinkPayload::decode_from_reader(r)?;
203
204        Ok(Self { version, payload })
205    }
206
207    fn encode_onto<W: Writer + ?Sized>(self, w: &mut W) -> EncodeResult<()> {
208        w.write(&self.version)?;
209        self.payload.encode_onto(w)?;
210        Ok(())
211    }
212}
213
214impl Body for V1LinkPayload {
215    fn decode_from_reader(r: &mut Reader<'_>) -> Result<Self> {
216        let nonce = r.extract()?;
217        let last_seqno_sent = r.take_u64()?;
218        let last_seqno_recv = r.take_u64()?;
219        let desired_ux = r.take_u8()?.into();
220
221        Ok(V1LinkPayload {
222            nonce,
223            last_seqno_sent,
224            last_seqno_recv,
225            desired_ux,
226        })
227    }
228
229    fn encode_onto<W: Writer + ?Sized>(self, w: &mut W) -> EncodeResult<()> {
230        let V1LinkPayload {
231            nonce,
232            last_seqno_sent,
233            last_seqno_recv,
234            desired_ux,
235        } = self;
236
237        w.write(&nonce)?;
238        w.write_u64(last_seqno_sent);
239        w.write_u64(last_seqno_recv);
240        w.write_u8(desired_ux.into());
241
242        Ok(())
243    }
244}
245
246/// A `CONFLUX_SWITCH` message, sent from a sending endpoint when switching leg
247/// in an already linked circuit construction.
248#[derive(Clone, Debug, Deftly, Getters)]
249#[derive_deftly(HasMemoryCost)]
250pub struct ConfluxSwitch {
251    /// The relative sequence number.
252    #[getter(as_copy)]
253    seqno: u32,
254}
255
256impl ConfluxSwitch {
257    /// Create a new v1 `CONFLUX_SWITCH` message.
258    pub fn new(seqno: u32) -> Self {
259        Self { seqno }
260    }
261}
262
263impl Body for ConfluxSwitch {
264    fn decode_from_reader(r: &mut Reader<'_>) -> Result<Self> {
265        let seqno = r.take_u32()?;
266        Ok(Self { seqno })
267    }
268
269    fn encode_onto<W: Writer + ?Sized>(self, w: &mut W) -> EncodeResult<()> {
270        w.write(&self.seqno)?;
271        Ok(())
272    }
273}
274
275empty_body! {
276    /// A `CONFLUX_LINKED_ACK` message.
277    pub struct ConfluxLinkedAck {}
278}