rings_node/onion/circuit/
codec.rs1use bytes::Bytes;
2use rings_core::dht::Did;
3use serde::Deserialize;
4use serde::Serialize;
5
6use super::cell::OnionCellBucket;
7use super::cell::OnionWireCell;
8use super::OnionBackwardFrame;
9use super::OnionCircuitId;
10use super::OnionForwardFrame;
11use super::OnionForwardLayer;
12use crate::error::Error;
13use crate::error::Result;
14use crate::extension::ext::Reject;
15use crate::extension::ext::Wire;
16
17#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq)]
18pub(super) enum OnionWireMessage {
19 Forward(OnionForwardFrame),
20 Backward(OnionBackwardFrame),
21 Cover,
23}
24
25#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq)]
26pub(super) enum OnionLocalMessage {
27 CellReady {
28 from: Did,
29 received_at_ms: u128,
30 bucket: OnionCellBucket,
31 message: OnionWireMessage,
32 },
33 ForwardReady {
34 from: Did,
35 received_at_ms: u128,
36 bucket: OnionCellBucket,
37 circuit_id: OnionCircuitId,
38 layer: OnionForwardLayer,
39 },
40}
41
42#[derive(Clone, Debug, Eq, PartialEq)]
43pub(super) enum OnionCircuitInput {
44 CellObserved {
45 from: Did,
46 bucket: OnionCellBucket,
47 sealed: rings_core::ecc::elgamal::impls::secp256k1::AeadCiphertext,
48 },
49 CellReady {
50 from: Did,
51 received_at_ms: u128,
52 bucket: OnionCellBucket,
53 message: OnionWireMessage,
54 },
55 ForwardReady {
56 from: Did,
57 received_at_ms: u128,
58 bucket: OnionCellBucket,
59 circuit_id: OnionCircuitId,
60 layer: OnionForwardLayer,
61 },
62}
63
64#[derive(Clone, Debug, Eq, PartialEq)]
66pub struct OnionCircuitEvent {
67 pub(super) input: OnionCircuitInput,
68}
69
70pub(super) fn decode_event(wire: Wire<'_>) -> std::result::Result<OnionCircuitEvent, Reject> {
71 if wire.from == wire.me {
72 decode_local_message(wire.payload)
73 } else {
74 decode_wire_message(wire.from, wire.payload)
75 }
76}
77
78fn decode_wire_message(
79 from: Did,
80 payload: &[u8],
81) -> std::result::Result<OnionCircuitEvent, Reject> {
82 const MAX_SERIALIZED_CELL_OVERHEAD: usize = 4 * 1024;
83 const AEAD_TAG_BYTES: usize = 16;
84 let max_wire_len = OnionCellBucket::MiB12
85 .plaintext_len()
86 .saturating_add(MAX_SERIALIZED_CELL_OVERHEAD);
87 if payload.len() > max_wire_len {
88 return Err(Reject(
89 "encrypted onion cell exceeds wire bound".to_string(),
90 ));
91 }
92 let cell = rings_codec::deserialize::<OnionWireCell>(payload)
93 .map_err(|error| Reject(format!("bad encrypted onion cell: {error}")))?;
94 let expected_ciphertext_len = cell
95 .bucket
96 .plaintext_len()
97 .checked_add(AEAD_TAG_BYTES)
98 .ok_or_else(|| Reject("encrypted onion cell length overflow".to_string()))?;
99 if cell.sealed.ciphertext.len() != expected_ciphertext_len {
100 return Err(Reject(
101 "encrypted onion cell does not match its size class".to_string(),
102 ));
103 }
104 Ok(OnionCircuitEvent {
105 input: OnionCircuitInput::CellObserved {
106 from,
107 bucket: cell.bucket,
108 sealed: cell.sealed,
109 },
110 })
111}
112
113fn decode_local_message(payload: &[u8]) -> std::result::Result<OnionCircuitEvent, Reject> {
114 let message = rings_codec::deserialize::<OnionLocalMessage>(payload)
115 .map_err(|error| Reject(format!("bad local onion circuit message: {error}")))?;
116 let input = match message {
117 OnionLocalMessage::CellReady {
118 from,
119 received_at_ms,
120 bucket,
121 message,
122 } => OnionCircuitInput::CellReady {
123 from,
124 received_at_ms,
125 bucket,
126 message,
127 },
128 OnionLocalMessage::ForwardReady {
129 from,
130 received_at_ms,
131 bucket,
132 circuit_id,
133 layer,
134 } => OnionCircuitInput::ForwardReady {
135 from,
136 received_at_ms,
137 bucket,
138 circuit_id,
139 layer,
140 },
141 };
142 Ok(OnionCircuitEvent { input })
143}
144
145pub(super) fn encode_local_message(message: OnionLocalMessage) -> Result<Bytes> {
146 rings_codec::serialize(&message)
147 .map(Bytes::from)
148 .map_err(|_| Error::EncodeError)
149}