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, MTU,
16};
17
18pub use types::{LinkAction, LinkError, LinkId, LinkMode, LinkState, TeardownReason};
19
20use crypto::{create_session_token, link_decrypt, link_encrypt};
21use handshake::{
22 build_linkrequest_data, compute_link_id, pack_rtt, parse_linkrequest_data,
23 perform_key_exchange, unpack_rtt, validate_lrproof,
24};
25use keepalive::{
26 compute_establishment_timeout, compute_keepalive, compute_stale_time, is_establishment_timeout,
27 should_go_stale, should_send_keepalive,
28};
29
30pub struct LinkEngine {
35 link_id: LinkId,
36 state: LinkState,
37 is_initiator: bool,
38 mode: LinkMode,
39
40 prv: X25519PrivateKey,
42
43 peer_pub_bytes: Option<[u8; 32]>,
45 peer_sig_pub_bytes: Option<[u8; 32]>,
46
47 derived_key: Option<Vec<u8>>,
49 token: Option<Token>,
50
51 request_time: f64,
53 activated_at: Option<f64>,
54 last_inbound: f64,
55 last_outbound: f64,
56 last_keepalive: f64,
57 last_proof: f64,
58 rtt: Option<f64>,
59 keepalive_interval: f64,
60 stale_time: f64,
61 establishment_timeout: f64,
62
63 remote_identity: Option<([u8; 16], [u8; 64])>,
65 destination_hash: [u8; 16],
66
67 mtu: u32,
69 mdu: usize,
70}
71
72impl LinkEngine {
73 pub fn new_initiator(
78 dest_hash: &[u8; 16],
79 hops: u8,
80 mode: LinkMode,
81 mtu: Option<u32>,
82 now: f64,
83 rng: &mut dyn Rng,
84 ) -> (Self, Vec<u8>) {
85 let prv = X25519PrivateKey::generate(rng);
86 let pub_bytes = prv.public_key().public_bytes();
87 let sig_prv = Ed25519PrivateKey::generate(rng);
88 let sig_pub_bytes = sig_prv.public_key().public_bytes();
89
90 let request_data = build_linkrequest_data(&pub_bytes, &sig_pub_bytes, mtu, mode);
91
92 let link_mtu = mtu.unwrap_or(MTU as u32);
93
94 let engine = LinkEngine {
95 link_id: [0u8; 16], state: LinkState::Pending,
97 is_initiator: true,
98 mode,
99 prv,
100 peer_pub_bytes: None,
101 peer_sig_pub_bytes: None,
102 derived_key: None,
103 token: None,
104 request_time: now,
105 activated_at: None,
106 last_inbound: now,
107 last_outbound: now,
108 last_keepalive: now,
109 last_proof: 0.0,
110 rtt: None,
111 keepalive_interval: LINK_KEEPALIVE_MAX,
112 stale_time: LINK_KEEPALIVE_MAX * 2.0,
113 establishment_timeout: compute_establishment_timeout(
114 LINK_ESTABLISHMENT_TIMEOUT_PER_HOP,
115 hops,
116 ),
117 remote_identity: None,
118 destination_hash: *dest_hash,
119 mtu: link_mtu,
120 mdu: compute_mdu(link_mtu as usize),
121 };
122
123 (engine, request_data)
124 }
125
126 pub fn set_link_id_from_hashable(&mut self, hashable_part: &[u8], data_len: usize) {
131 let extra = data_len.saturating_sub(LINK_ECPUBSIZE);
132 self.link_id = compute_link_id(hashable_part, extra);
133 }
134
135 #[allow(clippy::too_many_arguments)]
140 pub fn new_responder(
141 owner_sig_prv: &Ed25519PrivateKey,
142 owner_sig_pub_bytes: &[u8; 32],
143 linkrequest_data: &[u8],
144 hashable_part: &[u8],
145 dest_hash: &[u8; 16],
146 hops: u8,
147 now: f64,
148 rng: &mut dyn Rng,
149 ) -> Result<(Self, Vec<u8>), LinkError> {
150 let (peer_pub, peer_sig_pub, peer_mtu, mode) = parse_linkrequest_data(linkrequest_data)?;
151
152 let extra = linkrequest_data.len().saturating_sub(LINK_ECPUBSIZE);
153 let link_id = compute_link_id(hashable_part, extra);
154
155 let prv = X25519PrivateKey::generate(rng);
157 let pub_bytes = prv.public_key().public_bytes();
158 let sig_pub_bytes = *owner_sig_pub_bytes;
159
160 let derived_key = perform_key_exchange(&prv, &peer_pub, &link_id, mode)?;
162 let token = create_session_token(&derived_key)?;
163
164 let link_mtu = peer_mtu.unwrap_or(MTU as u32);
165
166 let lrproof_data = handshake::build_lrproof(
168 &link_id,
169 &pub_bytes,
170 &sig_pub_bytes,
171 owner_sig_prv,
172 peer_mtu,
173 mode,
174 );
175
176 let engine = LinkEngine {
177 link_id,
178 state: LinkState::Handshake,
179 is_initiator: false,
180 mode,
181 prv,
182 peer_pub_bytes: Some(peer_pub),
183 peer_sig_pub_bytes: Some(peer_sig_pub),
184 derived_key: Some(derived_key),
185 token: Some(token),
186 request_time: now,
187 activated_at: None,
188 last_inbound: now,
189 last_outbound: now,
190 last_keepalive: now,
191 last_proof: 0.0,
192 rtt: None,
193 keepalive_interval: LINK_KEEPALIVE_MAX,
194 stale_time: LINK_KEEPALIVE_MAX * 2.0,
195 establishment_timeout: compute_establishment_timeout(
196 LINK_ESTABLISHMENT_TIMEOUT_PER_HOP,
197 hops,
198 ),
199 remote_identity: None,
200 destination_hash: *dest_hash,
201 mtu: link_mtu,
202 mdu: compute_mdu(link_mtu as usize),
203 };
204
205 Ok((engine, lrproof_data))
206 }
207
208 pub fn handle_lrproof(
213 &mut self,
214 proof_data: &[u8],
215 peer_sig_pub_bytes: &[u8; 32],
216 now: f64,
217 rng: &mut dyn Rng,
218 ) -> Result<(Vec<u8>, Vec<LinkAction>), LinkError> {
219 if self.state != LinkState::Pending || !self.is_initiator {
220 return Err(LinkError::InvalidState);
221 }
222
223 let peer_sig_pub = Ed25519PublicKey::from_bytes(peer_sig_pub_bytes);
224
225 let (peer_pub, confirmed_mtu, confirmed_mode) =
226 validate_lrproof(proof_data, &self.link_id, &peer_sig_pub, peer_sig_pub_bytes)?;
227
228 if confirmed_mode != self.mode {
229 return Err(LinkError::UnsupportedMode);
230 }
231
232 self.peer_pub_bytes = Some(peer_pub);
233 self.peer_sig_pub_bytes = Some(*peer_sig_pub_bytes);
234
235 let derived_key = perform_key_exchange(&self.prv, &peer_pub, &self.link_id, self.mode)?;
237 let token = create_session_token(&derived_key)?;
238
239 self.derived_key = Some(derived_key);
240 self.token = Some(token);
241
242 if let Some(mtu) = confirmed_mtu {
244 self.mtu = mtu;
245 self.mdu = compute_mdu(mtu as usize);
246 }
247
248 let rtt = now - self.request_time;
250 self.rtt = Some(rtt);
251 self.state = LinkState::Active;
252 self.activated_at = Some(now);
253 self.last_inbound = now;
254 self.update_keepalive();
255
256 let rtt_packed = pack_rtt(rtt);
258 let rtt_encrypted = self.encrypt(&rtt_packed, rng)?;
259
260 let actions = vec![
261 LinkAction::StateChanged {
262 link_id: self.link_id,
263 new_state: LinkState::Active,
264 reason: None,
265 },
266 LinkAction::LinkEstablished {
267 link_id: self.link_id,
268 rtt,
269 is_initiator: true,
270 },
271 ];
272
273 Ok((rtt_encrypted, actions))
274 }
275
276 pub fn handle_lrrtt(
280 &mut self,
281 encrypted_data: &[u8],
282 now: f64,
283 ) -> Result<Vec<LinkAction>, LinkError> {
284 if self.state != LinkState::Handshake || self.is_initiator {
285 return Err(LinkError::InvalidState);
286 }
287
288 let plaintext = self.decrypt(encrypted_data)?;
289 let initiator_rtt = unpack_rtt(&plaintext).ok_or(LinkError::InvalidData)?;
290
291 let measured_rtt = now - self.request_time;
292 let rtt = if measured_rtt > initiator_rtt {
293 measured_rtt
294 } else {
295 initiator_rtt
296 };
297
298 self.rtt = Some(rtt);
299 self.state = LinkState::Active;
300 self.activated_at = Some(now);
301 self.last_inbound = now;
302 self.update_keepalive();
303
304 let actions = vec![
305 LinkAction::StateChanged {
306 link_id: self.link_id,
307 new_state: LinkState::Active,
308 reason: None,
309 },
310 LinkAction::LinkEstablished {
311 link_id: self.link_id,
312 rtt,
313 is_initiator: false,
314 },
315 ];
316
317 Ok(actions)
318 }
319
320 pub fn encrypt(&self, plaintext: &[u8], rng: &mut dyn Rng) -> Result<Vec<u8>, LinkError> {
322 let token = self.token.as_ref().ok_or(LinkError::NoSessionKey)?;
323 Ok(link_encrypt(token, plaintext, rng))
324 }
325
326 pub fn decrypt(&self, ciphertext: &[u8]) -> Result<Vec<u8>, LinkError> {
328 let token = self.token.as_ref().ok_or(LinkError::NoSessionKey)?;
329 link_decrypt(token, ciphertext)
330 }
331
332 pub fn build_identify(
334 &self,
335 identity: &rns_crypto::identity::Identity,
336 rng: &mut dyn Rng,
337 ) -> Result<Vec<u8>, LinkError> {
338 if self.state != LinkState::Active {
339 return Err(LinkError::InvalidState);
340 }
341 let plaintext = identify::build_identify_data(identity, &self.link_id)?;
342 self.encrypt(&plaintext, rng)
343 }
344
345 pub fn handle_identify(&mut self, encrypted_data: &[u8]) -> Result<Vec<LinkAction>, LinkError> {
349 if self.state != LinkState::Active || self.is_initiator {
350 return Err(LinkError::InvalidState);
351 }
352
353 let plaintext = self.decrypt(encrypted_data)?;
354 let (identity_hash, public_key) =
355 identify::validate_identify_data(&plaintext, &self.link_id)?;
356 self.remote_identity = Some((identity_hash, public_key));
357
358 Ok(alloc::vec![LinkAction::RemoteIdentified {
359 link_id: self.link_id,
360 identity_hash,
361 public_key,
362 }])
363 }
364
365 pub fn record_inbound(&mut self, now: f64) -> Vec<LinkAction> {
369 self.last_inbound = now;
370 if self.state == LinkState::Stale {
371 self.state = LinkState::Active;
372 return alloc::vec![LinkAction::StateChanged {
373 link_id: self.link_id,
374 new_state: LinkState::Active,
375 reason: None,
376 }];
377 }
378 Vec::new()
379 }
380
381 pub fn record_proof(&mut self, now: f64) {
383 self.last_proof = now;
384 }
385
386 pub fn record_outbound(&mut self, now: f64, is_keepalive: bool) {
388 self.last_outbound = now;
389 if is_keepalive {
390 self.last_keepalive = now;
391 }
392 }
393
394 pub fn tick(&mut self, now: f64) -> Vec<LinkAction> {
396 let mut actions = Vec::new();
397
398 match self.state {
399 LinkState::Pending | LinkState::Handshake => {
400 if is_establishment_timeout(self.request_time, self.establishment_timeout, now) {
401 self.state = LinkState::Closed;
402 actions.push(LinkAction::StateChanged {
403 link_id: self.link_id,
404 new_state: LinkState::Closed,
405 reason: Some(TeardownReason::Timeout),
406 });
407 }
408 }
409 LinkState::Active => {
410 let activated = self.activated_at.unwrap_or(0.0);
411 let last_inbound = self.last_inbound.max(self.last_proof).max(activated);
413
414 if should_go_stale(last_inbound, self.stale_time, now) {
415 self.state = LinkState::Stale;
416 actions.push(LinkAction::StateChanged {
417 link_id: self.link_id,
418 new_state: LinkState::Stale,
419 reason: None,
420 });
421 }
422 }
423 LinkState::Stale => {
424 self.state = LinkState::Closed;
426 actions.push(LinkAction::StateChanged {
427 link_id: self.link_id,
428 new_state: LinkState::Closed,
429 reason: Some(TeardownReason::Timeout),
430 });
431 }
432 LinkState::Closed => {}
433 }
434
435 actions
436 }
437
438 pub fn needs_keepalive(&self, now: f64) -> bool {
440 if self.state != LinkState::Active {
441 return false;
442 }
443 let activated = self.activated_at.unwrap_or(0.0);
444 let last_inbound = self.last_inbound.max(self.last_proof).max(activated);
445
446 if now < last_inbound + self.keepalive_interval {
448 return false;
449 }
450
451 should_send_keepalive(self.last_keepalive, self.keepalive_interval, now)
452 }
453
454 pub fn teardown(&mut self) -> Vec<LinkAction> {
456 if self.state == LinkState::Closed {
457 return Vec::new();
458 }
459 self.state = LinkState::Closed;
460 let reason = if self.is_initiator {
461 TeardownReason::InitiatorClosed
462 } else {
463 TeardownReason::DestinationClosed
464 };
465 alloc::vec![LinkAction::StateChanged {
466 link_id: self.link_id,
467 new_state: LinkState::Closed,
468 reason: Some(reason),
469 }]
470 }
471
472 pub fn handle_teardown(&mut self) -> Vec<LinkAction> {
474 if self.state == LinkState::Closed {
475 return Vec::new();
476 }
477 self.state = LinkState::Closed;
478 let reason = if self.is_initiator {
479 TeardownReason::DestinationClosed
480 } else {
481 TeardownReason::InitiatorClosed
482 };
483 alloc::vec![LinkAction::StateChanged {
484 link_id: self.link_id,
485 new_state: LinkState::Closed,
486 reason: Some(reason),
487 }]
488 }
489
490 pub fn link_id(&self) -> &LinkId {
493 &self.link_id
494 }
495
496 pub fn state(&self) -> LinkState {
497 self.state
498 }
499
500 pub fn rtt(&self) -> Option<f64> {
501 self.rtt
502 }
503
504 pub fn mdu(&self) -> usize {
505 self.mdu
506 }
507
508 pub fn is_initiator(&self) -> bool {
509 self.is_initiator
510 }
511
512 pub fn mode(&self) -> LinkMode {
513 self.mode
514 }
515
516 pub fn remote_identity(&self) -> Option<&([u8; 16], [u8; 64])> {
517 self.remote_identity.as_ref()
518 }
519
520 pub fn destination_hash(&self) -> &[u8; 16] {
521 &self.destination_hash
522 }
523
524 pub fn derived_key(&self) -> Option<&[u8]> {
526 self.derived_key.as_deref()
527 }
528
529 pub fn keepalive_interval(&self) -> f64 {
530 self.keepalive_interval
531 }
532
533 pub fn set_rtt(&mut self, rtt: f64) {
536 self.rtt = Some(rtt);
537 self.update_keepalive();
538 }
539
540 pub fn set_mtu(&mut self, mtu: u32) {
542 self.mtu = mtu;
543 self.mdu = compute_mdu(mtu as usize);
544 }
545
546 fn update_keepalive(&mut self) {
549 if let Some(rtt) = self.rtt {
550 self.keepalive_interval = compute_keepalive(rtt);
551 self.stale_time = compute_stale_time(self.keepalive_interval);
552 }
553 }
554}
555
556fn compute_mdu(mtu: usize) -> usize {
560 use crate::constants::{AES128_BLOCKSIZE, HEADER_MINSIZE, IFAC_MIN_SIZE, TOKEN_OVERHEAD};
561 let numerator = mtu.saturating_sub(IFAC_MIN_SIZE + HEADER_MINSIZE + TOKEN_OVERHEAD);
562 (numerator / AES128_BLOCKSIZE) * AES128_BLOCKSIZE - 1
563}
564
565#[cfg(test)]
566mod tests {
567 use super::*;
568 use crate::constants::LINK_MDU;
569 use rns_crypto::FixedRng;
570
571 fn make_rng(seed: u8) -> FixedRng {
572 FixedRng::new(&[seed; 128])
573 }
574
575 #[test]
576 fn test_compute_mdu_default() {
577 assert_eq!(compute_mdu(500), LINK_MDU);
578 }
579
580 #[test]
581 fn test_full_handshake() {
582 let mut rng_id = make_rng(0x01);
584 let dest_sig_prv = Ed25519PrivateKey::generate(&mut rng_id);
585 let dest_sig_pub_bytes = dest_sig_prv.public_key().public_bytes();
586
587 let dest_hash = [0xDD; 16];
588 let mode = LinkMode::Aes256Cbc;
589
590 let mut rng_init = make_rng(0x10);
592 let (mut initiator, request_data) =
593 LinkEngine::new_initiator(&dest_hash, 1, mode, Some(500), 100.0, &mut rng_init);
594 assert_eq!(initiator.state(), LinkState::Pending);
595
596 let mut hashable = Vec::new();
599 hashable.push(0x00); hashable.push(0x00); hashable.extend_from_slice(&dest_hash);
602 hashable.push(0x00); hashable.extend_from_slice(&request_data);
604
605 initiator.set_link_id_from_hashable(&hashable, request_data.len());
606 assert_ne!(initiator.link_id(), &[0u8; 16]);
607
608 let mut rng_resp = make_rng(0x20);
610 let (mut responder, lrproof_data) = LinkEngine::new_responder(
611 &dest_sig_prv,
612 &dest_sig_pub_bytes,
613 &request_data,
614 &hashable,
615 &dest_hash,
616 1,
617 100.5,
618 &mut rng_resp,
619 )
620 .unwrap();
621 assert_eq!(responder.state(), LinkState::Handshake);
622 assert_eq!(responder.link_id(), initiator.link_id());
623
624 let mut rng_lrrtt = make_rng(0x30);
626 let (lrrtt_encrypted, actions) = initiator
627 .handle_lrproof(&lrproof_data, &dest_sig_pub_bytes, 100.8, &mut rng_lrrtt)
628 .unwrap();
629 assert_eq!(initiator.state(), LinkState::Active);
630 assert!(initiator.rtt().is_some());
631 assert_eq!(actions.len(), 2); let actions = responder.handle_lrrtt(&lrrtt_encrypted, 101.0).unwrap();
635 assert_eq!(responder.state(), LinkState::Active);
636 assert!(responder.rtt().is_some());
637 assert_eq!(actions.len(), 2);
638 }
639
640 #[test]
641 fn test_encrypt_decrypt_after_handshake() {
642 let mut rng_id = make_rng(0x01);
643 let dest_sig_prv = Ed25519PrivateKey::generate(&mut rng_id);
644 let dest_sig_pub_bytes = dest_sig_prv.public_key().public_bytes();
645 let dest_hash = [0xDD; 16];
646
647 let mut rng_init = make_rng(0x10);
648 let (mut initiator, request_data) = LinkEngine::new_initiator(
649 &dest_hash,
650 1,
651 LinkMode::Aes256Cbc,
652 Some(500),
653 100.0,
654 &mut rng_init,
655 );
656 let mut hashable = Vec::new();
657 hashable.push(0x00);
658 hashable.push(0x00);
659 hashable.extend_from_slice(&dest_hash);
660 hashable.push(0x00);
661 hashable.extend_from_slice(&request_data);
662 initiator.set_link_id_from_hashable(&hashable, request_data.len());
663
664 let mut rng_resp = make_rng(0x20);
665 let (mut responder, lrproof_data) = LinkEngine::new_responder(
666 &dest_sig_prv,
667 &dest_sig_pub_bytes,
668 &request_data,
669 &hashable,
670 &dest_hash,
671 1,
672 100.5,
673 &mut rng_resp,
674 )
675 .unwrap();
676
677 let mut rng_lrrtt = make_rng(0x30);
678 let (lrrtt_encrypted, _) = initiator
679 .handle_lrproof(&lrproof_data, &dest_sig_pub_bytes, 100.8, &mut rng_lrrtt)
680 .unwrap();
681 responder.handle_lrrtt(&lrrtt_encrypted, 101.0).unwrap();
682
683 let mut rng_enc = make_rng(0x40);
685 let plaintext = b"Hello over encrypted link!";
686 let ciphertext = initiator.encrypt(plaintext, &mut rng_enc).unwrap();
687 let decrypted = responder.decrypt(&ciphertext).unwrap();
688 assert_eq!(decrypted, plaintext);
689
690 let mut rng_enc2 = make_rng(0x50);
692 let ciphertext2 = responder.encrypt(b"Reply!", &mut rng_enc2).unwrap();
693 let decrypted2 = initiator.decrypt(&ciphertext2).unwrap();
694 assert_eq!(decrypted2, b"Reply!");
695 }
696
697 #[test]
698 fn test_tick_establishment_timeout() {
699 let mut rng = make_rng(0x10);
700 let dest_hash = [0xDD; 16];
701 let (mut engine, _) =
702 LinkEngine::new_initiator(&dest_hash, 1, LinkMode::Aes256Cbc, None, 100.0, &mut rng);
703 let actions = engine.tick(110.0);
707 assert!(actions.is_empty());
708
709 let actions = engine.tick(113.0);
711 assert_eq!(actions.len(), 1);
712 assert_eq!(engine.state(), LinkState::Closed);
713 }
714
715 #[test]
716 fn test_tick_stale_and_close() {
717 let mut rng_id = make_rng(0x01);
718 let dest_sig_prv = Ed25519PrivateKey::generate(&mut rng_id);
719 let dest_sig_pub_bytes = dest_sig_prv.public_key().public_bytes();
720 let dest_hash = [0xDD; 16];
721
722 let mut rng_init = make_rng(0x10);
723 let (mut initiator, request_data) = LinkEngine::new_initiator(
724 &dest_hash,
725 1,
726 LinkMode::Aes256Cbc,
727 Some(500),
728 100.0,
729 &mut rng_init,
730 );
731 let mut hashable = Vec::new();
732 hashable.push(0x00);
733 hashable.push(0x00);
734 hashable.extend_from_slice(&dest_hash);
735 hashable.push(0x00);
736 hashable.extend_from_slice(&request_data);
737 initiator.set_link_id_from_hashable(&hashable, request_data.len());
738
739 let mut rng_resp = make_rng(0x20);
740 let (_, lrproof_data) = LinkEngine::new_responder(
741 &dest_sig_prv,
742 &dest_sig_pub_bytes,
743 &request_data,
744 &hashable,
745 &dest_hash,
746 1,
747 100.5,
748 &mut rng_resp,
749 )
750 .unwrap();
751
752 let mut rng_lrrtt = make_rng(0x30);
753 initiator
754 .handle_lrproof(&lrproof_data, &dest_sig_pub_bytes, 100.8, &mut rng_lrrtt)
755 .unwrap();
756 assert_eq!(initiator.state(), LinkState::Active);
757
758 let stale_time = initiator.stale_time;
760 let actions = initiator.tick(100.8 + stale_time + 1.0);
761 assert_eq!(initiator.state(), LinkState::Stale);
762 assert_eq!(actions.len(), 1);
763
764 let actions = initiator.tick(100.8 + stale_time + 2.0);
766 assert_eq!(initiator.state(), LinkState::Closed);
767 assert_eq!(actions.len(), 1);
768 }
769
770 #[test]
771 fn test_needs_keepalive() {
772 let mut rng_id = make_rng(0x01);
773 let dest_sig_prv = Ed25519PrivateKey::generate(&mut rng_id);
774 let dest_sig_pub_bytes = dest_sig_prv.public_key().public_bytes();
775 let dest_hash = [0xDD; 16];
776
777 let mut rng_init = make_rng(0x10);
778 let (mut initiator, request_data) = LinkEngine::new_initiator(
779 &dest_hash,
780 1,
781 LinkMode::Aes256Cbc,
782 Some(500),
783 100.0,
784 &mut rng_init,
785 );
786 let mut hashable = Vec::new();
787 hashable.push(0x00);
788 hashable.push(0x00);
789 hashable.extend_from_slice(&dest_hash);
790 hashable.push(0x00);
791 hashable.extend_from_slice(&request_data);
792 initiator.set_link_id_from_hashable(&hashable, request_data.len());
793
794 let mut rng_resp = make_rng(0x20);
795 let (_, lrproof_data) = LinkEngine::new_responder(
796 &dest_sig_prv,
797 &dest_sig_pub_bytes,
798 &request_data,
799 &hashable,
800 &dest_hash,
801 1,
802 100.5,
803 &mut rng_resp,
804 )
805 .unwrap();
806
807 let mut rng_lrrtt = make_rng(0x30);
808 initiator
809 .handle_lrproof(&lrproof_data, &dest_sig_pub_bytes, 100.8, &mut rng_lrrtt)
810 .unwrap();
811
812 let ka = initiator.keepalive_interval();
813 assert!(!initiator.needs_keepalive(100.8 + ka - 1.0));
815 assert!(initiator.needs_keepalive(100.8 + ka + 1.0));
817 }
818
819 #[test]
820 fn test_needs_keepalive_responder() {
821 let mut rng_id = make_rng(0x01);
822 let dest_sig_prv = Ed25519PrivateKey::generate(&mut rng_id);
823 let dest_sig_pub_bytes = dest_sig_prv.public_key().public_bytes();
824 let dest_hash = [0xDD; 16];
825
826 let mut rng_init = make_rng(0x10);
827 let (mut initiator, request_data) = LinkEngine::new_initiator(
828 &dest_hash,
829 1,
830 LinkMode::Aes256Cbc,
831 Some(500),
832 100.0,
833 &mut rng_init,
834 );
835 let mut hashable = Vec::new();
836 hashable.push(0x00);
837 hashable.push(0x00);
838 hashable.extend_from_slice(&dest_hash);
839 hashable.push(0x00);
840 hashable.extend_from_slice(&request_data);
841 initiator.set_link_id_from_hashable(&hashable, request_data.len());
842
843 let mut rng_resp = make_rng(0x20);
844 let (mut responder, lrproof_data) = LinkEngine::new_responder(
845 &dest_sig_prv,
846 &dest_sig_pub_bytes,
847 &request_data,
848 &hashable,
849 &dest_hash,
850 1,
851 100.5,
852 &mut rng_resp,
853 )
854 .unwrap();
855
856 let mut rng_lrrtt = make_rng(0x30);
857 let (lrrtt_encrypted, _) = initiator
858 .handle_lrproof(&lrproof_data, &dest_sig_pub_bytes, 100.8, &mut rng_lrrtt)
859 .unwrap();
860 responder.handle_lrrtt(&lrrtt_encrypted, 101.0).unwrap();
861
862 let ka = responder.keepalive_interval();
863 assert!(!responder.needs_keepalive(101.0 + ka - 1.0));
865 assert!(responder.needs_keepalive(101.0 + ka + 1.0));
866 }
867
868 #[test]
869 fn test_teardown() {
870 let mut rng = make_rng(0x10);
871 let (mut engine, _) =
872 LinkEngine::new_initiator(&[0xDD; 16], 1, LinkMode::Aes256Cbc, None, 100.0, &mut rng);
873 let actions = engine.teardown();
874 assert_eq!(engine.state(), LinkState::Closed);
875 assert_eq!(actions.len(), 1);
876
877 let actions = engine.teardown();
879 assert!(actions.is_empty());
880 }
881
882 #[test]
883 fn test_handle_teardown() {
884 let mut rng = make_rng(0x10);
885 let (mut engine, _) =
886 LinkEngine::new_initiator(&[0xDD; 16], 1, LinkMode::Aes256Cbc, None, 100.0, &mut rng);
887 let actions = engine.handle_teardown();
888 assert_eq!(engine.state(), LinkState::Closed);
889 assert_eq!(actions.len(), 1);
890 match &actions[0] {
891 LinkAction::StateChanged { reason, .. } => {
892 assert_eq!(*reason, Some(TeardownReason::DestinationClosed));
893 }
894 _ => panic!("Expected StateChanged"),
895 }
896 }
897
898 #[test]
899 fn test_identify_over_link() {
900 let mut rng_id = make_rng(0x01);
901 let dest_sig_prv = Ed25519PrivateKey::generate(&mut rng_id);
902 let dest_sig_pub_bytes = dest_sig_prv.public_key().public_bytes();
903 let dest_hash = [0xDD; 16];
904
905 let mut rng_init = make_rng(0x10);
906 let (mut initiator, request_data) = LinkEngine::new_initiator(
907 &dest_hash,
908 1,
909 LinkMode::Aes256Cbc,
910 Some(500),
911 100.0,
912 &mut rng_init,
913 );
914 let mut hashable = Vec::new();
915 hashable.push(0x00);
916 hashable.push(0x00);
917 hashable.extend_from_slice(&dest_hash);
918 hashable.push(0x00);
919 hashable.extend_from_slice(&request_data);
920 initiator.set_link_id_from_hashable(&hashable, request_data.len());
921
922 let mut rng_resp = make_rng(0x20);
923 let (mut responder, lrproof_data) = LinkEngine::new_responder(
924 &dest_sig_prv,
925 &dest_sig_pub_bytes,
926 &request_data,
927 &hashable,
928 &dest_hash,
929 1,
930 100.5,
931 &mut rng_resp,
932 )
933 .unwrap();
934
935 let mut rng_lrrtt = make_rng(0x30);
936 let (lrrtt_encrypted, _) = initiator
937 .handle_lrproof(&lrproof_data, &dest_sig_pub_bytes, 100.8, &mut rng_lrrtt)
938 .unwrap();
939 responder.handle_lrrtt(&lrrtt_encrypted, 101.0).unwrap();
940
941 let mut rng_ident = make_rng(0x40);
943 let my_identity = rns_crypto::identity::Identity::new(&mut rng_ident);
944
945 let mut rng_enc = make_rng(0x50);
947 let identify_encrypted = initiator
948 .build_identify(&my_identity, &mut rng_enc)
949 .unwrap();
950
951 let actions = responder.handle_identify(&identify_encrypted).unwrap();
952 assert_eq!(actions.len(), 1);
953 match &actions[0] {
954 LinkAction::RemoteIdentified {
955 identity_hash,
956 public_key,
957 ..
958 } => {
959 assert_eq!(identity_hash, my_identity.hash());
960 assert_eq!(public_key, &my_identity.get_public_key().unwrap());
961 }
962 _ => panic!("Expected RemoteIdentified"),
963 }
964 }
965
966 #[test]
967 fn test_aes128_mode_handshake() {
968 let mut rng_id = make_rng(0x01);
969 let dest_sig_prv = Ed25519PrivateKey::generate(&mut rng_id);
970 let dest_sig_pub_bytes = dest_sig_prv.public_key().public_bytes();
971 let dest_hash = [0xDD; 16];
972
973 let mut rng_init = make_rng(0x10);
974 let (mut initiator, request_data) = LinkEngine::new_initiator(
975 &dest_hash,
976 1,
977 LinkMode::Aes128Cbc,
978 Some(500),
979 100.0,
980 &mut rng_init,
981 );
982 let mut hashable = Vec::new();
983 hashable.push(0x00);
984 hashable.push(0x00);
985 hashable.extend_from_slice(&dest_hash);
986 hashable.push(0x00);
987 hashable.extend_from_slice(&request_data);
988 initiator.set_link_id_from_hashable(&hashable, request_data.len());
989
990 let mut rng_resp = make_rng(0x20);
991 let (mut responder, lrproof_data) = LinkEngine::new_responder(
992 &dest_sig_prv,
993 &dest_sig_pub_bytes,
994 &request_data,
995 &hashable,
996 &dest_hash,
997 1,
998 100.5,
999 &mut rng_resp,
1000 )
1001 .unwrap();
1002
1003 let mut rng_lrrtt = make_rng(0x30);
1004 let (lrrtt_encrypted, _) = initiator
1005 .handle_lrproof(&lrproof_data, &dest_sig_pub_bytes, 100.8, &mut rng_lrrtt)
1006 .unwrap();
1007 responder.handle_lrrtt(&lrrtt_encrypted, 101.0).unwrap();
1008
1009 assert_eq!(initiator.state(), LinkState::Active);
1010 assert_eq!(responder.state(), LinkState::Active);
1011 assert_eq!(initiator.mode(), LinkMode::Aes128Cbc);
1012
1013 let mut rng_enc = make_rng(0x40);
1015 let ct = initiator.encrypt(b"AES128 test", &mut rng_enc).unwrap();
1016 let pt = responder.decrypt(&ct).unwrap();
1017 assert_eq!(pt, b"AES128 test");
1018 }
1019}