1use ed25519_dalek::{Signature, SigningKey, VerifyingKey, SIGNATURE_LENGTH};
2use rand_core::CryptoRngCore;
3use x25519_dalek::PublicKey;
4
5use alloc::vec::Vec;
6use core::{fmt, marker::PhantomData};
7#[cfg(feature = "std")]
8use std::path::Path;
9
10use crate::{
11 error::RnsError,
12 hash::{AddressHash, Hash},
13 identity::{EmptyIdentity, HashIdentity, Identity, PrivateIdentity, PUBLIC_KEY_LENGTH},
14 packet::{
15 self, ContextFlag, DestinationType, Header, HeaderType, IfacFlag, Packet, PacketContext,
16 PacketDataBuffer, PacketType, PropagationType,
17 },
18 ratchets::{decrypt_with_identity, now_secs},
19};
20use sha2::Digest;
21
22#[path = "destination/primitives.rs"]
23mod primitives;
24#[path = "destination/ratchet.rs"]
25mod ratchet;
26#[cfg(test)]
27#[path = "destination/tests.rs"]
28mod tests;
29
30pub use primitives::{
31 group_decrypt, group_encrypt, Direction, Group, Input, Output, Plain, Single, Type,
32};
33pub use ratchet::RATCHET_LENGTH;
34use ratchet::{try_decrypt_with_ratchets, RatchetState};
35
36pub const NAME_HASH_LENGTH: usize = 10;
37pub const RAND_HASH_LENGTH: usize = 10;
38pub const MIN_ANNOUNCE_DATA_LENGTH: usize =
39 PUBLIC_KEY_LENGTH * 2 + NAME_HASH_LENGTH + RAND_HASH_LENGTH + SIGNATURE_LENGTH;
40
41#[derive(Copy, Clone)]
42pub struct DestinationName {
43 pub hash: Hash,
44}
45
46impl DestinationName {
47 pub fn new(app_name: &str, aspects: &str) -> Self {
48 let hash = Hash::new(
49 Hash::generator()
50 .chain_update(app_name.as_bytes())
51 .chain_update(".".as_bytes())
52 .chain_update(aspects.as_bytes())
53 .finalize()
54 .into(),
55 );
56
57 Self { hash }
58 }
59
60 pub fn new_from_hash_slice(hash_slice: &[u8]) -> Self {
61 let mut hash = [0u8; 32];
62 hash[..hash_slice.len()].copy_from_slice(hash_slice);
63
64 Self { hash: Hash::new(hash) }
65 }
66
67 pub fn as_name_hash_slice(&self) -> &[u8] {
68 &self.hash.as_slice()[..NAME_HASH_LENGTH]
69 }
70}
71
72#[derive(Copy, Clone)]
73pub struct DestinationDesc {
74 pub identity: Identity,
75 pub address_hash: AddressHash,
76 pub name: DestinationName,
77}
78
79impl fmt::Display for DestinationDesc {
80 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
81 write!(f, "{}", self.address_hash)?;
82
83 Ok(())
84 }
85}
86
87pub type DestinationAnnounce = Packet;
88
89pub struct AnnounceInfo<'a> {
90 pub destination: SingleOutputDestination,
91 pub app_data: &'a [u8],
92 pub ratchet: Option<[u8; RATCHET_LENGTH]>,
93}
94
95impl DestinationAnnounce {
96 pub fn validate(packet: &Packet) -> Result<AnnounceInfo<'_>, RnsError> {
97 let mut signed_data = [0u8; packet::PACKET_MDU];
98 Self::validate_with_buffer(packet, &mut signed_data)
99 }
100
101 pub fn validate_with_buffer<'a>(
102 packet: &'a Packet,
103 signed_data: &mut [u8],
104 ) -> Result<AnnounceInfo<'a>, RnsError> {
105 if packet.header.packet_type != PacketType::Announce {
106 return Err(RnsError::PacketError);
107 }
108
109 let announce_data = packet.data.as_slice();
110
111 if announce_data.len() < MIN_ANNOUNCE_DATA_LENGTH {
112 return Err(RnsError::OutOfMemory);
113 }
114
115 let mut offset = 0usize;
116
117 let public_key = {
118 let mut key_data = [0u8; PUBLIC_KEY_LENGTH];
119 key_data.copy_from_slice(&announce_data[offset..(offset + PUBLIC_KEY_LENGTH)]);
120 offset += PUBLIC_KEY_LENGTH;
121 PublicKey::from(key_data)
122 };
123
124 let verifying_key = {
125 let mut key_data = [0u8; PUBLIC_KEY_LENGTH];
126 key_data.copy_from_slice(&announce_data[offset..(offset + PUBLIC_KEY_LENGTH)]);
127 offset += PUBLIC_KEY_LENGTH;
128
129 VerifyingKey::from_bytes(&key_data).map_err(|_| RnsError::CryptoError)?
130 };
131
132 let identity = Identity::new(public_key, verifying_key);
133
134 let name_hash = &announce_data[offset..(offset + NAME_HASH_LENGTH)];
135 offset += NAME_HASH_LENGTH;
136 let rand_hash = &announce_data[offset..(offset + RAND_HASH_LENGTH)];
137 offset += RAND_HASH_LENGTH;
138 let destination = &packet.destination;
139 let expected_hash =
140 create_address_hash(&identity, &DestinationName::new_from_hash_slice(name_hash));
141 if expected_hash != *destination {
142 return Err(RnsError::IncorrectHash);
143 }
144
145 let remaining = announce_data.len().saturating_sub(offset);
146 if remaining < SIGNATURE_LENGTH {
147 return Err(RnsError::OutOfMemory);
148 }
149
150 let has_ratchet_flag = packet.header.context_flag == ContextFlag::Set;
151
152 if has_ratchet_flag {
153 if remaining < SIGNATURE_LENGTH + RATCHET_LENGTH {
154 return Err(RnsError::OutOfMemory);
155 }
156 let ratchet = &announce_data[offset..offset + RATCHET_LENGTH];
157 let sig_start = offset + RATCHET_LENGTH;
158 let sig_end = sig_start + SIGNATURE_LENGTH;
159 let signature = &announce_data[sig_start..sig_end];
160 let app_data = &announce_data[sig_end..];
161 verify_announce_with_buffer(
162 &identity,
163 destination.as_slice(),
164 public_key.as_bytes(),
165 verifying_key.as_bytes(),
166 name_hash,
167 rand_hash,
168 Some(ratchet),
169 signature,
170 app_data,
171 signed_data,
172 )?;
173 let mut ratchet_bytes = [0u8; RATCHET_LENGTH];
174 ratchet_bytes.copy_from_slice(ratchet);
175 return Ok(AnnounceInfo {
176 destination: SingleOutputDestination::new(
177 identity,
178 DestinationName::new_from_hash_slice(name_hash),
179 ),
180 app_data,
181 ratchet: Some(ratchet_bytes),
182 });
183 }
184
185 let signature = &announce_data[offset..(offset + SIGNATURE_LENGTH)];
186 let app_data = &announce_data[(offset + SIGNATURE_LENGTH)..];
187 verify_announce_with_buffer(
188 &identity,
189 destination.as_slice(),
190 public_key.as_bytes(),
191 verifying_key.as_bytes(),
192 name_hash,
193 rand_hash,
194 None,
195 signature,
196 app_data,
197 signed_data,
198 )?;
199
200 Ok(AnnounceInfo {
201 destination: SingleOutputDestination::new(
202 identity,
203 DestinationName::new_from_hash_slice(name_hash),
204 ),
205 app_data,
206 ratchet: None,
207 })
208 }
209}
210
211pub struct Destination<I: HashIdentity, D: Direction, T: Type> {
212 pub direction: PhantomData<D>,
213 pub r#type: PhantomData<T>,
214 pub identity: I,
215 pub desc: DestinationDesc,
216 ratchet_state: RatchetState,
217}
218
219impl<I: HashIdentity, D: Direction, T: Type> Destination<I, D, T> {
220 pub fn destination_type(&self) -> packet::DestinationType {
221 <T as Type>::destination_type()
222 }
223}
224
225pub enum DestinationHandleStatus {
253 None,
254 LinkProof,
255}
256
257impl Destination<PrivateIdentity, Input, Single> {
258 pub fn new(identity: PrivateIdentity, name: DestinationName) -> Self {
259 let address_hash = create_address_hash(&identity, &name);
260 let pub_identity = *identity.as_identity();
261
262 Self {
263 direction: PhantomData,
264 r#type: PhantomData,
265 identity,
266 desc: DestinationDesc { identity: pub_identity, name, address_hash },
267 ratchet_state: RatchetState::default(),
268 }
269 }
270
271 #[cfg(feature = "std")]
272 pub fn enable_ratchets<P: AsRef<Path>>(&mut self, path: P) -> Result<(), RnsError> {
273 let path = path.as_ref().to_path_buf();
274 self.ratchet_state.enable(&self.identity, path)
275 }
276
277 pub fn set_retained_ratchets(&mut self, retained: usize) -> Result<(), RnsError> {
278 if retained == 0 {
279 return Err(RnsError::InvalidArgument);
280 }
281 self.ratchet_state.retained_ratchets = retained;
282 if self.ratchet_state.ratchets.len() > retained {
283 self.ratchet_state.ratchets.truncate(retained);
284 }
285 Ok(())
286 }
287
288 pub fn set_ratchet_interval_secs(&mut self, secs: u64) -> Result<(), RnsError> {
289 if secs == 0 {
290 return Err(RnsError::InvalidArgument);
291 }
292 self.ratchet_state.ratchet_interval_secs = secs;
293 Ok(())
294 }
295
296 pub fn enforce_ratchets(&mut self, enforce: bool) {
297 self.ratchet_state.enforce_ratchets = enforce;
298 }
299
300 pub fn decrypt_with_ratchets(
301 &mut self,
302 ciphertext: &[u8],
303 ) -> Result<(Vec<u8>, bool), RnsError> {
304 let salt = self.identity.as_identity().address_hash.as_slice();
305 if self.ratchet_state.enabled && !self.ratchet_state.ratchets.is_empty() {
306 if let Some(plaintext) =
307 try_decrypt_with_ratchets(&self.ratchet_state, salt, ciphertext)
308 {
309 return Ok((plaintext, true));
310 }
311 #[cfg(feature = "std")]
312 if let Some(path) = self.ratchet_state.ratchets_path.clone() {
313 if self.ratchet_state.reload(&self.identity, &path).is_ok() {
314 if let Some(plaintext) =
315 try_decrypt_with_ratchets(&self.ratchet_state, salt, ciphertext)
316 {
317 return Ok((plaintext, true));
318 }
319 }
320 }
321 if self.ratchet_state.enforce_ratchets {
322 return Err(RnsError::CryptoError);
323 }
324 }
325
326 let plaintext = decrypt_with_identity(&self.identity, salt, ciphertext)?;
327 Ok((plaintext, false))
328 }
329
330 pub fn announce<R: CryptoRngCore + Copy>(
331 &mut self,
332 rng: R,
333 app_data: Option<&[u8]>,
334 ) -> Result<Packet, RnsError> {
335 let mut packet_data = PacketDataBuffer::new();
336
337 let mut rand_hash = [0u8; RAND_HASH_LENGTH];
341 let mut random_part = [0u8; RAND_HASH_LENGTH / 2];
342 let mut rng_mut = rng;
343 rng_mut.fill_bytes(&mut random_part);
344 rand_hash[..RAND_HASH_LENGTH / 2].copy_from_slice(&random_part);
345 let emitted_secs = now_secs().floor() as u64;
346 let emitted_be = emitted_secs.to_be_bytes();
347 rand_hash[RAND_HASH_LENGTH / 2..].copy_from_slice(&emitted_be[3..8]);
348
349 let pub_key = self.identity.as_identity().public_key_bytes();
350 let verifying_key = self.identity.as_identity().verifying_key_bytes();
351
352 let ratchet = if self.ratchet_state.enabled {
353 let now = now_secs();
354 self.ratchet_state.rotate_if_needed(&self.identity, now)?;
355 self.ratchet_state.current_ratchet_public()
356 } else {
357 None
358 };
359
360 packet_data
361 .chain_safe_write(self.desc.address_hash.as_slice())
362 .chain_safe_write(pub_key)
363 .chain_safe_write(verifying_key)
364 .chain_safe_write(self.desc.name.as_name_hash_slice())
365 .chain_safe_write(&rand_hash);
366
367 if let Some(ratchet) = ratchet {
368 packet_data.chain_safe_write(&ratchet);
369 }
370
371 if let Some(data) = app_data {
372 packet_data.chain_safe_write(data);
373 }
374
375 let signature = self.identity.sign(packet_data.as_slice());
376
377 packet_data.reset();
378
379 packet_data
380 .chain_safe_write(pub_key)
381 .chain_safe_write(verifying_key)
382 .chain_safe_write(self.desc.name.as_name_hash_slice())
383 .chain_safe_write(&rand_hash);
384
385 if let Some(ratchet) = ratchet {
386 packet_data.chain_safe_write(&ratchet);
387 }
388
389 packet_data.chain_safe_write(&signature.to_bytes());
390
391 if let Some(data) = app_data {
392 packet_data.write(data)?;
393 }
394
395 Ok(Packet {
396 header: Header {
397 ifac_flag: IfacFlag::Open,
398 header_type: HeaderType::Type1,
399 context_flag: if ratchet.is_some() { ContextFlag::Set } else { ContextFlag::Unset },
400 propagation_type: PropagationType::Broadcast,
401 destination_type: DestinationType::Single,
402 packet_type: PacketType::Announce,
403 hops: 0,
404 },
405 ifac: None,
406 destination: self.desc.address_hash,
407 transport: None,
408 context: PacketContext::None,
409 data: packet_data,
410 })
411 }
412
413 pub fn path_response<R: CryptoRngCore + Copy>(
414 &mut self,
415 rng: R,
416 app_data: Option<&[u8]>,
417 ) -> Result<Packet, RnsError> {
418 let mut announce = self.announce(rng, app_data)?;
419 announce.context = PacketContext::PathResponse;
420
421 Ok(announce)
422 }
423
424 pub fn handle_packet(&mut self, packet: &Packet) -> DestinationHandleStatus {
425 if self.desc.address_hash != packet.destination {
426 return DestinationHandleStatus::None;
427 }
428
429 if packet.header.packet_type == PacketType::LinkRequest {
430 return DestinationHandleStatus::LinkProof;
432 }
433
434 DestinationHandleStatus::None
435 }
436
437 pub fn sign_key(&self) -> &SigningKey {
438 self.identity.sign_key()
439 }
440}
441
442impl Destination<Identity, Output, Single> {
443 pub fn new(identity: Identity, name: DestinationName) -> Self {
444 let address_hash = create_address_hash(&identity, &name);
445 Self {
446 direction: PhantomData,
447 r#type: PhantomData,
448 identity,
449 desc: DestinationDesc { identity, name, address_hash },
450 ratchet_state: RatchetState::default(),
451 }
452 }
453}
454
455impl<D: Direction> Destination<EmptyIdentity, D, Plain> {
456 pub fn new(identity: EmptyIdentity, name: DestinationName) -> Self {
457 let address_hash = create_address_hash(&identity, &name);
458 Self {
459 direction: PhantomData,
460 r#type: PhantomData,
461 identity,
462 desc: DestinationDesc { identity: Default::default(), name, address_hash },
463 ratchet_state: RatchetState::default(),
464 }
465 }
466}
467
468fn create_address_hash<I: HashIdentity>(identity: &I, name: &DestinationName) -> AddressHash {
469 AddressHash::new_from_hash(&Hash::new(
470 Hash::generator()
471 .chain_update(name.as_name_hash_slice())
472 .chain_update(identity.as_address_hash_slice())
473 .finalize()
474 .into(),
475 ))
476}
477
478#[allow(clippy::too_many_arguments)]
479fn verify_announce_with_buffer(
480 identity: &Identity,
481 destination: &[u8],
482 public_key: &[u8],
483 verifying_key: &[u8],
484 name_hash: &[u8],
485 rand_hash: &[u8],
486 ratchet: Option<&[u8]>,
487 signature: &[u8],
488 app_data: &[u8],
489 signed_data: &mut [u8],
490) -> Result<(), RnsError> {
491 let required_len = destination.len()
492 + public_key.len()
493 + verifying_key.len()
494 + name_hash.len()
495 + rand_hash.len()
496 + ratchet.map(|value| value.len()).unwrap_or(0)
497 + app_data.len();
498 if required_len > signed_data.len() {
499 return Err(RnsError::OutOfMemory);
500 }
501
502 let mut offset = 0usize;
503 signed_data[offset..offset + destination.len()].copy_from_slice(destination);
504 offset += destination.len();
505 signed_data[offset..offset + public_key.len()].copy_from_slice(public_key);
506 offset += public_key.len();
507 signed_data[offset..offset + verifying_key.len()].copy_from_slice(verifying_key);
508 offset += verifying_key.len();
509 signed_data[offset..offset + name_hash.len()].copy_from_slice(name_hash);
510 offset += name_hash.len();
511 signed_data[offset..offset + rand_hash.len()].copy_from_slice(rand_hash);
512 offset += rand_hash.len();
513 if let Some(ratchet) = ratchet {
514 signed_data[offset..offset + ratchet.len()].copy_from_slice(ratchet);
515 offset += ratchet.len();
516 }
517 if !app_data.is_empty() {
518 signed_data[offset..offset + app_data.len()].copy_from_slice(app_data);
519 offset += app_data.len();
520 }
521
522 let signature = Signature::from_slice(signature).map_err(|_| RnsError::CryptoError)?;
523 identity.verify(&signed_data[..offset], &signature).map_err(|_| RnsError::IncorrectSignature)
524}
525
526pub type SingleInputDestination = Destination<PrivateIdentity, Input, Single>;
527pub type SingleOutputDestination = Destination<Identity, Output, Single>;
528pub type PlainInputDestination = Destination<EmptyIdentity, Input, Plain>;
529pub type PlainOutputDestination = Destination<EmptyIdentity, Output, Plain>;
530
531pub fn new_in(identity: PrivateIdentity, app_name: &str, aspect: &str) -> SingleInputDestination {
532 SingleInputDestination::new(identity, DestinationName::new(app_name, aspect))
533}
534
535pub fn new_out(identity: Identity, app_name: &str, aspect: &str) -> SingleOutputDestination {
536 SingleOutputDestination::new(identity, DestinationName::new(app_name, aspect))
537}