tor_cell/relaycell.rs
1//! Implementation for parsing and encoding relay cells
2
3use std::num::NonZeroU16;
4
5use crate::chancell::{BoxedCellBody, CELL_DATA_LEN};
6use derive_deftly::Deftly;
7use smallvec::{smallvec, SmallVec};
8use tor_bytes::{EncodeError, EncodeResult, Error, Result};
9use tor_bytes::{Reader, Writer};
10use tor_error::internal;
11use tor_memquota::derive_deftly_template_HasMemoryCost;
12
13use caret::caret_int;
14use rand::{CryptoRng, Rng};
15
16#[cfg(feature = "conflux")]
17pub mod conflux;
18pub mod extend;
19mod extlist;
20#[cfg(feature = "flowctl-cc")]
21pub mod flow_ctrl;
22#[cfg(feature = "hs")]
23pub mod hs;
24pub mod msg;
25#[cfg(feature = "experimental-udp")]
26pub mod udp;
27
28caret_int! {
29 /// A command that identifies the type of a relay cell
30 #[derive(Deftly)]
31 #[derive_deftly(HasMemoryCost)]
32 pub struct RelayCmd(u8) {
33 /// Start a new stream
34 BEGIN = 1,
35 /// Data on a stream
36 DATA = 2,
37 /// Close a stream
38 END = 3,
39 /// Acknowledge a BEGIN; stream is open
40 CONNECTED = 4,
41 /// Used for flow control
42 SENDME = 5,
43 /// Extend a circuit to a new hop; deprecated
44 EXTEND = 6,
45 /// Reply to EXTEND handshake; deprecated
46 EXTENDED = 7,
47 /// Partially close a circuit
48 TRUNCATE = 8,
49 /// Circuit has been partially closed
50 TRUNCATED = 9,
51 /// Padding cell
52 DROP = 10,
53 /// Start a DNS lookup
54 RESOLVE = 11,
55 /// Reply to a DNS lookup
56 RESOLVED = 12,
57 /// Start a directory stream
58 BEGIN_DIR = 13,
59 /// Extend a circuit to a new hop
60 EXTEND2 = 14,
61 /// Reply to an EXTEND2 cell.
62 EXTENDED2 = 15,
63
64 /// NOTE: UDP command are reserved but only used with experimental-udp feature
65
66 /// UDP: Start of a stream
67 CONNECT_UDP = 16,
68 /// UDP: Acknowledge a CONNECT_UDP. Stream is open.
69 CONNECTED_UDP = 17,
70 /// UDP: Data on a UDP stream.
71 DATAGRAM = 18,
72
73 /// CONFLUX: Link circuits together at the receiving endpoint.
74 CONFLUX_LINK = 19,
75 /// CONFLUX: Confirm that the circuits were linked.
76 CONFLUX_LINKED = 20,
77 /// CONFLUX: Acknowledge the linkage of the circuits, for RTT measurement.
78 CONFLUX_LINKED_ACK = 21,
79 /// CONFLUX: Switch to another leg in an already linked circuit construction.
80 CONFLUX_SWITCH = 22,
81
82 /// HS: establish an introduction point.
83 ESTABLISH_INTRO = 32,
84 /// HS: establish a rendezvous point.
85 ESTABLISH_RENDEZVOUS = 33,
86 /// HS: send introduction (client to introduction point)
87 INTRODUCE1 = 34,
88 /// HS: send introduction (introduction point to service)
89 INTRODUCE2 = 35,
90 /// HS: connect rendezvous point (service to rendezvous point)
91 RENDEZVOUS1 = 36,
92 /// HS: connect rendezvous point (rendezvous point to client)
93 RENDEZVOUS2 = 37,
94 /// HS: Response to ESTABLISH_INTRO
95 INTRO_ESTABLISHED = 38,
96 /// HS: Response to ESTABLISH_RENDEZVOUS
97 RENDEZVOUS_ESTABLISHED = 39,
98 /// HS: Response to INTRODUCE1 from introduction point to client
99 INTRODUCE_ACK = 40,
100
101 /// Padding: declare what kind of padding we want
102 PADDING_NEGOTIATE = 41,
103 /// Padding: reply to a PADDING_NEGOTIATE
104 PADDING_NEGOTIATED = 42,
105
106 /// Flow control: rate update (transmit on with rate limit)
107 XON = 43,
108 /// Flow control: rate update (transmit off)
109 XOFF = 44,
110 }
111}
112
113/// Possible requirements on stream IDs for a relay command.
114enum StreamIdReq {
115 /// Can only be used with a stream ID of 0
116 WantNone,
117 /// Can only be used with a stream ID that isn't 0
118 WantSome,
119 /// Can be used with any stream ID.
120 ///
121 /// This result is impossible with `RelayCellFormat::V1`.
122 Any,
123 /// Unrecognized; might be used with a stream ID or without.
124 Unrecognized,
125}
126
127impl RelayCmd {
128 /// Check whether this command requires a certain kind of
129 /// StreamId in the provided `format`, and return a corresponding StreamIdReq.
130 ///
131 /// If `format` is None, return a result that is correct for _any_ version.
132 fn expects_streamid(self, format: Option<RelayCellFormat>) -> StreamIdReq {
133 match self {
134 RelayCmd::BEGIN
135 | RelayCmd::DATA
136 | RelayCmd::END
137 | RelayCmd::CONNECTED
138 | RelayCmd::RESOLVE
139 | RelayCmd::RESOLVED
140 | RelayCmd::BEGIN_DIR
141 | RelayCmd::XON
142 | RelayCmd::XOFF => StreamIdReq::WantSome,
143 // NOTE: Even when a RelayCmd is not implemented (like these UDP-based commands),
144 // we need to implement expects_streamid() unconditionally.
145 // Otherwise we leak more information than necessary
146 // when parsing RelayCellFormat::V1 cells.
147 RelayCmd::CONNECT_UDP | RelayCmd::CONNECTED_UDP | RelayCmd::DATAGRAM => {
148 StreamIdReq::WantSome
149 }
150 RelayCmd::EXTEND
151 | RelayCmd::EXTENDED
152 | RelayCmd::TRUNCATE
153 | RelayCmd::TRUNCATED
154 | RelayCmd::DROP
155 | RelayCmd::EXTEND2
156 | RelayCmd::EXTENDED2
157 | RelayCmd::CONFLUX_LINK
158 | RelayCmd::CONFLUX_LINKED
159 | RelayCmd::CONFLUX_LINKED_ACK
160 | RelayCmd::CONFLUX_SWITCH
161 | RelayCmd::ESTABLISH_INTRO
162 | RelayCmd::ESTABLISH_RENDEZVOUS
163 | RelayCmd::INTRODUCE1
164 | RelayCmd::INTRODUCE2
165 | RelayCmd::RENDEZVOUS1
166 | RelayCmd::RENDEZVOUS2
167 | RelayCmd::INTRO_ESTABLISHED
168 | RelayCmd::RENDEZVOUS_ESTABLISHED
169 | RelayCmd::INTRODUCE_ACK => StreamIdReq::WantNone,
170 RelayCmd::SENDME => match format {
171 // There are no stream-level SENDMES in V1, since CC is mandatory.
172 // Further, the 'Any' result is not possible with V1, since
173 // we need be able to decide whether a stream ID is present
174 // from the value of the command.
175 Some(RelayCellFormat::V1) => StreamIdReq::WantNone,
176 // In V0, CC is not mandatory, so stream-level SENDMES are possible.
177 Some(RelayCellFormat::V0) => StreamIdReq::Any,
178 // When we're checking for general compatibility, we need to allow V0 or V1.
179 None => StreamIdReq::Any,
180 },
181 _ => StreamIdReq::Unrecognized,
182 }
183 }
184 /// Return true if this command is one that accepts the particular
185 /// stream ID `id`.
186 ///
187 /// Note that this method does not consider the [`RelayCellFormat`] in use:
188 /// it will return "true" for _any_ stream ID if the command is `SENDME`,
189 /// and if the command is unrecognized.
190 pub fn accepts_streamid_val(self, id: Option<StreamId>) -> bool {
191 match self.expects_streamid(None) {
192 StreamIdReq::WantNone => id.is_none(),
193 StreamIdReq::WantSome => id.is_some(),
194 StreamIdReq::Any => true,
195 StreamIdReq::Unrecognized => true,
196 }
197 }
198}
199
200/// Identify a single stream on a circuit.
201///
202/// These identifiers are local to each hop on a circuit.
203/// This can't be zero; if you need something that can be zero in the protocol,
204/// use `Option<StreamId>`.
205#[derive(Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Debug, Hash, Deftly)]
206#[derive_deftly(HasMemoryCost)]
207pub struct StreamId(NonZeroU16);
208
209impl From<NonZeroU16> for StreamId {
210 fn from(id: NonZeroU16) -> Self {
211 Self(id)
212 }
213}
214
215impl From<StreamId> for NonZeroU16 {
216 fn from(id: StreamId) -> NonZeroU16 {
217 id.0
218 }
219}
220
221impl From<StreamId> for u16 {
222 fn from(id: StreamId) -> u16 {
223 id.0.get()
224 }
225}
226
227impl std::fmt::Display for StreamId {
228 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
229 self.0.fmt(f)
230 }
231}
232
233impl StreamId {
234 /// Creates a `StreamId` for non-zero `stream_id`.
235 ///
236 /// Returns `None` when `stream_id` is zero. Messages with a zero/None stream ID
237 /// apply to the circuit as a whole instead of a particular stream.
238 pub fn new(stream_id: u16) -> Option<Self> {
239 NonZeroU16::new(stream_id).map(Self)
240 }
241
242 /// Convenience function to convert to a `u16`; `None` is mapped to 0.
243 pub fn get_or_zero(stream_id: Option<Self>) -> u16 {
244 match stream_id {
245 Some(stream_id) => stream_id.0.get(),
246 None => 0,
247 }
248 }
249}
250
251/// Specifies which encoding version of RelayCell to use.
252#[non_exhaustive]
253#[derive(Copy, Clone, Debug)]
254pub enum RelayCellFormat {
255 /// This is the "legacy" pre-prop340 format. No packing or fragmentation.
256 V0,
257 /// A "transitional" format for use with Counter Galois Onion encryption.
258 ///
259 /// It provides a 16-byte tag field, and a simplified layout for the rest of
260 /// the cell.
261 V1,
262}
263
264/// Internal decoder state.
265#[derive(Clone, Debug)]
266enum RelayCellDecoderInternal {
267 /// Internal state for `RelayCellFormat::V0`
268 V0,
269 /// Internal state for `RelayCellFormat::V1`
270 V1,
271}
272
273// TODO prop340: We should also fuzz RelayCellDecoder, but not in this fuzzer.
274
275/// Decodes a stream of relay cell bodies into `UnparsedRelayMsg`s.
276#[derive(Clone, Debug)]
277pub struct RelayCellDecoder {
278 /// Internal representation.
279 internal: RelayCellDecoderInternal,
280}
281
282impl RelayCellDecoder {
283 /// Returns a new `Decoder`, handling a stream of relay cells
284 /// of the given `version`.
285 pub fn new(version: RelayCellFormat) -> Self {
286 match version {
287 RelayCellFormat::V0 => Self {
288 internal: RelayCellDecoderInternal::V0,
289 },
290 RelayCellFormat::V1 => Self {
291 internal: RelayCellDecoderInternal::V1,
292 },
293 }
294 }
295 /// Parse a RELAY or RELAY_EARLY cell body.
296 ///
297 /// Requires that the cryptographic checks on the message have already been
298 /// performed
299 pub fn decode(&mut self, cell: BoxedCellBody) -> Result<RelayCellDecoderResult> {
300 let msg_internal = match &self.internal {
301 RelayCellDecoderInternal::V0 => UnparsedRelayMsgInternal::V0(cell),
302 RelayCellDecoderInternal::V1 => UnparsedRelayMsgInternal::V1(cell),
303 };
304 Ok(RelayCellDecoderResult {
305 msgs: smallvec![UnparsedRelayMsg {
306 internal: msg_internal
307 }],
308 incomplete: None,
309 })
310 }
311 /// Returns the `IncompleteRelayMsgInfo` describing the partial
312 /// (fragmented) relay message at the end of the so-far-processed relay cell
313 /// stream.
314 pub fn incomplete_info(&self) -> Option<IncompleteRelayMsgInfo> {
315 match &self.internal {
316 // V0 and V1 don't support fragmentation, so there is never a pending fragment.
317 RelayCellDecoderInternal::V0 | RelayCellDecoderInternal::V1 => None,
318 }
319 }
320}
321
322/// Result of calling `RelayCellDecoder::decode`.
323#[derive(Debug)]
324pub struct RelayCellDecoderResult {
325 /// Complete messages obtained by decoding the cell. i.e. messages
326 /// that were completely contained within the cell, or for which the cell
327 /// contained the final fragment.
328 msgs: SmallVec<[UnparsedRelayMsg; 1]>,
329 /// Description of the partial message at the end of the cell, if any.
330 incomplete: Option<IncompleteRelayMsgInfo>,
331}
332
333impl RelayCellDecoderResult {
334 /// Returns a non-empty iterator over commands in relay messages that the
335 /// cell producing this result contained *any* part of. i.e. this includes
336 /// the command of "head", "middle", and/or "tail" message fragments that
337 /// were in the cell.
338 pub fn cmds(&self) -> impl Iterator<Item = RelayCmd> + '_ {
339 let complete_msg_cmds = self.msgs.iter().map(|m| m.cmd());
340 let partial_msg_cmd = self.incomplete.as_ref().map(|c| c.cmd());
341 complete_msg_cmds.chain(partial_msg_cmd)
342 }
343 /// Converts `self` to an iterator over the complete messages, and metadata
344 /// about the trailing incomplete message (as for `Self::incomplete_info`),
345 /// if any.
346 pub fn into_parts(
347 self,
348 ) -> (
349 impl Iterator<Item = UnparsedRelayMsg>,
350 Option<IncompleteRelayMsgInfo>,
351 ) {
352 (self.msgs.into_iter(), self.incomplete)
353 }
354 /// Returns the `IncompleteRelayMsgInfo` describing the incomplete
355 /// relay message that this cell contained a fragment of, if any.
356 ///
357 /// Note that:
358 /// * This does not describe a fragment that includes the end of the relay
359 /// message, since the message is then complete.
360 /// * This *does* include a fragment that continues, but does not complete,
361 /// a message started in an earlier relay cell.
362 /// * There is at most one such incomplete relay message, since no current
363 /// relay cell format supports starting a new message before completing the
364 /// previous one.
365 pub fn incomplete_info(&self) -> Option<IncompleteRelayMsgInfo> {
366 self.incomplete.clone()
367 }
368}
369
370/// Information about a relay message for which we don't yet have the complete body.
371#[derive(Clone, Debug)]
372pub struct IncompleteRelayMsgInfo {
373 /// The message's command.
374 cmd: RelayCmd,
375 /// The message's stream ID, if any.
376 stream_id: Option<StreamId>,
377 /// The total number of bytes in the body of the message.
378 total_msg_len: usize,
379 /// The number of bytes of the body of the message that we've decoded so
380 /// far.
381 num_bytes_present: usize,
382}
383
384impl IncompleteRelayMsgInfo {
385 /// Returns the message's command.
386 pub fn cmd(&self) -> RelayCmd {
387 self.cmd
388 }
389 /// Returns the message's `StreamId`, if any.
390 pub fn stream_id(&self) -> Option<StreamId> {
391 self.stream_id
392 }
393 /// Returns the total size of the complete message body.
394 pub fn total_msg_len(&self) -> usize {
395 self.total_msg_len
396 }
397 /// Returns the number of bytes of the message body that we have so far.
398 pub fn num_bytes_present(&self) -> usize {
399 self.num_bytes_present
400 }
401 /// Returns the number of bytes of the message body that we still need.
402 pub fn num_bytes_missing(&self) -> usize {
403 self.total_msg_len - self.num_bytes_present
404 }
405}
406
407/// Internal representation of an `UnparsedRelayMsg`.
408#[derive(Clone, Debug, Deftly)]
409#[derive_deftly(HasMemoryCost)]
410enum UnparsedRelayMsgInternal {
411 /// For `RelayCellFormat::V0` we can avoid copying data around by just
412 /// storing the original cell body here.
413 // NOTE: we could also have a separate command and stream ID field here, but
414 // we expect to be working with a TON of these, so we will be mildly
415 // over-optimized and just peek into the body.
416 //
417 // It *is* a bit ugly to have to encode so much knowledge about the format in
418 // different functions here, but that information shouldn't leak out of this module.
419 V0(BoxedCellBody),
420
421 /// For `V1` we can also avoid copies, since there is still exactly one
422 /// relay message per cell.
423 V1(BoxedCellBody),
424}
425
426/// An enveloped relay message that has not yet been fully parsed, but where we
427/// have access to the command, stream ID, and payload data length for dispatching
428/// and congestion control purposes.
429#[derive(Clone, Debug, Deftly)]
430#[derive_deftly(HasMemoryCost)]
431pub struct UnparsedRelayMsg {
432 /// The internal representation.
433 internal: UnparsedRelayMsgInternal,
434}
435
436/// Position of the stream ID within the V0 cell body.
437const STREAM_ID_OFFSET_V0: usize = 3;
438
439/// Position of the stream ID within the V1 cell body, if it is present.
440const STREAM_ID_OFFSET_V1: usize = 16 + 1 + 2; // tag, command, length.
441
442/// Position of the payload data length within the V0 cell body.
443const LENGTH_OFFSET_V0: usize = 1 + 2 + 2 + 4; // command, recognized, stream_id, digest.
444
445/// Position of the payload data length within the V1 cell body.
446const LENGTH_OFFSET_V1: usize = 16 + 1; // tag, command.
447
448impl UnparsedRelayMsg {
449 /// Wrap a BoxedCellBody as an UnparsedRelayMsg.
450 ///
451 /// Fails if the body doesn't correspond to exactly one relay message, but
452 /// doesn't parse the message itself.
453 ///
454 /// Non-test code should generally use `RelayCellDecoder` instead.
455 // Ideally we'd make this `#[cfg(test)]`, but then we wouldn't be able
456 // to use it in integration tests.
457 // https://github.com/rust-lang/rust/issues/84629
458 pub fn from_singleton_body(version: RelayCellFormat, body: BoxedCellBody) -> Result<Self> {
459 let mut decoder = RelayCellDecoder::new(version);
460 let res = decoder.decode(body)?;
461 let (mut msgs, incomplete) = res.into_parts();
462 let Some(msg) = msgs.next() else {
463 // There was no complete message in the cell.
464 return Err(Error::MissingData);
465 };
466 if incomplete.is_some() {
467 // There was an incomplete message at the end of the cell.
468 return Err(Error::ExtraneousBytes);
469 }
470 if msgs.next().is_some() {
471 // There was more than one message in the cell.
472 return Err(Error::ExtraneousBytes);
473 }
474 Ok(msg)
475 }
476
477 /// Return the command for this cell.
478 pub fn cmd(&self) -> RelayCmd {
479 match &self.internal {
480 UnparsedRelayMsgInternal::V0(body) => {
481 /// Position of the command within the v0 cell body.
482 const CMD_OFFSET: usize = 0;
483 body[CMD_OFFSET].into()
484 }
485 UnparsedRelayMsgInternal::V1(body) => {
486 /// Position of the command within the v1 body.
487 const CMD_OFFSET: usize = 16;
488 body[CMD_OFFSET].into()
489 }
490 }
491 }
492 /// Return the stream ID for the stream that this msg corresponds to, if any.
493 pub fn stream_id(&self) -> Option<StreamId> {
494 match &self.internal {
495 UnparsedRelayMsgInternal::V0(body) => StreamId::new(u16::from_be_bytes(
496 body[STREAM_ID_OFFSET_V0..STREAM_ID_OFFSET_V0 + 2]
497 .try_into()
498 .expect("two-byte slice was not two bytes long!?"),
499 )),
500 UnparsedRelayMsgInternal::V1(body) => {
501 match self.cmd().expects_streamid(Some(RelayCellFormat::V1)) {
502 StreamIdReq::WantNone => None,
503 StreamIdReq::Unrecognized | StreamIdReq::Any => None,
504 StreamIdReq::WantSome => StreamId::new(u16::from_be_bytes(
505 body[STREAM_ID_OFFSET_V1..STREAM_ID_OFFSET_V1 + 2]
506 .try_into()
507 .expect("two-byte slice was not two bytes long!?"),
508 )),
509 }
510 }
511 }
512 }
513 /// Return the "length" field of the cell.
514 ///
515 /// This is the size of the cell data (the "data" field), not the size of the cell.
516 /// No bounds checking or validation is performed.
517 pub fn data_len(&self) -> u16 {
518 let bytes: [u8; 2] = match &self.internal {
519 UnparsedRelayMsgInternal::V0(body) => &body[LENGTH_OFFSET_V0..LENGTH_OFFSET_V0 + 2],
520 UnparsedRelayMsgInternal::V1(body) => &body[LENGTH_OFFSET_V1..LENGTH_OFFSET_V1 + 2],
521 }
522 .try_into()
523 .expect("two-byte slice was not two bytes long!?");
524 u16::from_be_bytes(bytes)
525 }
526 /// Decode this unparsed cell into a given cell type.
527 pub fn decode<M: RelayMsg>(self) -> Result<RelayMsgOuter<M>> {
528 match self.internal {
529 UnparsedRelayMsgInternal::V0(body) => {
530 let mut reader = Reader::from_slice(body.as_ref());
531 RelayMsgOuter::decode_v0_from_reader(&mut reader)
532 }
533 UnparsedRelayMsgInternal::V1(body) => {
534 let mut reader = Reader::from_slice(body.as_ref());
535 RelayMsgOuter::decode_v1_from_reader(&mut reader)
536 }
537 }
538 }
539}
540
541/// A decoded and parsed relay message of unrestricted type,
542/// with an accompanying optional Stream ID.
543pub type AnyRelayMsgOuter = RelayMsgOuter<msg::AnyRelayMsg>;
544
545/// A deprecated name for AnyRelayMsgOuter.
546#[deprecated(note = "Use AnyRelayMsgOuter instead.")]
547pub type AnyRelayCell = AnyRelayMsgOuter;
548
549/// Trait implemented by anything that can serve as a relay message.
550///
551/// Typically, this will be [`RelayMsg`] (to represent an unrestricted relay
552/// message), or a restricted subset of `RelayMsg`.
553pub trait RelayMsg {
554 /// Return the stream command associated with this message.
555 fn cmd(&self) -> RelayCmd;
556 /// Encode the body of this message, not including command or length
557 fn encode_onto<W: tor_bytes::Writer + ?Sized>(self, w: &mut W) -> tor_bytes::EncodeResult<()>;
558 /// Extract the body of a message with command `cmd` from reader `r`.
559 fn decode_from_reader(cmd: RelayCmd, r: &mut Reader<'_>) -> Result<Self>
560 where
561 Self: Sized;
562}
563
564/// A decoded and parsed relay message, along with an optional Stream ID.
565///
566/// This type represents a message that can be sent along a
567/// circuit, along with the ID for an associated stream that the
568/// message is meant for.
569///
570/// NOTE: This name is a placeholder; we intend to replace it once we have
571/// standardized our vocabulary in this area.
572#[derive(Debug)]
573pub struct RelayMsgOuter<M> {
574 /// The stream ID for the stream that this cell corresponds to.
575 streamid: Option<StreamId>,
576 /// The relay message for this cell.
577 msg: M,
578}
579
580/// A deprecated name for RelayMsgOuter.
581#[deprecated(note = "Use RelayMsgOuter instead.")]
582pub type RelayCell<M> = RelayMsgOuter<M>;
583
584impl<M: RelayMsg> RelayMsgOuter<M> {
585 /// Construct a new relay cell.
586 pub fn new(streamid: Option<StreamId>, msg: M) -> Self {
587 RelayMsgOuter { streamid, msg }
588 }
589 /// Consume this cell and return its components.
590 pub fn into_streamid_and_msg(self) -> (Option<StreamId>, M) {
591 (self.streamid, self.msg)
592 }
593 /// Return the command for this cell.
594 pub fn cmd(&self) -> RelayCmd {
595 self.msg.cmd()
596 }
597 /// Return the stream ID for the stream that this cell corresponds to.
598 pub fn stream_id(&self) -> Option<StreamId> {
599 self.streamid
600 }
601 /// Return the underlying message for this cell.
602 pub fn msg(&self) -> &M {
603 &self.msg
604 }
605 /// Consume this cell and return the underlying message.
606 pub fn into_msg(self) -> M {
607 self.msg
608 }
609 /// Consume this relay message and encode it as a 509-byte padded cell
610 /// body.
611 //
612 // TODO prop340: This API won't work for packed or fragmented messages.
613 pub fn encode<R: Rng + CryptoRng>(
614 self,
615 format: RelayCellFormat,
616 rng: &mut R,
617 ) -> crate::Result<BoxedCellBody> {
618 /// We skip this much space before adding any random padding to the
619 /// end of the cell
620 const MIN_SPACE_BEFORE_PADDING: usize = 4;
621
622 let (mut body, enc_len) = match format {
623 RelayCellFormat::V0 => self.encode_to_cell_v0()?,
624 RelayCellFormat::V1 => self.encode_to_cell_v1()?,
625 };
626 debug_assert!(enc_len <= CELL_DATA_LEN);
627 if enc_len < CELL_DATA_LEN - MIN_SPACE_BEFORE_PADDING {
628 rng.fill_bytes(&mut body[enc_len + MIN_SPACE_BEFORE_PADDING..]);
629 }
630
631 Ok(body)
632 }
633
634 /// Consume a relay cell and return its contents, encoded for use
635 /// in a RELAY or RELAY_EARLY cell.
636 fn encode_to_cell_v0(self) -> EncodeResult<(BoxedCellBody, usize)> {
637 // NOTE: This implementation is a bit optimized, since it happens to
638 // literally every relay cell that we produce.
639
640 // TODO -NM: Add a specialized implementation for making a DATA cell from
641 // a body?
642
643 /// The position of the length field within a relay cell.
644 const LEN_POS: usize = 9;
645 /// The position of the body a relay cell.
646 const BODY_POS: usize = 11;
647
648 let body = BodyWrapper(Box::new([0_u8; 509]));
649
650 let mut w = crate::slicewriter::SliceWriter::new(body);
651 w.write_u8(self.msg.cmd().into());
652 w.write_u16(0); // "Recognized"
653 w.assert_offset_is(STREAM_ID_OFFSET_V0);
654 w.write_u16(StreamId::get_or_zero(self.streamid));
655 w.write_u32(0); // Digest
656 // (It would be simpler to use NestedWriter at this point, but it uses an internal Vec that we are trying to avoid.)
657 w.assert_offset_is(LEN_POS);
658 w.write_u16(0); // Length.
659 w.assert_offset_is(BODY_POS);
660 self.msg.encode_onto(&mut w)?; // body
661 let (mut body, written) = w.try_unwrap().map_err(|_| {
662 EncodeError::Bug(internal!(
663 "Encoding of relay message was too long to fit into a cell!"
664 ))
665 })?;
666 let payload_len = written - BODY_POS;
667 debug_assert!(payload_len < u16::MAX as usize);
668 *(<&mut [u8; 2]>::try_from(&mut body.0[LEN_POS..LEN_POS + 2])
669 .expect("Two-byte slice was not two bytes long!?")) =
670 (payload_len as u16).to_be_bytes();
671 Ok((body.0, written))
672 }
673
674 /// Consume a relay cell and return its contents, encoded for use
675 /// in a RELAY or RELAY_EARLY cell.
676 fn encode_to_cell_v1(self) -> EncodeResult<(BoxedCellBody, usize)> {
677 // NOTE: This implementation is a bit optimized, since it happens to
678 // literally every relay cell that we produce.
679 // TODO -NM: Add a specialized implementation for making a DATA cell from
680 // a body?
681
682 /// Position of the length field within the cell.
683 const LEN_POS_V1: usize = 16 + 1; // Skipping tag, command.
684
685 let cmd = self.msg.cmd();
686 let body = BodyWrapper(Box::new([0_u8; 509]));
687 let mut w = crate::slicewriter::SliceWriter::new(body);
688 w.advance(16); // Tag: 16 bytes
689 w.write_u8(cmd.get()); // Command: 1 byte.
690 w.assert_offset_is(LEN_POS_V1);
691 w.advance(2); // Length: 2 bytes.
692 let mut body_pos = 16 + 1 + 2;
693 match (
694 cmd.expects_streamid(Some(RelayCellFormat::V1)),
695 self.streamid,
696 ) {
697 (StreamIdReq::WantNone, None) => {}
698 (StreamIdReq::WantSome, Some(id)) => {
699 w.write_u16(id.into());
700 body_pos += 2;
701 }
702 (_, id) => {
703 return Err(EncodeError::Bug(internal!(
704 "Tried to encode invalid stream ID {id:?} for {cmd}"
705 )))
706 }
707 }
708 w.assert_offset_is(body_pos);
709
710 self.msg.encode_onto(&mut w)?; // body
711 let (mut body, written) = w.try_unwrap().map_err(|_| {
712 EncodeError::Bug(internal!(
713 "Encoding of relay message was too long to fit into a cell!"
714 ))
715 })?;
716 let payload_len = written - body_pos;
717 debug_assert!(payload_len < u16::MAX as usize);
718 *(<&mut [u8; 2]>::try_from(&mut body.0[LEN_POS_V1..LEN_POS_V1 + 2])
719 .expect("Two-byte slice was not two bytes long!?")) =
720 (payload_len as u16).to_be_bytes();
721 Ok((body.0, written))
722 }
723
724 /// Parse a RELAY or RELAY_EARLY cell body into a RelayMsgOuter.
725 /// Requires that the cryptographic checks on the message have already been
726 /// performed
727 ///
728 /// Fails if the cell doesn't contain exactly one message.
729 ///
730 /// Non-test code should generally use `RelayCellDecoder` instead.
731 // Ideally we'd make this `#[cfg(test)]`, but then we wouldn't be able
732 // to use it in integration tests.
733 // https://github.com/rust-lang/rust/issues/84629
734 #[allow(clippy::needless_pass_by_value)] // TODO this will go away soon.
735 pub fn decode_singleton(version: RelayCellFormat, body: BoxedCellBody) -> Result<Self> {
736 let unparsed_msg = UnparsedRelayMsg::from_singleton_body(version, body)?;
737 unparsed_msg.decode()
738 }
739 /// Parse a `RelayCellFormat::V0` RELAY or RELAY_EARLY cell body into a
740 /// RelayMsgOuter from a reader.
741 ///
742 /// Requires that the cryptographic checks on the message have already been
743 /// performed
744 fn decode_v0_from_reader(r: &mut Reader<'_>) -> Result<Self> {
745 let cmd = r.take_u8()?.into();
746 r.advance(2)?; // "recognized"
747 let streamid = StreamId::new(r.take_u16()?);
748 r.advance(4)?; // digest
749 let len = r.take_u16()? as usize;
750 if r.remaining() < len {
751 return Err(Error::InvalidMessage(
752 "Insufficient data in relay cell".into(),
753 ));
754 }
755 r.truncate(len);
756 let msg = M::decode_from_reader(cmd, r)?;
757 Ok(Self { streamid, msg })
758 }
759
760 /// Parse a `RelayCellFormat::V1` RELAY or RELAY_EARLY cell body into a
761 /// RelayMsgOuter from a reader.
762 ///
763 /// Requires that the cryptographic checks on the message have already been
764 /// performed.
765 fn decode_v1_from_reader(r: &mut Reader<'_>) -> Result<Self> {
766 r.advance(16)?; // Tag
767 let cmd: RelayCmd = r.take_u8()?.into();
768 let len = r.take_u16()?.into();
769 let streamid = match cmd.expects_streamid(Some(RelayCellFormat::V1)) {
770 // If no stream ID is expected, then the body begins immediately.
771 StreamIdReq::WantNone => None,
772 // In this case, a stream ID _is_ expected.
773 //
774 // (If it happens to be zero, we will reject the message,
775 // since zero is never a stream ID.)
776 StreamIdReq::WantSome => Some(StreamId::new(r.take_u16()?).ok_or_else(|| {
777 Error::InvalidMessage(
778 format!("Zero-valued stream ID with relay command {cmd}").into(),
779 )
780 })?),
781 // We treat an unrecognized command as having no stream ID.
782 //
783 // (Note: This command is truly unrecognized, and not one that we could parse
784 // differently under other circumstances.)
785 //
786 // Note that this enables a destructive fingerprinting opportunity,
787 // where an attacker can learn whether we have a version of Arti that recognizes this
788 // command, at the expense of our killing this circuit immediately if they are wrong.
789 // This is not a very bad attack.
790 //
791 // Note that StreamIdReq::Any should be impossible here, since we're using the V1
792 // format.
793 StreamIdReq::Unrecognized | StreamIdReq::Any => {
794 return Err(Error::InvalidMessage(
795 format!("Unrecognized relay command {cmd}").into(),
796 ))
797 }
798 };
799 if r.remaining() < len {
800 //
801 return Err(Error::InvalidMessage(
802 "Insufficient data in relay cell".into(),
803 ));
804 }
805 r.truncate(len);
806 let msg = M::decode_from_reader(cmd, r)?;
807 Ok(Self { streamid, msg })
808 }
809}
810
811/// Wrap a BoxedCellBody and implement AsMut<[u8]>, so we can use it with `SliceWriter`.
812struct BodyWrapper(BoxedCellBody);
813impl AsMut<[u8]> for BodyWrapper {
814 fn as_mut(&mut self) -> &mut [u8] {
815 self.0.as_mut()
816 }
817}