rings_node/onion/circuit/
mod.rs1mod cell;
9mod codec;
10mod crypto;
11mod limiter;
12mod protocol;
13mod reducer;
14mod send_outbox;
15mod shell;
16
17#[cfg(test)]
18mod tests;
19
20use bytes::Bytes;
21pub use cell::OnionCellBucket;
22pub use codec::OnionCircuitEvent;
23pub use crypto::encode_initial_forward;
24#[cfg(rings_browser)]
25pub(crate) use crypto::encode_initial_forward_link;
26pub use crypto::route_first_hop;
27pub(crate) use crypto::send_backward;
28#[cfg(rings_native)]
29pub(crate) use crypto::OnionCircuitPath;
30pub use protocol::OnionCircuitCapabilities;
31pub use protocol::OnionCircuitProtocol;
32pub use reducer::OnionCircuitEffect;
33pub use reducer::OnionCircuitState;
34use rings_core::dht::Did;
35use rings_core::ecc::elgamal::impls::secp256k1::AeadCiphertext;
36use rings_core::ecc::PublicKey;
37use rings_core::message::MessageVerification;
38pub(crate) use send_outbox::OnionLinkSender;
39use serde::Deserialize;
40use serde::Serialize;
41pub use shell::OnionCircuitExitFrame;
42pub use shell::OnionCircuitHandler;
43pub use shell::OnionCircuitShell;
44
45use super::OnionServiceName;
46use crate::error::Result;
47
48#[derive(Clone, Copy, Debug, Eq, PartialEq)]
50pub(crate) struct OnionLink {
51 peer: Did,
52 recipient: PublicKey<33>,
53}
54
55impl OnionLink {
56 const fn new(peer: Did, recipient: PublicKey<33>) -> Self {
57 Self { peer, recipient }
58 }
59}
60
61pub const ONION_CIRCUIT_NAMESPACE: &str = "onion-circuit";
63
64#[derive(Clone, Copy, Debug, Eq, PartialEq)]
66pub enum OnionCircuitSecurity {
67 LayeredAead,
69}
70
71pub const ONION_CIRCUIT_SECURITY: OnionCircuitSecurity = OnionCircuitSecurity::LayeredAead;
73
74pub const MAX_ONION_CIRCUIT_HOPS: u8 = 8;
77
78pub(super) const MAX_ONION_RELAY_CIRCUITS: usize = 1024;
79pub(super) const ONION_RELAY_RETURN_TTL_MS: u128 = 120_000;
80pub(super) const ONION_FORWARD_PAYLOAD_TTL_MS: u128 = 120_000;
81pub(super) const ONION_FORWARD_EXPIRY_QUANTUM_MS: u128 = 30_000;
82pub(super) const ONION_FORWARD_MAX_VALIDITY_MS: u128 =
87 ONION_FORWARD_PAYLOAD_TTL_MS + ONION_FORWARD_EXPIRY_QUANTUM_MS;
88pub(super) const ONION_CRYPTO_LIMIT_WINDOW_MS: u128 = 60_000;
89pub(super) const MAX_ONION_CRYPTO_OPS_PER_WINDOW: u32 = 4096;
90pub(super) const MAX_ONION_CRYPTO_OPS_GLOBAL_PER_WINDOW: u32 = 8192;
91pub(super) const MAX_ONION_CRYPTO_BYTES_PER_WINDOW: u64 = 256 * 1024 * 1024;
92pub(super) const MAX_ONION_CRYPTO_BYTES_GLOBAL_PER_WINDOW: u64 = 512 * 1024 * 1024;
93pub(super) const MAX_ONION_CRYPTO_PEERS: usize = 64;
94pub(super) const ONION_AEAD_NAMESPACE: &str = "rings-node:onion-circuit:v1";
95
96#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq)]
101pub struct OnionCircuitPayload {
102 pub service: OnionServiceName,
104 pub body: Bytes,
106}
107
108impl OnionCircuitPayload {
109 pub fn new(service: OnionServiceName, body: impl Into<Bytes>) -> Self {
111 Self {
112 service,
113 body: body.into(),
114 }
115 }
116
117 pub fn try_new(service: impl AsRef<str>, body: impl Into<Bytes>) -> Result<Self> {
119 Ok(Self::new(OnionServiceName::parse(service)?, body))
120 }
121
122 pub fn service(&self) -> &str {
124 self.service.as_str()
125 }
126
127 pub fn service_name(&self) -> &OnionServiceName {
129 &self.service
130 }
131
132 pub fn is_service(&self, service: &OnionServiceName) -> bool {
134 &self.service == service
135 }
136
137 pub fn matches_service(&self, service: &str) -> bool {
139 self.service.matches(service)
140 }
141}
142
143#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq)]
145pub struct OnionAuthenticatedPayload {
146 pub return_id: OnionReturnId,
148 pub nonce: OnionBackwardNonce,
150 pub sequence: OnionBackwardSequence,
152 pub authentication: MessageVerification,
154 pub payload: OnionCircuitPayload,
156}
157
158#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
164pub struct OnionReturnId([u8; 16]);
165
166impl OnionReturnId {
167 pub const fn new(bytes: [u8; 16]) -> Self {
169 Self(bytes)
170 }
171
172 pub fn random() -> Self {
174 Self(rand::random())
175 }
176}
177
178#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
180pub struct OnionBackwardNonce([u8; 16]);
181
182impl OnionBackwardNonce {
183 pub const fn new(bytes: [u8; 16]) -> Self {
185 Self(bytes)
186 }
187
188 pub fn random() -> Self {
190 Self(rand::random())
191 }
192}
193
194#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
196pub struct OnionBackwardSequence(u64);
197
198impl OnionBackwardSequence {
199 pub const FIRST: Self = Self(0);
201
202 pub const fn new(value: u64) -> Self {
204 Self(value)
205 }
206
207 pub const fn value(self) -> u64 {
209 self.0
210 }
211}
212
213#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
215pub struct OnionForwardNonce([u8; 16]);
216
217impl OnionForwardNonce {
218 pub const fn new(bytes: [u8; 16]) -> Self {
220 Self(bytes)
221 }
222
223 pub fn random() -> Self {
225 Self(rand::random())
226 }
227}
228
229#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
231pub struct OnionForwardSequence(u64);
232
233impl OnionForwardSequence {
234 pub const FIRST: Self = Self(0);
236
237 pub const fn new(value: u64) -> Self {
239 Self(value)
240 }
241
242 pub const fn value(self) -> u64 {
244 self.0
245 }
246}
247
248#[derive(Clone, Debug, Eq, PartialEq)]
250pub struct OnionVerifiedPayload {
251 pub return_id: OnionReturnId,
253 pub nonce: OnionBackwardNonce,
255 pub sequence: OnionBackwardSequence,
257 pub payload: OnionCircuitPayload,
259}
260
261#[derive(Clone, Copy, Debug, Deserialize, Serialize, Eq, PartialEq)]
263pub struct OnionClientReturn {
264 pub session_public_key: PublicKey<33>,
266 pub return_id: OnionReturnId,
268}
269
270impl OnionClientReturn {
271 pub fn new(session_public_key: PublicKey<33>) -> Self {
273 Self {
274 session_public_key,
275 return_id: OnionReturnId::random(),
276 }
277 }
278}
279
280#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
286pub struct OnionCircuitId([u8; 16]);
287
288impl OnionCircuitId {
289 pub const fn new(bytes: [u8; 16]) -> Self {
291 Self(bytes)
292 }
293
294 pub fn random() -> Self {
296 Self(rand::random())
297 }
298}
299
300#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq)]
302pub struct OnionForwardFrame {
303 pub circuit_id: OnionCircuitId,
305 pub layer: AeadCiphertext,
307}
308
309#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq)]
311pub struct OnionBackwardFrame {
312 pub circuit_id: OnionCircuitId,
314 pub payload: AeadCiphertext,
316}
317
318#[derive(Clone, Copy, Debug, Eq, PartialEq)]
320pub struct OnionBackwardPath {
321 pub circuit_id: OnionCircuitId,
323 pub return_peer: Did,
325 pub return_session_public_key: PublicKey<33>,
327 pub client: OnionClientReturn,
329}
330
331impl OnionBackwardPath {
332 pub const fn new(
334 circuit_id: OnionCircuitId,
335 return_peer: Did,
336 return_session_public_key: PublicKey<33>,
337 client: OnionClientReturn,
338 ) -> Self {
339 Self {
340 circuit_id,
341 return_peer,
342 return_session_public_key,
343 client,
344 }
345 }
346}
347
348#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq)]
349pub(super) enum OnionForwardLayer {
350 Relay {
351 next_hop: Did,
352 next_circuit_id: OnionCircuitId,
353 next_session_public_key: PublicKey<33>,
354 return_session_public_key: PublicKey<33>,
355 inner: AeadCiphertext,
356 },
357 Exit {
358 client: OnionClientReturn,
359 return_session_public_key: PublicKey<33>,
360 expires_at_ms: u128,
361 forward_nonce: OnionForwardNonce,
362 forward_sequence: OnionForwardSequence,
363 payload: OnionCircuitPayload,
364 },
365}