1use crate::encoding::STANDARD;
6use ed25519_dalek::{Signature, Signer, SigningKey, Verifier, VerifyingKey};
7use serde::{Deserialize, Serialize};
8use serde_json::Value;
9use sha2::{Digest, Sha256};
10
11use crate::canonicalize;
12use crate::expiration::{is_within_window, Window};
13
14#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
15pub struct RelayAuthority {
16 pub relay_authority_version: String,
17 pub relay: String,
18 pub trust_domain: String,
19 pub kinds: Vec<String>,
20 #[serde(skip_serializing_if = "Option::is_none", default)]
21 pub max_hop_count: Option<u32>,
22 #[serde(skip_serializing_if = "Option::is_none", default)]
23 pub rate_limit_per_minute: Option<u32>,
24 pub valid_from: String,
25 #[serde(skip_serializing_if = "Option::is_none", default)]
26 pub valid_until: Option<String>,
27 pub issuer: String,
28 #[serde(skip_serializing_if = "Option::is_none", default)]
29 pub constraints: Option<Vec<Value>>,
30 pub signature: SignatureEnvelope,
31}
32
33#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
34pub struct SignatureEnvelope {
35 pub algorithm: String,
36 pub signer: String,
37 pub signature: String,
38}
39
40#[derive(Clone, Debug, Default)]
41pub struct RelayFrame {
42 pub ciphertext: Vec<u8>,
43 pub destination: String,
44 pub priority: Option<String>,
45 pub hop_count: u32,
46 pub expires_at: Option<String>,
47 pub source: Option<String>,
48}
49
50#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
51pub struct RelayForwardedEvent {
52 #[serde(rename = "type")]
53 pub kind: String,
54 pub relay: String,
55 pub destination: String,
56 #[serde(skip_serializing_if = "Option::is_none", default)]
57 pub source: Option<String>,
58 pub hop_count_in: u32,
59 pub hop_count_out: u32,
60 pub size_bytes: usize,
61 pub forwarded_at: String,
62 #[serde(skip_serializing_if = "Option::is_none", default)]
63 pub authority_id: Option<String>,
64 #[serde(skip_serializing_if = "Option::is_none", default)]
65 pub priority: Option<String>,
66}
67
68#[derive(Debug, thiserror::Error)]
69pub enum RelayPolicyError {
70 #[error("relay authority invalid: {0}")]
71 Authority(String),
72 #[error("authority window: {0}")]
73 Window(String),
74 #[error("frame expired: {0}")]
75 Expired(String),
76 #[error("hop count: {0}")]
77 HopCap(String),
78 #[error("rate limit: {0}")]
79 Rate(String),
80}
81
82pub struct RelayHandler {
83 authority: RelayAuthority,
84 issuer_pub: [u8; 32],
85 validated: std::cell::Cell<bool>,
86 rate_bucket_minute: std::cell::Cell<i64>,
87 rate_bucket_count: std::cell::Cell<u32>,
88}
89
90impl RelayHandler {
91 pub fn new(authority: RelayAuthority, issuer_public_key: [u8; 32]) -> Self {
92 RelayHandler {
93 authority,
94 issuer_pub: issuer_public_key,
95 validated: std::cell::Cell::new(false),
96 rate_bucket_minute: std::cell::Cell::new(-1),
97 rate_bucket_count: std::cell::Cell::new(0),
98 }
99 }
100
101 pub fn authority(&self) -> &RelayAuthority {
102 &self.authority
103 }
104
105 pub fn forward(
106 &self,
107 frame: &RelayFrame,
108 now: &str,
109 ) -> Result<(RelayFrame, RelayForwardedEvent), RelayPolicyError> {
110 if !self.validated.get() {
111 let v = verify_relay_authority(&self.authority, &self.issuer_pub);
112 if !v.ok {
113 return Err(RelayPolicyError::Authority(
114 v.reason.unwrap_or_else(|| "unknown".into()),
115 ));
116 }
117 self.validated.set(true);
118 }
119 let window = Window {
120 valid_from: Some(self.authority.valid_from.as_str()),
121 valid_until: self.authority.valid_until.as_deref(),
122 ..Window::default()
123 };
124 if !is_within_window(&window, now) {
125 return Err(RelayPolicyError::Window(
126 "outside valid_from/valid_until".into(),
127 ));
128 }
129 if let Some(exp) = &frame.expires_at {
130 if exp.as_str() < now {
131 return Err(RelayPolicyError::Expired(format!(
132 "frame expired at {}",
133 exp
134 )));
135 }
136 }
137 if let Some(max) = self.authority.max_hop_count {
138 if frame.hop_count >= max {
139 return Err(RelayPolicyError::HopCap(format!(
140 "hop count {} >= max {}",
141 frame.hop_count, max
142 )));
143 }
144 }
145 if let Some(limit) = self.authority.rate_limit_per_minute {
146 let minute = parse_minute(now);
147 if minute != self.rate_bucket_minute.get() {
148 self.rate_bucket_minute.set(minute);
149 self.rate_bucket_count.set(0);
150 }
151 self.rate_bucket_count.set(self.rate_bucket_count.get() + 1);
152 if self.rate_bucket_count.get() > limit {
153 return Err(RelayPolicyError::Rate(format!(
154 "rate limit {}/min exceeded",
155 limit
156 )));
157 }
158 }
159 let mut outgoing = frame.clone();
160 outgoing.hop_count = frame.hop_count + 1;
161 let event = RelayForwardedEvent {
162 kind: "relay.forwarded".into(),
163 relay: self.authority.relay.clone(),
164 destination: frame.destination.clone(),
165 source: frame.source.clone(),
166 hop_count_in: frame.hop_count,
167 hop_count_out: outgoing.hop_count,
168 size_bytes: frame.ciphertext.len(),
169 forwarded_at: now.to_string(),
170 authority_id: Some(self.authority.relay.clone()),
171 priority: frame.priority.clone(),
172 };
173 Ok((outgoing, event))
174 }
175}
176
177fn parse_minute(now: &str) -> i64 {
178 let len = now.len().min(19);
182 if !now.is_char_boundary(len) || len < 19 {
183 return 0;
184 }
185 let trimmed = &now[..19];
186 let unix = parse_iso8601(trimmed).unwrap_or(0);
187 unix / 60
188}
189
190fn parse_iso8601(s: &str) -> Option<i64> {
191 if s.len() < 19 {
192 return None;
193 }
194 let year: i64 = s.get(0..4)?.parse().ok()?;
195 let month: u32 = s.get(5..7)?.parse().ok()?;
196 let day: u32 = s.get(8..10)?.parse().ok()?;
197 let hour: u32 = s.get(11..13)?.parse().ok()?;
198 let minute: u32 = s.get(14..16)?.parse().ok()?;
199 let second: u32 = s.get(17..19)?.parse().ok()?;
200 let y = if month <= 2 { year - 1 } else { year };
201 let era = if y >= 0 { y } else { y - 399 } / 400;
202 let yoe = (y - era * 400) as u64;
203 let m = if month > 2 { month - 3 } else { month + 9 };
204 let doy = (153 * m as u64 + 2) / 5 + day as u64 - 1;
205 let doe = yoe * 365 + yoe / 4 - yoe / 100 + doy;
206 let days = era * 146_097 + doe as i64 - 719_468;
207 Some(days * 86_400 + (hour as i64) * 3600 + (minute as i64) * 60 + second as i64)
208}
209
210pub fn relay_authority_signing_bytes(a: &RelayAuthority) -> [u8; 32] {
211 let mut value = serde_json::to_value(a).unwrap_or(Value::Null);
212 if let Value::Object(map) = &mut value {
213 map.remove("signature");
214 }
215 let canonical = canonicalize(&value).unwrap_or_default();
216 Sha256::digest(canonical.as_bytes()).into()
217}
218
219pub fn sign_relay_authority(
220 mut authority: RelayAuthority,
221 private_key: &[u8; 32],
222) -> RelayAuthority {
223 authority.signature = SignatureEnvelope {
224 algorithm: "ed25519".into(),
225 signer: authority.issuer.clone(),
226 signature: String::new(),
227 };
228 let digest = relay_authority_signing_bytes(&authority);
229 let signing = SigningKey::from_bytes(private_key);
230 let sig: Signature = signing.sign(&digest);
231 authority.signature.signature = STANDARD.encode(sig.to_bytes());
232 authority
233}
234
235#[derive(Debug)]
236pub struct VerifyRelayAuthorityResult {
237 pub ok: bool,
238 pub reason: Option<String>,
239}
240
241pub fn verify_relay_authority(
242 authority: &RelayAuthority,
243 issuer_public_key: &[u8; 32],
244) -> VerifyRelayAuthorityResult {
245 let rejected = |r: &str| VerifyRelayAuthorityResult {
246 ok: false,
247 reason: Some(r.to_string()),
248 };
249 if authority.relay_authority_version != "1" {
250 return rejected(&format!(
251 "unsupported version {}",
252 authority.relay_authority_version
253 ));
254 }
255 if authority.signature.algorithm != "ed25519" {
256 return rejected(&format!(
257 "unsupported signature algorithm {}",
258 authority.signature.algorithm
259 ));
260 }
261 if authority.signature.signer != authority.issuer {
262 return rejected("signature signer does not match authority issuer");
263 }
264 let digest = relay_authority_signing_bytes(authority);
265 let sig_bytes = match STANDARD.decode(&authority.signature.signature) {
266 Ok(b) => b,
267 Err(e) => return rejected(&format!("signature base64 decode: {}", e)),
268 };
269 let sig = match Signature::from_slice(&sig_bytes) {
270 Ok(s) => s,
271 Err(e) => return rejected(&format!("signature parse: {}", e)),
272 };
273 let vk = match VerifyingKey::from_bytes(issuer_public_key) {
274 Ok(v) => v,
275 Err(e) => return rejected(&format!("verifying key: {}", e)),
276 };
277 if vk.verify(&digest, &sig).is_err() {
278 return rejected("relay authority signature did not verify");
279 }
280 VerifyRelayAuthorityResult {
281 ok: true,
282 reason: None,
283 }
284}