1
//! Encoding and decoding for relay messages related to conflux.
2

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

            
5
use amplify::Getters;
6
use caret::caret_int;
7
use derive_deftly::Deftly;
8
use rand::{CryptoRng, Rng, RngCore};
9

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

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

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

            
20
/// Helper macro for implementing wrapper types over [`Link`]
21
macro_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)]
40
pub struct ConfluxLink(Link);
41

            
42
impl 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

            
54
impl_link_wrapper!(ConfluxLink);
55

            
56
impl 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)]
69
pub struct ConfluxLinked(Link);
70

            
71
impl 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

            
83
impl_link_wrapper!(ConfluxLinked);
84

            
85
impl 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)]
98
struct 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)]
113
pub struct V1Nonce(CtByteArray<V1_LINK_NONCE_LEN>);
114

            
115
impl V1Nonce {
116
    /// Create a random `V1Nonce` to put in a LINK cell.
117
160
    pub fn new<R: RngCore + CryptoRng>(rng: &mut R) -> V1Nonce {
118
160
        let mut nonce = [0_u8; V1_LINK_NONCE_LEN];
119
160
        rng.fill(&mut nonce[..]);
120
160
        Self(nonce.into())
121
160
    }
122
}
123

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

            
130
impl 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)]
139
pub 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

            
150
impl 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

            
175
caret_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

            
193
impl 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

            
214
impl 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)]
250
pub struct ConfluxSwitch {
251
    /// The relative sequence number.
252
    #[getter(as_copy)]
253
    seqno: u32,
254
}
255

            
256
impl ConfluxSwitch {
257
    /// Create a new v1 `CONFLUX_SWITCH` message.
258
    pub fn new(seqno: u32) -> Self {
259
        Self { seqno }
260
    }
261
}
262

            
263
impl 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

            
275
empty_body! {
276
    /// A `CONFLUX_LINKED_ACK` message.
277
    pub struct ConfluxLinkedAck {}
278
}