1use std::{
8 fmt::{self, Display, Formatter},
9 sync::atomic::Ordering,
10};
11
12use iroh_base::{PublicKey, SecretKey, Signature};
13use n0_error::{AnyError, anyerr, e, stack_error};
14use portable_atomic::AtomicU64;
15use simple_dns::{CLASS, Name, Packet, ResourceRecord, rdata::RData};
16
17const MAX_DNS_PACKET_SIZE: usize = 1000;
19
20const HEADER_SIZE: usize = 104;
22
23pub const MAX_SIGNED_PACKET_SIZE: usize = HEADER_SIZE + MAX_DNS_PACKET_SIZE;
25
26#[derive(Clone, PartialEq, Eq, derive_more::Debug)]
32#[debug("SignedPacket {{ public_key: {}, timestamp: {:?} }}", self.public_key(), self.timestamp())]
33pub struct SignedPacket {
34 bytes: Vec<u8>,
35}
36
37impl SignedPacket {
38 pub const MAX_BYTES: usize = MAX_SIGNED_PACKET_SIZE;
40
41 pub fn from_txt_strings(
45 secret_key: &SecretKey,
46 name: &str,
47 values: impl IntoIterator<Item = impl AsRef<str>>,
48 ttl: u32,
49 ) -> Result<SignedPacket, SignedPacketBuildError> {
50 let public_key = secret_key.public();
51 let origin = public_key.to_z32();
52 let normalized = normalize_name(&origin, name.to_string());
53 let dns_name = Name::new_unchecked(&normalized).into_owned();
54 let mut packet = Packet::new_reply(0);
55
56 for value in values {
57 let mut txt = simple_dns::rdata::TXT::new();
58 txt.add_string(value.as_ref())
59 .map_err(|e| e!(SignedPacketBuildError::DnsError, anyerr!(e)))?;
60 packet.answers.push(ResourceRecord::new(
61 dns_name.clone(),
62 CLASS::IN,
63 ttl,
64 RData::TXT(txt.into_owned()),
65 ));
66 }
67
68 let encoded_packet = packet
69 .build_bytes_vec_compressed()
70 .map_err(|e| e!(SignedPacketBuildError::DnsError, anyerr!(e)))?;
71
72 if encoded_packet.len() > MAX_DNS_PACKET_SIZE {
73 return Err(e!(SignedPacketBuildError::PacketTooLarge {
74 len: encoded_packet.len()
75 }));
76 }
77
78 let timestamp = Timestamp::now();
79 let signature = secret_key.sign(&signable(timestamp.as_micros(), &encoded_packet));
80
81 let mut bytes = Vec::with_capacity(HEADER_SIZE + encoded_packet.len());
82 bytes.extend_from_slice(public_key.as_bytes());
83 bytes.extend_from_slice(&signature.to_bytes());
84 bytes.extend_from_slice(×tamp.to_be_bytes());
85 bytes.extend_from_slice(&encoded_packet);
86
87 Ok(SignedPacket { bytes })
88 }
89
90 pub fn from_bytes(bytes: &[u8]) -> Result<SignedPacket, SignedPacketVerifyError> {
92 if bytes.len() < HEADER_SIZE {
93 return Err(e!(SignedPacketVerifyError::TooShort { len: bytes.len() }));
94 }
95 if bytes.len() > MAX_SIGNED_PACKET_SIZE {
96 return Err(e!(SignedPacketVerifyError::TooLarge { len: bytes.len() }));
97 }
98
99 let public_key = PublicKey::try_from(&bytes[..32])
100 .map_err(|e| e!(SignedPacketVerifyError::InvalidKey, e))?;
101 let signature =
102 Signature::from_bytes(bytes[32..96].try_into().expect("64 bytes for signature"));
103 let timestamp =
104 u64::from_be_bytes(bytes[96..104].try_into().expect("8 bytes for timestamp"));
105 let encoded_packet = &bytes[104..];
106
107 public_key
108 .verify(&signable(timestamp, encoded_packet), &signature)
109 .map_err(|e| e!(SignedPacketVerifyError::SignatureError, e))?;
110
111 Packet::parse(encoded_packet)
112 .map_err(|e| e!(SignedPacketVerifyError::DnsError, anyerr!(e)))?;
113
114 Ok(SignedPacket {
115 bytes: bytes.to_vec(),
116 })
117 }
118
119 pub fn from_relay_payload(
121 public_key: &PublicKey,
122 payload: &[u8],
123 ) -> Result<SignedPacket, SignedPacketVerifyError> {
124 let mut bytes = Vec::with_capacity(32 + payload.len());
125 bytes.extend_from_slice(public_key.as_bytes());
126 bytes.extend_from_slice(payload);
127 Self::from_bytes(&bytes)
128 }
129
130 pub fn from_bytes_unchecked(bytes: &[u8]) -> Result<SignedPacket, SignedPacketVerifyError> {
134 if bytes.len() < HEADER_SIZE {
135 return Err(e!(SignedPacketVerifyError::TooShort { len: bytes.len() }));
136 }
137 if bytes.len() > MAX_SIGNED_PACKET_SIZE {
138 return Err(e!(SignedPacketVerifyError::TooLarge { len: bytes.len() }));
139 }
140 Packet::parse(&bytes[104..])
141 .map_err(|e| e!(SignedPacketVerifyError::DnsError, anyerr!(e)))?;
142 Ok(SignedPacket {
143 bytes: bytes.to_vec(),
144 })
145 }
146
147 pub fn as_bytes(&self) -> &[u8] {
149 &self.bytes
150 }
151
152 pub fn to_relay_payload(&self) -> Vec<u8> {
154 self.bytes[32..].to_vec()
155 }
156
157 pub fn public_key(&self) -> PublicKey {
159 PublicKey::try_from(&self.bytes[..32]).expect("valid public key in SignedPacket")
160 }
161
162 pub fn signature(&self) -> Signature {
164 Signature::from_bytes(
165 self.bytes[32..96]
166 .try_into()
167 .expect("64 bytes for signature"),
168 )
169 }
170
171 pub fn timestamp(&self) -> Timestamp {
173 Timestamp::from_be_bytes(
174 self.bytes[96..104]
175 .try_into()
176 .expect("8 bytes for timestamp"),
177 )
178 }
179
180 pub fn encoded_packet(&self) -> &[u8] {
182 &self.bytes[104..]
183 }
184
185 pub fn txt_records(&self, name: &str) -> Vec<String> {
190 let origin = self.public_key().to_z32();
191 let normalized = normalize_name(&origin, name.to_string());
192 let Ok(packet) = Packet::parse(self.encoded_packet()) else {
193 return Vec::new();
194 };
195 let Ok(zone) = Name::new(&origin) else {
196 return Vec::new();
197 };
198 packet
199 .answers
200 .iter()
201 .filter_map(|rr| match &rr.rdata {
202 RData::TXT(txt) => {
203 let rr_name = rr.name.to_string();
205 if rr_name == normalized {
206 String::try_from(txt.clone()).ok()
207 } else if let Some(relative) = rr.name.without(&zone) {
208 if relative.to_string() == name {
209 String::try_from(txt.clone()).ok()
210 } else {
211 None
212 }
213 } else {
214 None
215 }
216 }
217 _ => None,
218 })
219 .collect()
220 }
221
222 pub fn all_txt_records(&self) -> Vec<(String, String)> {
226 let origin = self.public_key().to_z32();
227 let Ok(packet) = Packet::parse(self.encoded_packet()) else {
228 return Vec::new();
229 };
230 let Ok(zone) = Name::new(&origin) else {
231 return Vec::new();
232 };
233 packet
234 .answers
235 .iter()
236 .filter_map(|rr| match &rr.rdata {
237 RData::TXT(txt) => {
238 let relative_name = rr
239 .name
240 .without(&zone)
241 .map(|n| n.to_string())
242 .unwrap_or_default();
243 let value = String::try_from(txt.clone()).ok()?;
244 Some((relative_name, value))
245 }
246 _ => None,
247 })
248 .collect()
249 }
250
251 pub fn from_parts_unchecked(
256 public_key: &[u8],
257 signature: &[u8],
258 timestamp: Timestamp,
259 encoded_packet: &[u8],
260 ) -> Result<Self, SignedPacketVerifyError> {
261 let mut bytes = Vec::with_capacity(HEADER_SIZE + encoded_packet.len());
262 bytes.extend_from_slice(public_key);
263 bytes.extend_from_slice(signature);
264 bytes.extend_from_slice(×tamp.to_be_bytes());
265 bytes.extend_from_slice(encoded_packet);
266 Self::from_bytes_unchecked(&bytes)
267 }
268
269 pub fn more_recent_than(&self, other: &SignedPacket) -> bool {
271 if self.timestamp() == other.timestamp() {
272 self.encoded_packet() > other.encoded_packet()
273 } else {
274 self.timestamp() > other.timestamp()
275 }
276 }
277}
278
279impl Display for SignedPacket {
280 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
281 writeln!(f, "SignedPacket ({}):", self.public_key().to_z32())?;
282 writeln!(f, " timestamp: {}µs", self.timestamp().as_micros())?;
283 for (name, value) in self.all_txt_records() {
284 writeln!(f, " {name} TXT \"{value}\"")?;
285 }
286 Ok(())
287 }
288}
289
290fn signable(timestamp: u64, v: &[u8]) -> Vec<u8> {
292 let mut signable = format!("3:seqi{}e1:v{}:", timestamp, v.len()).into_bytes();
293 signable.extend(v);
294 signable
295}
296
297fn normalize_name(origin: &str, name: String) -> String {
299 let name = if name.ends_with('.') {
300 name[..name.len() - 1].to_string()
301 } else {
302 name
303 };
304
305 let parts: Vec<&str> = name.split('.').collect();
306 let last = *parts.last().unwrap_or(&"");
307
308 if last == origin {
309 return name;
310 }
311
312 if last == "@" || last.is_empty() {
313 return origin.to_string();
314 }
315
316 format!("{name}.{origin}")
317}
318
319#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, derive_more::Debug)]
330#[debug("Timestamp({}µs)", _0)]
331pub struct Timestamp(u64);
332
333static LAST_TIMESTAMP: AtomicU64 = AtomicU64::new(0);
335
336impl Timestamp {
337 pub fn now() -> Self {
342 use n0_future::time::SystemTime;
343 let micros = SystemTime::now()
344 .duration_since(SystemTime::UNIX_EPOCH)
345 .expect("system time before UNIX epoch")
346 .as_micros() as u64;
347 let mut last = LAST_TIMESTAMP.load(Ordering::Relaxed);
350 loop {
351 let next = micros.max(last + 1);
352 match LAST_TIMESTAMP.compare_exchange_weak(
353 last,
354 next,
355 Ordering::Relaxed,
356 Ordering::Relaxed,
357 ) {
358 Ok(_) => return Self(next),
359 Err(actual) => last = actual,
360 }
361 }
362 }
363
364 pub fn from_micros(micros: u64) -> Self {
366 Self(micros)
367 }
368
369 pub fn as_micros(self) -> u64 {
371 self.0
372 }
373
374 pub fn to_be_bytes(self) -> [u8; 8] {
376 self.0.to_be_bytes()
377 }
378
379 pub fn from_be_bytes(bytes: [u8; 8]) -> Self {
381 Self(u64::from_be_bytes(bytes))
382 }
383}
384
385#[allow(missing_docs)]
387#[stack_error(derive, add_meta)]
388#[non_exhaustive]
389pub enum SignedPacketBuildError {
390 #[error("DNS packet too large: {len} bytes (max {MAX_DNS_PACKET_SIZE})")]
391 PacketTooLarge { len: usize },
392 #[error("DNS encoding error")]
393 DnsError { source: AnyError },
394}
395
396#[allow(missing_docs)]
398#[stack_error(derive, add_meta)]
399#[non_exhaustive]
400pub enum SignedPacketVerifyError {
401 #[error("Signed packet too short: {len} bytes (min {HEADER_SIZE})")]
402 TooShort { len: usize },
403 #[error("Signed packet too large: {len} bytes (max {MAX_SIGNED_PACKET_SIZE})")]
404 TooLarge { len: usize },
405 #[error("Invalid signature")]
406 SignatureError {
407 #[error(std_err)]
408 source: iroh_base::SignatureError,
409 },
410 #[error("DNS decoding error")]
411 DnsError { source: AnyError },
412 #[error("Invalid public key")]
413 InvalidKey { source: iroh_base::KeyParsingError },
414}