1pub mod crypto;
2pub mod handshake;
3pub mod identify;
4pub mod keepalive;
5pub mod types;
6
7use alloc::vec::Vec;
8
9use rns_crypto::ed25519::{Ed25519PrivateKey, Ed25519PublicKey};
10use rns_crypto::token::Token;
11use rns_crypto::x25519::X25519PrivateKey;
12use rns_crypto::Rng;
13
14use crate::constants::{
15 LINK_ECPUBSIZE, LINK_ESTABLISHMENT_TIMEOUT_PER_HOP, LINK_KEEPALIVE_MAX, LINK_MTU_MAX,
16 LINK_MTU_MIN, MTU, PATHFINDER_M,
17};
18
19pub use types::{LinkAction, LinkError, LinkId, LinkMode, LinkState, TeardownReason};
20
21use crypto::{create_session_token, link_decrypt, link_encrypt};
22use handshake::{
23 build_linkrequest_data, compute_link_id, pack_rtt, parse_linkrequest_data,
24 perform_key_exchange, unpack_rtt, validate_lrproof,
25};
26use keepalive::{
27 compute_establishment_timeout, compute_keepalive, compute_stale_time, is_establishment_timeout,
28 should_go_stale, should_send_keepalive,
29};
30
31pub struct LinkEngine {
36 link_id: LinkId,
37 state: LinkState,
38 is_initiator: bool,
39 mode: LinkMode,
40
41 prv: X25519PrivateKey,
43 sig_prv: Ed25519PrivateKey,
44
45 peer_pub_bytes: Option<[u8; 32]>,
47 peer_sig_pub_bytes: Option<[u8; 32]>,
48
49 derived_key: Option<Vec<u8>>,
51 token: Option<Token>,
52
53 request_time: f64,
55 activated_at: Option<f64>,
56 last_inbound: f64,
57 last_outbound: f64,
58 last_keepalive: f64,
59 last_proof: f64,
60 rtt: Option<f64>,
61 keepalive_interval: f64,
62 stale_time: f64,
63 establishment_timeout: f64,
64 expected_hops: u8,
65 rebalanced_at: Option<f64>,
67
68 tx_packets: u64,
70 rx_packets: u64,
71 tx_bytes: u64,
72 rx_bytes: u64,
73
74 remote_identity: Option<([u8; 16], [u8; 64])>,
76 destination_hash: [u8; 16],
77
78 mtu: u32,
80 mdu: usize,
81}
82
83impl LinkEngine {
84 pub fn new_initiator(
89 dest_hash: &[u8; 16],
90 hops: u8,
91 mode: LinkMode,
92 mtu: Option<u32>,
93 now: f64,
94 rng: &mut dyn Rng,
95 ) -> (Self, Vec<u8>) {
96 let prv = X25519PrivateKey::generate(rng);
97 let pub_bytes = prv.public_key().public_bytes();
98 let sig_prv = Ed25519PrivateKey::generate(rng);
99 let sig_pub_bytes = sig_prv.public_key().public_bytes();
100
101 let request_data = build_linkrequest_data(&pub_bytes, &sig_pub_bytes, mtu, mode);
102
103 let link_mtu = effective_link_mtu(mtu);
104
105 let engine = LinkEngine {
106 link_id: [0u8; 16], state: LinkState::Pending,
108 is_initiator: true,
109 mode,
110 prv,
111 sig_prv,
112 peer_pub_bytes: None,
113 peer_sig_pub_bytes: None,
114 derived_key: None,
115 token: None,
116 request_time: now,
117 activated_at: None,
118 last_inbound: now,
119 last_outbound: now,
120 last_keepalive: now,
121 last_proof: 0.0,
122 rtt: None,
123 keepalive_interval: LINK_KEEPALIVE_MAX,
124 stale_time: LINK_KEEPALIVE_MAX * 2.0,
125 establishment_timeout: compute_establishment_timeout(
126 LINK_ESTABLISHMENT_TIMEOUT_PER_HOP,
127 hops,
128 ),
129 expected_hops: if hops < PATHFINDER_M {
130 hops
131 } else {
132 PATHFINDER_M
133 },
134 rebalanced_at: None,
135 tx_packets: 0,
136 rx_packets: 0,
137 tx_bytes: 0,
138 rx_bytes: 0,
139 remote_identity: None,
140 destination_hash: *dest_hash,
141 mtu: link_mtu,
142 mdu: compute_mdu(link_mtu as usize),
143 };
144
145 (engine, request_data)
146 }
147
148 pub fn set_link_id_from_hashable(&mut self, hashable_part: &[u8], data_len: usize) {
153 let extra = data_len.saturating_sub(LINK_ECPUBSIZE);
154 self.link_id = compute_link_id(hashable_part, extra);
155 }
156
157 #[allow(clippy::too_many_arguments)]
162 pub fn new_responder(
163 owner_sig_prv: &Ed25519PrivateKey,
164 owner_sig_pub_bytes: &[u8; 32],
165 linkrequest_data: &[u8],
166 hashable_part: &[u8],
167 dest_hash: &[u8; 16],
168 hops: u8,
169 now: f64,
170 rng: &mut dyn Rng,
171 ) -> Result<(Self, Vec<u8>), LinkError> {
172 let (peer_pub, peer_sig_pub, peer_mtu, mode) = parse_linkrequest_data(linkrequest_data)?;
173
174 let extra = linkrequest_data.len().saturating_sub(LINK_ECPUBSIZE);
175 let link_id = compute_link_id(hashable_part, extra);
176
177 let prv = X25519PrivateKey::generate(rng);
179 let pub_bytes = prv.public_key().public_bytes();
180 let sig_pub_bytes = *owner_sig_pub_bytes;
181
182 let derived_key = perform_key_exchange(&prv, &peer_pub, &link_id, mode)?;
184 let token = create_session_token(&derived_key)?;
185
186 let link_mtu = effective_link_mtu(peer_mtu);
187
188 let lrproof_data = handshake::build_lrproof(
190 &link_id,
191 &pub_bytes,
192 &sig_pub_bytes,
193 owner_sig_prv,
194 peer_mtu,
195 mode,
196 );
197
198 let engine = LinkEngine {
199 link_id,
200 state: LinkState::Handshake,
201 is_initiator: false,
202 mode,
203 prv,
204 sig_prv: Ed25519PrivateKey::from_bytes(&owner_sig_prv.private_bytes()),
205 peer_pub_bytes: Some(peer_pub),
206 peer_sig_pub_bytes: Some(peer_sig_pub),
207 derived_key: Some(derived_key),
208 token: Some(token),
209 request_time: now,
210 activated_at: None,
211 last_inbound: now,
212 last_outbound: now,
213 last_keepalive: now,
214 last_proof: 0.0,
215 rtt: None,
216 keepalive_interval: LINK_KEEPALIVE_MAX,
217 stale_time: LINK_KEEPALIVE_MAX * 2.0,
218 establishment_timeout: compute_establishment_timeout(
219 LINK_ESTABLISHMENT_TIMEOUT_PER_HOP,
220 hops,
221 ),
222 expected_hops: PATHFINDER_M,
223 rebalanced_at: None,
224 tx_packets: 0,
225 rx_packets: 0,
226 tx_bytes: 0,
227 rx_bytes: 0,
228 remote_identity: None,
229 destination_hash: *dest_hash,
230 mtu: link_mtu,
231 mdu: compute_mdu(link_mtu as usize),
232 };
233
234 Ok((engine, lrproof_data))
235 }
236
237 pub fn handle_lrproof(
242 &mut self,
243 proof_data: &[u8],
244 peer_sig_pub_bytes: &[u8; 32],
245 now: f64,
246 rng: &mut dyn Rng,
247 ) -> Result<(Vec<u8>, Vec<LinkAction>), LinkError> {
248 self.handle_lrproof_with_hops(proof_data, peer_sig_pub_bytes, None, now, rng)
249 }
250
251 pub fn handle_lrproof_with_hops(
253 &mut self,
254 proof_data: &[u8],
255 peer_sig_pub_bytes: &[u8; 32],
256 packet_hops: Option<u8>,
257 now: f64,
258 rng: &mut dyn Rng,
259 ) -> Result<(Vec<u8>, Vec<LinkAction>), LinkError> {
260 if self.state != LinkState::Pending || !self.is_initiator {
261 return Err(LinkError::InvalidState);
262 }
263
264 let peer_sig_pub = Ed25519PublicKey::from_bytes(peer_sig_pub_bytes)
265 .map_err(|_| LinkError::InvalidSignature)?;
266
267 let (peer_pub, confirmed_mtu, confirmed_mode) =
268 validate_lrproof(proof_data, &self.link_id, &peer_sig_pub, peer_sig_pub_bytes)?;
269
270 if confirmed_mode != self.mode {
271 return Err(LinkError::UnsupportedMode);
272 }
273
274 if let Some(hops) = packet_hops {
275 if hops != self.expected_hops && self.rebalanced_at.is_none() {
276 self.rebalanced_at = Some(now);
277 self.expected_hops = hops;
278 }
279 }
280
281 self.peer_pub_bytes = Some(peer_pub);
282 self.peer_sig_pub_bytes = Some(*peer_sig_pub_bytes);
283
284 let derived_key = perform_key_exchange(&self.prv, &peer_pub, &self.link_id, self.mode)?;
286 let token = create_session_token(&derived_key)?;
287
288 self.derived_key = Some(derived_key);
289 self.token = Some(token);
290
291 if confirmed_mtu.is_some() {
293 let mtu = effective_link_mtu(confirmed_mtu);
294 self.mtu = mtu;
295 self.mdu = compute_mdu(mtu as usize);
296 }
297
298 let rtt = now - self.request_time;
300 self.rtt = Some(rtt);
301 self.state = LinkState::Active;
302 self.activated_at = Some(now);
303 self.last_inbound = now;
304 self.update_keepalive();
305
306 let rtt_packed = pack_rtt(rtt);
308 let rtt_encrypted = self.encrypt(&rtt_packed, rng)?;
309
310 let actions = vec![
311 LinkAction::StateChanged {
312 link_id: self.link_id,
313 new_state: LinkState::Active,
314 reason: None,
315 },
316 LinkAction::LinkEstablished {
317 link_id: self.link_id,
318 rtt,
319 is_initiator: true,
320 },
321 ];
322
323 Ok((rtt_encrypted, actions))
324 }
325
326 pub fn handle_lrrtt(
330 &mut self,
331 encrypted_data: &[u8],
332 now: f64,
333 ) -> Result<Vec<LinkAction>, LinkError> {
334 self.handle_lrrtt_with_hops(encrypted_data, None, now)
335 }
336
337 pub fn handle_lrrtt_with_hops(
340 &mut self,
341 encrypted_data: &[u8],
342 packet_hops: Option<u8>,
343 now: f64,
344 ) -> Result<Vec<LinkAction>, LinkError> {
345 if self.state != LinkState::Handshake || self.is_initiator {
346 return Err(LinkError::InvalidState);
347 }
348
349 let plaintext = self.decrypt(encrypted_data)?;
350 let initiator_rtt = unpack_rtt(&plaintext).ok_or(LinkError::InvalidData)?;
351 if let Some(hops) = packet_hops {
352 self.expected_hops = hops;
353 }
354
355 let measured_rtt = now - self.request_time;
356 let rtt = measured_rtt.max(initiator_rtt);
358
359 self.rtt = Some(rtt);
360 self.state = LinkState::Active;
361 self.activated_at = Some(now);
362 self.last_inbound = now;
363 self.update_keepalive();
364
365 let actions = vec![
366 LinkAction::StateChanged {
367 link_id: self.link_id,
368 new_state: LinkState::Active,
369 reason: None,
370 },
371 LinkAction::LinkEstablished {
372 link_id: self.link_id,
373 rtt,
374 is_initiator: false,
375 },
376 ];
377
378 Ok(actions)
379 }
380
381 pub fn encrypt(&self, plaintext: &[u8], rng: &mut dyn Rng) -> Result<Vec<u8>, LinkError> {
383 let token = self.token.as_ref().ok_or(LinkError::NoSessionKey)?;
384 Ok(link_encrypt(token, plaintext, rng))
385 }
386
387 pub fn decrypt(&self, ciphertext: &[u8]) -> Result<Vec<u8>, LinkError> {
389 let token = self.token.as_ref().ok_or(LinkError::NoSessionKey)?;
390 link_decrypt(token, ciphertext)
391 }
392
393 pub fn sign_packet_hash(&self, packet_hash: &[u8; 32]) -> [u8; 64] {
395 self.sig_prv.sign(packet_hash)
396 }
397
398 pub fn validate_packet_proof(&self, packet_hash: &[u8; 32], signature: &[u8; 64]) -> bool {
400 self.peer_sig_pub_bytes
401 .and_then(|bytes| Ed25519PublicKey::from_bytes(&bytes).ok())
402 .map(|key| key.verify(signature, packet_hash))
403 .unwrap_or(false)
404 }
405
406 pub fn build_identify(
408 &self,
409 identity: &rns_crypto::identity::Identity,
410 rng: &mut dyn Rng,
411 ) -> Result<Vec<u8>, LinkError> {
412 if self.state != LinkState::Active {
413 return Err(LinkError::InvalidState);
414 }
415 let plaintext = identify::build_identify_data(identity, &self.link_id)?;
416 self.encrypt(&plaintext, rng)
417 }
418
419 pub fn handle_identify(&mut self, encrypted_data: &[u8]) -> Result<Vec<LinkAction>, LinkError> {
423 if self.state != LinkState::Active || self.is_initiator {
424 return Err(LinkError::InvalidState);
425 }
426
427 let plaintext = self.decrypt(encrypted_data)?;
428 let (identity_hash, public_key) =
429 identify::validate_identify_data(&plaintext, &self.link_id)?;
430 if self.remote_identity.is_some() {
431 return Ok(Vec::new());
432 }
433 self.remote_identity = Some((identity_hash, public_key));
434
435 Ok(alloc::vec![LinkAction::RemoteIdentified {
436 link_id: self.link_id,
437 identity_hash,
438 public_key,
439 }])
440 }
441
442 pub fn record_inbound(&mut self, now: f64) -> Vec<LinkAction> {
446 self.last_inbound = now;
447 if self.state == LinkState::Stale {
448 self.state = LinkState::Active;
449 return alloc::vec![LinkAction::StateChanged {
450 link_id: self.link_id,
451 new_state: LinkState::Active,
452 reason: None,
453 }];
454 }
455 Vec::new()
456 }
457
458 pub fn record_proof(&mut self, now: f64) {
460 self.last_proof = now;
461 }
462
463 pub fn record_outbound(&mut self, now: f64, is_keepalive: bool) {
465 self.last_outbound = now;
466 if is_keepalive {
467 self.last_keepalive = now;
468 }
469 }
470
471 pub fn record_inbound_traffic(&mut self, data_len: usize) {
473 if self.state != LinkState::Closed {
474 self.rx_packets = self.rx_packets.saturating_add(1);
475 self.rx_bytes = self.rx_bytes.saturating_add(data_len as u64);
476 }
477 }
478
479 pub fn record_outbound_traffic(&mut self, data_len: usize) {
481 self.tx_packets = self.tx_packets.saturating_add(1);
482 self.tx_bytes = self.tx_bytes.saturating_add(data_len as u64);
483 }
484
485 pub fn tick(&mut self, now: f64) -> Vec<LinkAction> {
487 let mut actions = Vec::new();
488
489 match self.state {
490 LinkState::Pending | LinkState::Handshake => {
491 if is_establishment_timeout(self.request_time, self.establishment_timeout, now) {
492 self.state = LinkState::Closed;
493 actions.push(LinkAction::StateChanged {
494 link_id: self.link_id,
495 new_state: LinkState::Closed,
496 reason: Some(TeardownReason::Timeout),
497 });
498 }
499 }
500 LinkState::Active => {
501 let activated = self.activated_at.unwrap_or(0.0);
502 let last_inbound = self.last_inbound.max(self.last_proof).max(activated);
504
505 if should_go_stale(last_inbound, self.stale_time, now) {
506 self.state = LinkState::Stale;
507 actions.push(LinkAction::StateChanged {
508 link_id: self.link_id,
509 new_state: LinkState::Stale,
510 reason: None,
511 });
512 }
513 }
514 LinkState::Stale => {
515 self.state = LinkState::Closed;
517 actions.push(LinkAction::StateChanged {
518 link_id: self.link_id,
519 new_state: LinkState::Closed,
520 reason: Some(TeardownReason::Timeout),
521 });
522 }
523 LinkState::Closed => {}
524 }
525
526 actions
527 }
528
529 pub fn needs_keepalive(&self, now: f64) -> bool {
531 if self.state != LinkState::Active || !self.is_initiator {
532 return false;
533 }
534 let activated = self.activated_at.unwrap_or(0.0);
535 let last_inbound = self.last_inbound.max(self.last_proof).max(activated);
536
537 let inbound_quiet = now >= last_inbound + self.keepalive_interval;
542 let outbound_quiet = now >= self.last_outbound + self.keepalive_interval;
543 if !inbound_quiet && !outbound_quiet {
544 return false;
545 }
546
547 should_send_keepalive(self.last_keepalive, self.keepalive_interval, now)
548 }
549
550 pub fn should_reply_keepalive(&self, payload: &[u8], now: f64) -> bool {
557 self.state == LinkState::Active
558 && !self.is_initiator
559 && payload == [0xff]
560 && now >= self.last_outbound + self.keepalive_interval
561 }
562
563 pub fn teardown(&mut self) -> Vec<LinkAction> {
565 if self.state == LinkState::Closed {
566 return Vec::new();
567 }
568 self.state = LinkState::Closed;
569 let reason = if self.is_initiator {
570 TeardownReason::InitiatorClosed
571 } else {
572 TeardownReason::DestinationClosed
573 };
574 alloc::vec![LinkAction::StateChanged {
575 link_id: self.link_id,
576 new_state: LinkState::Closed,
577 reason: Some(reason),
578 }]
579 }
580
581 pub fn handle_teardown(&mut self) -> Vec<LinkAction> {
583 if self.state == LinkState::Closed {
584 return Vec::new();
585 }
586 self.state = LinkState::Closed;
587 let reason = if self.is_initiator {
588 TeardownReason::DestinationClosed
589 } else {
590 TeardownReason::InitiatorClosed
591 };
592 alloc::vec![LinkAction::StateChanged {
593 link_id: self.link_id,
594 new_state: LinkState::Closed,
595 reason: Some(reason),
596 }]
597 }
598
599 pub fn link_id(&self) -> &LinkId {
602 &self.link_id
603 }
604
605 pub fn state(&self) -> LinkState {
606 self.state
607 }
608
609 pub fn rtt(&self) -> Option<f64> {
610 self.rtt
611 }
612
613 pub fn mdu(&self) -> usize {
614 self.mdu
615 }
616
617 pub fn mtu(&self) -> u32 {
618 self.mtu
619 }
620
621 pub fn is_initiator(&self) -> bool {
622 self.is_initiator
623 }
624
625 pub fn mode(&self) -> LinkMode {
626 self.mode
627 }
628
629 pub fn remote_identity(&self) -> Option<&([u8; 16], [u8; 64])> {
630 self.remote_identity.as_ref()
631 }
632
633 pub fn destination_hash(&self) -> &[u8; 16] {
634 &self.destination_hash
635 }
636
637 pub fn expected_hops(&self) -> u8 {
638 self.expected_hops
639 }
640
641 pub fn rebalanced_at(&self) -> Option<f64> {
642 self.rebalanced_at
643 }
644
645 pub fn tx_packets(&self) -> u64 {
646 self.tx_packets
647 }
648
649 pub fn rx_packets(&self) -> u64 {
650 self.rx_packets
651 }
652
653 pub fn tx_bytes(&self) -> u64 {
654 self.tx_bytes
655 }
656
657 pub fn rx_bytes(&self) -> u64 {
658 self.rx_bytes
659 }
660
661 pub fn derived_key(&self) -> Option<&[u8]> {
663 self.derived_key.as_deref()
664 }
665
666 pub fn keepalive_interval(&self) -> f64 {
667 self.keepalive_interval
668 }
669
670 pub fn set_rtt(&mut self, rtt: f64) {
673 self.rtt = Some(rtt);
674 self.update_keepalive();
675 }
676
677 pub fn set_mtu(&mut self, mtu: u32) {
679 self.mtu = mtu;
680 self.mdu = compute_mdu(mtu as usize);
681 }
682
683 #[doc(hidden)]
684 pub fn clear_session_for_testing(&mut self) {
685 self.derived_key = None;
686 self.token = None;
687 }
688
689 fn update_keepalive(&mut self) {
692 if let Some(rtt) = self.rtt {
693 self.keepalive_interval = compute_keepalive(rtt);
694 self.stale_time = compute_stale_time(self.keepalive_interval);
695 }
696 }
697}
698
699fn compute_mdu(mtu: usize) -> usize {
705 use crate::constants::{AES128_BLOCKSIZE, HEADER_MINSIZE, IFAC_MIN_SIZE, TOKEN_OVERHEAD};
706 let numerator = mtu.saturating_sub(IFAC_MIN_SIZE + HEADER_MINSIZE + TOKEN_OVERHEAD);
707 ((numerator / AES128_BLOCKSIZE) * AES128_BLOCKSIZE).saturating_sub(1)
708}
709
710fn effective_link_mtu(signalled_mtu: Option<u32>) -> u32 {
714 signalled_mtu
715 .filter(|mtu| *mtu != 0)
716 .map(|mtu| mtu.clamp(LINK_MTU_MIN, LINK_MTU_MAX))
717 .unwrap_or(MTU as u32)
718}
719use alloc::vec;
720
721#[cfg(test)]
722mod tests {
723 use super::*;
724 use crate::constants::LINK_MDU;
725 use rns_crypto::FixedRng;
726
727 fn make_rng(seed: u8) -> FixedRng {
728 FixedRng::new(&[seed; 128])
729 }
730
731 fn active_link_pair() -> (LinkEngine, LinkEngine, Vec<u8>) {
732 let mut rng_id = make_rng(0x01);
733 let dest_sig_prv = Ed25519PrivateKey::generate(&mut rng_id);
734 let dest_sig_pub_bytes = dest_sig_prv.public_key().public_bytes();
735 let dest_hash = [0xDD; 16];
736
737 let mut rng_init = make_rng(0x10);
738 let (mut initiator, request_data) = LinkEngine::new_initiator(
739 &dest_hash,
740 1,
741 LinkMode::Aes256Cbc,
742 Some(500),
743 100.0,
744 &mut rng_init,
745 );
746 let mut hashable = vec![0x00, 0x00];
747 hashable.extend_from_slice(&dest_hash);
748 hashable.push(0x00);
749 hashable.extend_from_slice(&request_data);
750 initiator.set_link_id_from_hashable(&hashable, request_data.len());
751
752 let mut rng_resp = make_rng(0x20);
753 let (mut responder, lrproof_data) = LinkEngine::new_responder(
754 &dest_sig_prv,
755 &dest_sig_pub_bytes,
756 &request_data,
757 &hashable,
758 &dest_hash,
759 1,
760 100.5,
761 &mut rng_resp,
762 )
763 .unwrap();
764 let mut rng_lrrtt = make_rng(0x30);
765 let (lrrtt_encrypted, _) = initiator
766 .handle_lrproof(&lrproof_data, &dest_sig_pub_bytes, 100.8, &mut rng_lrrtt)
767 .unwrap();
768 responder.handle_lrrtt(&lrrtt_encrypted, 101.0).unwrap();
769
770 (initiator, responder, lrrtt_encrypted)
771 }
772
773 #[test]
774 fn test_compute_mdu_default() {
775 assert_eq!(compute_mdu(500), LINK_MDU);
776 }
777
778 #[test]
779 fn zero_mtu_signalling_falls_back_to_reticulum_mtu() {
780 let mut rng_id = make_rng(0x01);
781 let dest_sig_prv = Ed25519PrivateKey::generate(&mut rng_id);
782 let dest_sig_pub_bytes = dest_sig_prv.public_key().public_bytes();
783 let dest_hash = [0xDD; 16];
784
785 let mut rng_init = make_rng(0x10);
786 let (mut initiator, request_data) = LinkEngine::new_initiator(
787 &dest_hash,
788 1,
789 LinkMode::Aes256Cbc,
790 Some(0),
791 100.0,
792 &mut rng_init,
793 );
794 let mut hashable = vec![0x00, 0x00];
795 hashable.extend_from_slice(&dest_hash);
796 hashable.push(0x00);
797 hashable.extend_from_slice(&request_data);
798 initiator.set_link_id_from_hashable(&hashable, request_data.len());
799
800 let mut rng_resp = make_rng(0x20);
801 let (responder, lrproof_data) = LinkEngine::new_responder(
802 &dest_sig_prv,
803 &dest_sig_pub_bytes,
804 &request_data,
805 &hashable,
806 &dest_hash,
807 1,
808 100.5,
809 &mut rng_resp,
810 )
811 .unwrap();
812
813 let mut rng_lrrtt = make_rng(0x30);
814 initiator
815 .handle_lrproof(&lrproof_data, &dest_sig_pub_bytes, 100.8, &mut rng_lrrtt)
816 .unwrap();
817
818 assert_eq!(initiator.mtu(), MTU as u32);
819 assert_eq!(responder.mtu(), MTU as u32);
820 assert_eq!(initiator.mdu(), LINK_MDU);
821 assert_eq!(responder.mdu(), LINK_MDU);
822 }
823
824 #[test]
825 fn test_full_handshake() {
826 let mut rng_id = make_rng(0x01);
828 let dest_sig_prv = Ed25519PrivateKey::generate(&mut rng_id);
829 let dest_sig_pub_bytes = dest_sig_prv.public_key().public_bytes();
830
831 let dest_hash = [0xDD; 16];
832 let mode = LinkMode::Aes256Cbc;
833
834 let mut rng_init = make_rng(0x10);
836 let (mut initiator, request_data) =
837 LinkEngine::new_initiator(&dest_hash, 1, mode, Some(500), 100.0, &mut rng_init);
838 assert_eq!(initiator.state(), LinkState::Pending);
839
840 let mut hashable = Vec::new();
843 hashable.push(0x00); hashable.push(0x00); hashable.extend_from_slice(&dest_hash);
846 hashable.push(0x00); hashable.extend_from_slice(&request_data);
848
849 initiator.set_link_id_from_hashable(&hashable, request_data.len());
850 assert_ne!(initiator.link_id(), &[0u8; 16]);
851
852 let mut rng_resp = make_rng(0x20);
854 let (mut responder, lrproof_data) = LinkEngine::new_responder(
855 &dest_sig_prv,
856 &dest_sig_pub_bytes,
857 &request_data,
858 &hashable,
859 &dest_hash,
860 1,
861 100.5,
862 &mut rng_resp,
863 )
864 .unwrap();
865 assert_eq!(responder.state(), LinkState::Handshake);
866 assert_eq!(responder.link_id(), initiator.link_id());
867
868 let mut rng_lrrtt = make_rng(0x30);
870 assert!(initiator
871 .handle_lrproof_with_hops(
872 &[0xAA; 3],
873 &dest_sig_pub_bytes,
874 Some(8),
875 100.6,
876 &mut rng_lrrtt,
877 )
878 .is_err());
879 assert_eq!(initiator.expected_hops(), 1);
880 let mismatched_mode_proof = handshake::build_lrproof(
881 initiator.link_id(),
882 lrproof_data[64..96].try_into().unwrap(),
883 &dest_sig_pub_bytes,
884 &dest_sig_prv,
885 Some(500),
886 LinkMode::Aes128Cbc,
887 );
888 assert_eq!(
889 initiator
890 .handle_lrproof_with_hops(
891 &mismatched_mode_proof,
892 &dest_sig_pub_bytes,
893 Some(9),
894 100.65,
895 &mut rng_lrrtt,
896 )
897 .unwrap_err(),
898 LinkError::UnsupportedMode,
899 );
900 assert_eq!(initiator.expected_hops(), 1);
901 let mut invalid_lrproof = lrproof_data.clone();
902 invalid_lrproof[0] ^= 0x01;
903 assert!(initiator
904 .handle_lrproof_with_hops(
905 &invalid_lrproof,
906 &dest_sig_pub_bytes,
907 Some(7),
908 100.7,
909 &mut rng_lrrtt,
910 )
911 .is_err());
912 assert_eq!(initiator.expected_hops(), 1);
913 let (lrrtt_encrypted, actions) = initiator
914 .handle_lrproof_with_hops(
915 &lrproof_data,
916 &dest_sig_pub_bytes,
917 Some(4),
918 100.8,
919 &mut rng_lrrtt,
920 )
921 .unwrap();
922 assert_eq!(initiator.state(), LinkState::Active);
923 assert!(initiator.rtt().is_some());
924 assert_eq!(initiator.rebalanced_at(), Some(100.8));
925 assert_eq!(actions.len(), 2); let actions = responder
929 .handle_lrrtt_with_hops(&lrrtt_encrypted, Some(4), 101.0)
930 .unwrap();
931 assert_eq!(responder.state(), LinkState::Active);
932 assert_eq!(initiator.expected_hops(), 4);
933 assert_eq!(responder.expected_hops(), 4);
934 assert_eq!(responder.rebalanced_at(), None);
935
936 initiator.state = LinkState::Pending;
939 let mut repeated_rng = make_rng(0x31);
940 initiator
941 .handle_lrproof_with_hops(
942 &lrproof_data,
943 &dest_sig_pub_bytes,
944 Some(6),
945 101.1,
946 &mut repeated_rng,
947 )
948 .unwrap();
949 assert_eq!(initiator.expected_hops(), 4);
950 assert_eq!(initiator.rebalanced_at(), Some(100.8));
951
952 initiator.record_outbound_traffic(48);
953 initiator.record_inbound_traffic(32);
954 assert_eq!((initiator.tx_packets(), initiator.tx_bytes()), (1, 48));
955 assert_eq!((initiator.rx_packets(), initiator.rx_bytes()), (1, 32));
956 assert!(responder.rtt().is_some());
957 assert_eq!(actions.len(), 2);
958 }
959
960 #[test]
961 fn test_encrypt_decrypt_after_handshake() {
962 let mut rng_id = make_rng(0x01);
963 let dest_sig_prv = Ed25519PrivateKey::generate(&mut rng_id);
964 let dest_sig_pub_bytes = dest_sig_prv.public_key().public_bytes();
965 let dest_hash = [0xDD; 16];
966
967 let mut rng_init = make_rng(0x10);
968 let (mut initiator, request_data) = LinkEngine::new_initiator(
969 &dest_hash,
970 1,
971 LinkMode::Aes256Cbc,
972 Some(500),
973 100.0,
974 &mut rng_init,
975 );
976 let mut hashable = Vec::new();
977 hashable.push(0x00);
978 hashable.push(0x00);
979 hashable.extend_from_slice(&dest_hash);
980 hashable.push(0x00);
981 hashable.extend_from_slice(&request_data);
982 initiator.set_link_id_from_hashable(&hashable, request_data.len());
983
984 let mut rng_resp = make_rng(0x20);
985 let (mut responder, lrproof_data) = LinkEngine::new_responder(
986 &dest_sig_prv,
987 &dest_sig_pub_bytes,
988 &request_data,
989 &hashable,
990 &dest_hash,
991 1,
992 100.5,
993 &mut rng_resp,
994 )
995 .unwrap();
996
997 let mut rng_lrrtt = make_rng(0x30);
998 let (lrrtt_encrypted, _) = initiator
999 .handle_lrproof(&lrproof_data, &dest_sig_pub_bytes, 100.8, &mut rng_lrrtt)
1000 .unwrap();
1001 responder.handle_lrrtt(&lrrtt_encrypted, 101.0).unwrap();
1002
1003 let mut rng_enc = make_rng(0x40);
1005 let plaintext = b"Hello over encrypted link!";
1006 let ciphertext = initiator.encrypt(plaintext, &mut rng_enc).unwrap();
1007 let decrypted = responder.decrypt(&ciphertext).unwrap();
1008 assert_eq!(decrypted, plaintext);
1009
1010 let mut rng_enc2 = make_rng(0x50);
1012 let ciphertext2 = responder.encrypt(b"Reply!", &mut rng_enc2).unwrap();
1013 let decrypted2 = initiator.decrypt(&ciphertext2).unwrap();
1014 assert_eq!(decrypted2, b"Reply!");
1015 }
1016
1017 #[test]
1018 fn test_tick_establishment_timeout() {
1019 let mut rng = make_rng(0x10);
1020 let dest_hash = [0xDD; 16];
1021 let (mut engine, _) =
1022 LinkEngine::new_initiator(&dest_hash, 1, LinkMode::Aes256Cbc, None, 100.0, &mut rng);
1023 let actions = engine.tick(110.0);
1027 assert!(actions.is_empty());
1028
1029 let actions = engine.tick(113.0);
1031 assert_eq!(actions.len(), 1);
1032 assert_eq!(engine.state(), LinkState::Closed);
1033 }
1034
1035 #[test]
1036 fn test_tick_stale_and_close() {
1037 let mut rng_id = make_rng(0x01);
1038 let dest_sig_prv = Ed25519PrivateKey::generate(&mut rng_id);
1039 let dest_sig_pub_bytes = dest_sig_prv.public_key().public_bytes();
1040 let dest_hash = [0xDD; 16];
1041
1042 let mut rng_init = make_rng(0x10);
1043 let (mut initiator, request_data) = LinkEngine::new_initiator(
1044 &dest_hash,
1045 1,
1046 LinkMode::Aes256Cbc,
1047 Some(500),
1048 100.0,
1049 &mut rng_init,
1050 );
1051 let mut hashable = Vec::new();
1052 hashable.push(0x00);
1053 hashable.push(0x00);
1054 hashable.extend_from_slice(&dest_hash);
1055 hashable.push(0x00);
1056 hashable.extend_from_slice(&request_data);
1057 initiator.set_link_id_from_hashable(&hashable, request_data.len());
1058
1059 let mut rng_resp = make_rng(0x20);
1060 let (_, lrproof_data) = LinkEngine::new_responder(
1061 &dest_sig_prv,
1062 &dest_sig_pub_bytes,
1063 &request_data,
1064 &hashable,
1065 &dest_hash,
1066 1,
1067 100.5,
1068 &mut rng_resp,
1069 )
1070 .unwrap();
1071
1072 let mut rng_lrrtt = make_rng(0x30);
1073 initiator
1074 .handle_lrproof(&lrproof_data, &dest_sig_pub_bytes, 100.8, &mut rng_lrrtt)
1075 .unwrap();
1076 assert_eq!(initiator.state(), LinkState::Active);
1077
1078 let stale_time = initiator.stale_time;
1080 let actions = initiator.tick(100.8 + stale_time + 1.0);
1081 assert_eq!(initiator.state(), LinkState::Stale);
1082 assert_eq!(actions.len(), 1);
1083
1084 let actions = initiator.tick(100.8 + stale_time + 2.0);
1086 assert_eq!(initiator.state(), LinkState::Closed);
1087 assert_eq!(actions.len(), 1);
1088 }
1089
1090 #[test]
1091 fn test_needs_keepalive() {
1092 let mut rng_id = make_rng(0x01);
1093 let dest_sig_prv = Ed25519PrivateKey::generate(&mut rng_id);
1094 let dest_sig_pub_bytes = dest_sig_prv.public_key().public_bytes();
1095 let dest_hash = [0xDD; 16];
1096
1097 let mut rng_init = make_rng(0x10);
1098 let (mut initiator, request_data) = LinkEngine::new_initiator(
1099 &dest_hash,
1100 1,
1101 LinkMode::Aes256Cbc,
1102 Some(500),
1103 100.0,
1104 &mut rng_init,
1105 );
1106 let mut hashable = Vec::new();
1107 hashable.push(0x00);
1108 hashable.push(0x00);
1109 hashable.extend_from_slice(&dest_hash);
1110 hashable.push(0x00);
1111 hashable.extend_from_slice(&request_data);
1112 initiator.set_link_id_from_hashable(&hashable, request_data.len());
1113
1114 let mut rng_resp = make_rng(0x20);
1115 let (_, lrproof_data) = LinkEngine::new_responder(
1116 &dest_sig_prv,
1117 &dest_sig_pub_bytes,
1118 &request_data,
1119 &hashable,
1120 &dest_hash,
1121 1,
1122 100.5,
1123 &mut rng_resp,
1124 )
1125 .unwrap();
1126
1127 let mut rng_lrrtt = make_rng(0x30);
1128 initiator
1129 .handle_lrproof(&lrproof_data, &dest_sig_pub_bytes, 100.8, &mut rng_lrrtt)
1130 .unwrap();
1131
1132 let ka = initiator.keepalive_interval();
1133 assert!(!initiator.needs_keepalive(100.8 + ka - 1.0));
1135 assert!(initiator.needs_keepalive(100.8 + ka + 1.0));
1137 }
1138
1139 #[test]
1140 fn initiator_probes_when_outbound_is_quiet_despite_recent_inbound() {
1141 let (mut initiator, _, _) = active_link_pair();
1142 let keepalive = initiator.keepalive_interval();
1143 let now = 1_000.0;
1144
1145 initiator.record_inbound(now - 0.1);
1146 initiator.record_outbound(now - keepalive - 0.1, false);
1147
1148 assert!(initiator.needs_keepalive(now));
1149 }
1150
1151 #[test]
1152 fn initiator_probes_when_inbound_is_quiet_despite_recent_outbound() {
1153 let (mut initiator, _, _) = active_link_pair();
1154 let keepalive = initiator.keepalive_interval();
1155 let now = 1_000.0;
1156
1157 initiator.record_inbound(now - keepalive - 0.1);
1158 initiator.record_outbound(now - 0.1, false);
1159
1160 assert!(initiator.needs_keepalive(now));
1161 }
1162
1163 #[test]
1164 fn initiator_does_not_probe_when_both_directions_are_recent() {
1165 let (mut initiator, _, _) = active_link_pair();
1166 let now = 1_000.0;
1167
1168 initiator.record_inbound(now - 0.1);
1169 initiator.record_outbound(now - 0.1, false);
1170
1171 assert!(!initiator.needs_keepalive(now));
1172 }
1173
1174 #[test]
1175 fn test_needs_keepalive_responder() {
1176 let mut rng_id = make_rng(0x01);
1177 let dest_sig_prv = Ed25519PrivateKey::generate(&mut rng_id);
1178 let dest_sig_pub_bytes = dest_sig_prv.public_key().public_bytes();
1179 let dest_hash = [0xDD; 16];
1180
1181 let mut rng_init = make_rng(0x10);
1182 let (mut initiator, request_data) = LinkEngine::new_initiator(
1183 &dest_hash,
1184 1,
1185 LinkMode::Aes256Cbc,
1186 Some(500),
1187 100.0,
1188 &mut rng_init,
1189 );
1190 let mut hashable = Vec::new();
1191 hashable.push(0x00);
1192 hashable.push(0x00);
1193 hashable.extend_from_slice(&dest_hash);
1194 hashable.push(0x00);
1195 hashable.extend_from_slice(&request_data);
1196 initiator.set_link_id_from_hashable(&hashable, request_data.len());
1197
1198 let mut rng_resp = make_rng(0x20);
1199 let (mut responder, lrproof_data) = LinkEngine::new_responder(
1200 &dest_sig_prv,
1201 &dest_sig_pub_bytes,
1202 &request_data,
1203 &hashable,
1204 &dest_hash,
1205 1,
1206 100.5,
1207 &mut rng_resp,
1208 )
1209 .unwrap();
1210
1211 let mut rng_lrrtt = make_rng(0x30);
1212 let (lrrtt_encrypted, _) = initiator
1213 .handle_lrproof(&lrproof_data, &dest_sig_pub_bytes, 100.8, &mut rng_lrrtt)
1214 .unwrap();
1215 responder.handle_lrrtt(&lrrtt_encrypted, 101.0).unwrap();
1216
1217 let ka = responder.keepalive_interval();
1218 assert!(!responder.needs_keepalive(101.0 + ka - 1.0));
1220 assert!(!responder.needs_keepalive(101.0 + ka + 1.0));
1221 }
1222
1223 #[test]
1224 fn responder_replies_only_to_probe_and_only_when_outbound_is_quiet() {
1225 let (_, mut responder, _) = active_link_pair();
1226 let keepalive = responder.keepalive_interval();
1227 let now = 1_000.0;
1228
1229 responder.record_outbound(now - keepalive - 0.1, false);
1230 assert!(responder.should_reply_keepalive(&[0xff], now));
1231 assert!(!responder.should_reply_keepalive(&[0xfe], now));
1232 assert!(!responder.should_reply_keepalive(&[], now));
1233
1234 responder.record_outbound(now - 0.1, false);
1235 assert!(!responder.should_reply_keepalive(&[0xff], now));
1236 }
1237
1238 #[test]
1239 fn initiator_never_replies_to_keepalive_probe() {
1240 let (mut initiator, _, _) = active_link_pair();
1241 let keepalive = initiator.keepalive_interval();
1242 let now = 1_000.0;
1243 initiator.record_outbound(now - keepalive - 0.1, false);
1244
1245 assert!(!initiator.should_reply_keepalive(&[0xff], now));
1246 }
1247
1248 #[test]
1249 fn packet_delivery_proofs_are_bidirectional_and_peer_authenticated() {
1250 let (initiator, responder, _) = active_link_pair();
1251 let packet_hash = [0x42; 32];
1252
1253 let initiator_proof = initiator.sign_packet_hash(&packet_hash);
1254 assert!(responder.validate_packet_proof(&packet_hash, &initiator_proof));
1255 assert!(!initiator.validate_packet_proof(&packet_hash, &initiator_proof));
1256
1257 let responder_proof = responder.sign_packet_hash(&packet_hash);
1258 assert!(initiator.validate_packet_proof(&packet_hash, &responder_proof));
1259 assert!(!responder.validate_packet_proof(&packet_hash, &responder_proof));
1260 assert!(!initiator.validate_packet_proof(&[0x43; 32], &responder_proof));
1261 }
1262
1263 #[test]
1264 fn test_teardown() {
1265 let mut rng = make_rng(0x10);
1266 let (mut engine, _) =
1267 LinkEngine::new_initiator(&[0xDD; 16], 1, LinkMode::Aes256Cbc, None, 100.0, &mut rng);
1268 let actions = engine.teardown();
1269 assert_eq!(engine.state(), LinkState::Closed);
1270 assert_eq!(actions.len(), 1);
1271
1272 let actions = engine.teardown();
1274 assert!(actions.is_empty());
1275 }
1276
1277 #[test]
1278 fn test_handle_teardown() {
1279 let mut rng = make_rng(0x10);
1280 let (mut engine, _) =
1281 LinkEngine::new_initiator(&[0xDD; 16], 1, LinkMode::Aes256Cbc, None, 100.0, &mut rng);
1282 let actions = engine.handle_teardown();
1283 assert_eq!(engine.state(), LinkState::Closed);
1284 assert_eq!(actions.len(), 1);
1285 match &actions[0] {
1286 LinkAction::StateChanged { reason, .. } => {
1287 assert_eq!(*reason, Some(TeardownReason::DestinationClosed));
1288 }
1289 _ => panic!("Expected StateChanged"),
1290 }
1291 }
1292
1293 #[test]
1294 fn test_identify_over_link() {
1295 let mut rng_id = make_rng(0x01);
1296 let dest_sig_prv = Ed25519PrivateKey::generate(&mut rng_id);
1297 let dest_sig_pub_bytes = dest_sig_prv.public_key().public_bytes();
1298 let dest_hash = [0xDD; 16];
1299
1300 let mut rng_init = make_rng(0x10);
1301 let (mut initiator, request_data) = LinkEngine::new_initiator(
1302 &dest_hash,
1303 1,
1304 LinkMode::Aes256Cbc,
1305 Some(500),
1306 100.0,
1307 &mut rng_init,
1308 );
1309 let mut hashable = Vec::new();
1310 hashable.push(0x00);
1311 hashable.push(0x00);
1312 hashable.extend_from_slice(&dest_hash);
1313 hashable.push(0x00);
1314 hashable.extend_from_slice(&request_data);
1315 initiator.set_link_id_from_hashable(&hashable, request_data.len());
1316
1317 let mut rng_resp = make_rng(0x20);
1318 let (mut responder, lrproof_data) = LinkEngine::new_responder(
1319 &dest_sig_prv,
1320 &dest_sig_pub_bytes,
1321 &request_data,
1322 &hashable,
1323 &dest_hash,
1324 1,
1325 100.5,
1326 &mut rng_resp,
1327 )
1328 .unwrap();
1329
1330 let mut rng_lrrtt = make_rng(0x30);
1331 let (lrrtt_encrypted, _) = initiator
1332 .handle_lrproof(&lrproof_data, &dest_sig_pub_bytes, 100.8, &mut rng_lrrtt)
1333 .unwrap();
1334 responder.handle_lrrtt(&lrrtt_encrypted, 101.0).unwrap();
1335
1336 let mut rng_ident = make_rng(0x40);
1338 let my_identity = rns_crypto::identity::Identity::new(&mut rng_ident);
1339
1340 let mut rng_enc = make_rng(0x50);
1342 let identify_encrypted = initiator
1343 .build_identify(&my_identity, &mut rng_enc)
1344 .unwrap();
1345
1346 let actions = responder.handle_identify(&identify_encrypted).unwrap();
1347 assert_eq!(actions.len(), 1);
1348 match &actions[0] {
1349 LinkAction::RemoteIdentified {
1350 identity_hash,
1351 public_key,
1352 ..
1353 } => {
1354 assert_eq!(identity_hash, my_identity.hash());
1355 assert_eq!(public_key, &my_identity.get_public_key().unwrap());
1356 }
1357 _ => panic!("Expected RemoteIdentified"),
1358 }
1359
1360 let mut rng_repeat = make_rng(0x51);
1363 let repeated = initiator
1364 .build_identify(&my_identity, &mut rng_repeat)
1365 .unwrap();
1366 assert!(responder.handle_identify(&repeated).unwrap().is_empty());
1367
1368 let mut rng_other = make_rng(0x60);
1369 let other_identity = rns_crypto::identity::Identity::new(&mut rng_other);
1370 let mut rng_other_enc = make_rng(0x61);
1371 let other = initiator
1372 .build_identify(&other_identity, &mut rng_other_enc)
1373 .unwrap();
1374 assert!(responder.handle_identify(&other).unwrap().is_empty());
1375 assert_eq!(
1376 responder.remote_identity().map(|(hash, _)| hash),
1377 Some(my_identity.hash())
1378 );
1379
1380 let mut rng_bad = make_rng(0x70);
1382 let malformed = initiator
1383 .encrypt(b"invalid identify", &mut rng_bad)
1384 .unwrap();
1385 assert!(responder.handle_identify(&malformed).is_err());
1386 assert_eq!(
1387 responder.remote_identity().map(|(hash, _)| hash),
1388 Some(my_identity.hash())
1389 );
1390 }
1391
1392 #[test]
1393 fn test_aes128_mode_handshake() {
1394 let mut rng_id = make_rng(0x01);
1395 let dest_sig_prv = Ed25519PrivateKey::generate(&mut rng_id);
1396 let dest_sig_pub_bytes = dest_sig_prv.public_key().public_bytes();
1397 let dest_hash = [0xDD; 16];
1398
1399 let mut rng_init = make_rng(0x10);
1400 let (mut initiator, request_data) = LinkEngine::new_initiator(
1401 &dest_hash,
1402 1,
1403 LinkMode::Aes128Cbc,
1404 Some(500),
1405 100.0,
1406 &mut rng_init,
1407 );
1408 let mut hashable = Vec::new();
1409 hashable.push(0x00);
1410 hashable.push(0x00);
1411 hashable.extend_from_slice(&dest_hash);
1412 hashable.push(0x00);
1413 hashable.extend_from_slice(&request_data);
1414 initiator.set_link_id_from_hashable(&hashable, request_data.len());
1415
1416 let mut rng_resp = make_rng(0x20);
1417 let (mut responder, lrproof_data) = LinkEngine::new_responder(
1418 &dest_sig_prv,
1419 &dest_sig_pub_bytes,
1420 &request_data,
1421 &hashable,
1422 &dest_hash,
1423 1,
1424 100.5,
1425 &mut rng_resp,
1426 )
1427 .unwrap();
1428
1429 let mut rng_lrrtt = make_rng(0x30);
1430 let (lrrtt_encrypted, _) = initiator
1431 .handle_lrproof(&lrproof_data, &dest_sig_pub_bytes, 100.8, &mut rng_lrrtt)
1432 .unwrap();
1433 responder.handle_lrrtt(&lrrtt_encrypted, 101.0).unwrap();
1434
1435 assert_eq!(initiator.state(), LinkState::Active);
1436 assert_eq!(responder.state(), LinkState::Active);
1437 assert_eq!(initiator.mode(), LinkMode::Aes128Cbc);
1438
1439 let mut rng_enc = make_rng(0x40);
1441 let ct = initiator.encrypt(b"AES128 test", &mut rng_enc).unwrap();
1442 let pt = responder.decrypt(&ct).unwrap();
1443 assert_eq!(pt, b"AES128 test");
1444 }
1445
1446 fn handshake_pair_with_mtu(mtu: Option<u32>) -> (LinkEngine, LinkEngine) {
1449 let mut rng_id = make_rng(0x01);
1450 let dest_sig_prv = Ed25519PrivateKey::generate(&mut rng_id);
1451 let dest_sig_pub_bytes = dest_sig_prv.public_key().public_bytes();
1452 let dest_hash = [0xDD; 16];
1453
1454 let mut rng_init = make_rng(0x10);
1455 let (mut initiator, request_data) = LinkEngine::new_initiator(
1456 &dest_hash,
1457 1,
1458 LinkMode::Aes256Cbc,
1459 mtu,
1460 100.0,
1461 &mut rng_init,
1462 );
1463 let mut hashable = vec![0x00, 0x00];
1464 hashable.extend_from_slice(&dest_hash);
1465 hashable.push(0x00);
1466 hashable.extend_from_slice(&request_data);
1467 initiator.set_link_id_from_hashable(&hashable, request_data.len());
1468
1469 let mut rng_resp = make_rng(0x20);
1470 let (responder, lrproof_data) = LinkEngine::new_responder(
1471 &dest_sig_prv,
1472 &dest_sig_pub_bytes,
1473 &request_data,
1474 &hashable,
1475 &dest_hash,
1476 1,
1477 100.5,
1478 &mut rng_resp,
1479 )
1480 .unwrap();
1481
1482 let mut rng_lrrtt = make_rng(0x30);
1483 initiator
1484 .handle_lrproof(&lrproof_data, &dest_sig_pub_bytes, 100.8, &mut rng_lrrtt)
1485 .unwrap();
1486 (initiator, responder)
1487 }
1488
1489 fn raw_rtt_payload(rtt: f64) -> Vec<u8> {
1490 let mut data = vec![0xcb];
1491 data.extend_from_slice(&rtt.to_be_bytes());
1492 data
1493 }
1494
1495 #[test]
1496 fn compute_mdu_keeps_values_for_regular_mtus() {
1497 assert_eq!(compute_mdu(500), 431);
1498 assert_eq!(compute_mdu(508), 431);
1499 assert_eq!(compute_mdu(1064), 991);
1500 assert_eq!(compute_mdu(8192), 8111);
1501 assert_eq!(compute_mdu(262_144), 262_063);
1502 assert_eq!(compute_mdu(LINK_MTU_MAX as usize), 2_097_071);
1503 }
1504
1505 #[test]
1506 fn compute_mdu_saturates_for_small_mtus() {
1507 for mtu in [0usize, 1, 50, 67, 68, 83] {
1508 assert_eq!(compute_mdu(mtu), 0, "mtu {mtu}");
1509 }
1510 assert_eq!(compute_mdu(LINK_MTU_MIN as usize), 15);
1511 }
1512
1513 #[test]
1514 fn effective_link_mtu_is_clamped_and_mdu_monotone() {
1515 assert_eq!(effective_link_mtu(None), MTU as u32);
1516 assert_eq!(effective_link_mtu(Some(0)), MTU as u32);
1517 assert_eq!(effective_link_mtu(Some(1)), LINK_MTU_MIN);
1518 assert_eq!(effective_link_mtu(Some(50)), LINK_MTU_MIN);
1519 assert_eq!(effective_link_mtu(Some(83)), LINK_MTU_MIN);
1520 assert_eq!(effective_link_mtu(Some(84)), 84);
1521 assert_eq!(effective_link_mtu(Some(500)), 500);
1522 assert_eq!(effective_link_mtu(Some(LINK_MTU_MAX)), LINK_MTU_MAX);
1523 assert_eq!(effective_link_mtu(Some(u32::MAX)), LINK_MTU_MAX);
1524
1525 let mut prev = 0usize;
1526 for mtu in [1u32, 50, 83, 84, 500, LINK_MTU_MAX] {
1527 let mdu = compute_mdu(effective_link_mtu(Some(mtu)) as usize);
1528 assert!(mdu >= 15, "mtu {mtu} gave mdu {mdu}");
1529 assert!(mdu >= prev, "mdu not monotone at mtu {mtu}");
1530 prev = mdu;
1531 }
1532 }
1533
1534 #[test]
1535 fn small_signalled_mtu_is_raised_to_minimum_on_both_sides() {
1536 let (initiator, mut responder) = handshake_pair_with_mtu(Some(70));
1537 assert_eq!(responder.mtu(), LINK_MTU_MIN);
1538 assert_eq!(responder.mdu(), 15);
1539 assert_eq!(initiator.mtu(), LINK_MTU_MIN);
1540 assert_eq!(initiator.mdu(), 15);
1541 assert_eq!(initiator.state(), LinkState::Active);
1542
1543 let mut rng = make_rng(0x40);
1544 let lrrtt = initiator.encrypt(&pack_rtt(0.3), &mut rng).unwrap();
1545 responder.handle_lrrtt(&lrrtt, 101.0).unwrap();
1546 assert_eq!(responder.state(), LinkState::Active);
1547
1548 let ct = initiator.encrypt(b"hello", &mut rng).unwrap();
1549 assert_eq!(responder.decrypt(&ct).unwrap(), b"hello");
1550 }
1551
1552 #[test]
1553 fn signalled_mtu_edge_values_do_not_panic() {
1554 for mtu in [0u32, 1, 50, 83, 84, 500, LINK_MTU_MAX] {
1555 let (initiator, responder) = handshake_pair_with_mtu(Some(mtu));
1556 assert!(responder.mtu() >= LINK_MTU_MIN);
1557 assert!(responder.mdu() >= 15);
1558 assert_eq!(initiator.mtu(), responder.mtu());
1559 assert_eq!(initiator.mdu(), responder.mdu());
1560 }
1561 }
1562
1563 #[test]
1564 fn non_finite_or_negative_lrrtt_is_rejected() {
1565 for bad in [f64::NAN, f64::INFINITY, f64::NEG_INFINITY, -1.0] {
1566 let (initiator, mut responder) = handshake_pair_with_mtu(Some(500));
1567 let mut rng = make_rng(0x40);
1568 let lrrtt = initiator.encrypt(&raw_rtt_payload(bad), &mut rng).unwrap();
1569 assert!(
1570 matches!(
1571 responder.handle_lrrtt(&lrrtt, 101.0),
1572 Err(LinkError::InvalidData)
1573 ),
1574 "rtt {bad}"
1575 );
1576 assert_eq!(responder.state(), LinkState::Handshake);
1577 assert_eq!(responder.rtt(), None);
1578 assert!(responder.keepalive_interval.is_finite());
1579 assert!(responder.stale_time.is_finite());
1580
1581 let deadline = responder.request_time + responder.establishment_timeout;
1583 let actions = responder.tick(deadline + 1.0);
1584 assert_eq!(responder.state(), LinkState::Closed);
1585 assert_eq!(actions.len(), 1);
1586 }
1587 }
1588
1589 #[test]
1590 fn valid_lrrtt_updates_rtt_and_timers() {
1591 let (initiator, mut responder) = handshake_pair_with_mtu(Some(500));
1592 let mut rng = make_rng(0x40);
1593 let lrrtt = initiator.encrypt(&pack_rtt(0.9), &mut rng).unwrap();
1595 responder.handle_lrrtt(&lrrtt, 101.0).unwrap();
1596 assert_eq!(responder.state(), LinkState::Active);
1597 assert_eq!(responder.rtt(), Some(0.9));
1598 assert_eq!(responder.keepalive_interval, compute_keepalive(0.9));
1599 assert_eq!(
1600 responder.stale_time,
1601 compute_stale_time(compute_keepalive(0.9))
1602 );
1603 }
1604
1605 #[test]
1606 fn non_finite_rtt_keeps_timers_finite_and_link_goes_stale() {
1607 let (_, mut responder, _) = active_link_pair();
1608 for bad in [f64::NAN, f64::INFINITY, f64::NEG_INFINITY] {
1609 responder.set_rtt(bad);
1610 assert!(responder.keepalive_interval.is_finite(), "rtt {bad}");
1611 assert!(responder.stale_time.is_finite(), "rtt {bad}");
1612 assert!(responder.stale_time <= LINK_KEEPALIVE_MAX * 2.0);
1613 }
1614
1615 let stale_time = responder.stale_time;
1616 responder.tick(101.0 + stale_time + 1.0);
1617 assert_eq!(responder.state(), LinkState::Stale);
1618 }
1619}