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 mtu(&self) -> u32 {
509 self.mtu
510 }
511
512 pub fn is_initiator(&self) -> bool {
513 self.is_initiator
514 }
515
516 pub fn mode(&self) -> LinkMode {
517 self.mode
518 }
519
520 pub fn remote_identity(&self) -> Option<&([u8; 16], [u8; 64])> {
521 self.remote_identity.as_ref()
522 }
523
524 pub fn destination_hash(&self) -> &[u8; 16] {
525 &self.destination_hash
526 }
527
528 pub fn derived_key(&self) -> Option<&[u8]> {
530 self.derived_key.as_deref()
531 }
532
533 pub fn keepalive_interval(&self) -> f64 {
534 self.keepalive_interval
535 }
536
537 pub fn set_rtt(&mut self, rtt: f64) {
540 self.rtt = Some(rtt);
541 self.update_keepalive();
542 }
543
544 pub fn set_mtu(&mut self, mtu: u32) {
546 self.mtu = mtu;
547 self.mdu = compute_mdu(mtu as usize);
548 }
549
550 fn update_keepalive(&mut self) {
553 if let Some(rtt) = self.rtt {
554 self.keepalive_interval = compute_keepalive(rtt);
555 self.stale_time = compute_stale_time(self.keepalive_interval);
556 }
557 }
558}
559
560fn compute_mdu(mtu: usize) -> usize {
564 use crate::constants::{AES128_BLOCKSIZE, HEADER_MINSIZE, IFAC_MIN_SIZE, TOKEN_OVERHEAD};
565 let numerator = mtu.saturating_sub(IFAC_MIN_SIZE + HEADER_MINSIZE + TOKEN_OVERHEAD);
566 (numerator / AES128_BLOCKSIZE) * AES128_BLOCKSIZE - 1
567}
568
569#[cfg(test)]
570mod tests {
571 use super::*;
572 use crate::constants::LINK_MDU;
573 use rns_crypto::FixedRng;
574
575 fn make_rng(seed: u8) -> FixedRng {
576 FixedRng::new(&[seed; 128])
577 }
578
579 #[test]
580 fn test_compute_mdu_default() {
581 assert_eq!(compute_mdu(500), LINK_MDU);
582 }
583
584 #[test]
585 fn test_full_handshake() {
586 let mut rng_id = make_rng(0x01);
588 let dest_sig_prv = Ed25519PrivateKey::generate(&mut rng_id);
589 let dest_sig_pub_bytes = dest_sig_prv.public_key().public_bytes();
590
591 let dest_hash = [0xDD; 16];
592 let mode = LinkMode::Aes256Cbc;
593
594 let mut rng_init = make_rng(0x10);
596 let (mut initiator, request_data) =
597 LinkEngine::new_initiator(&dest_hash, 1, mode, Some(500), 100.0, &mut rng_init);
598 assert_eq!(initiator.state(), LinkState::Pending);
599
600 let mut hashable = Vec::new();
603 hashable.push(0x00); hashable.push(0x00); hashable.extend_from_slice(&dest_hash);
606 hashable.push(0x00); hashable.extend_from_slice(&request_data);
608
609 initiator.set_link_id_from_hashable(&hashable, request_data.len());
610 assert_ne!(initiator.link_id(), &[0u8; 16]);
611
612 let mut rng_resp = make_rng(0x20);
614 let (mut responder, lrproof_data) = LinkEngine::new_responder(
615 &dest_sig_prv,
616 &dest_sig_pub_bytes,
617 &request_data,
618 &hashable,
619 &dest_hash,
620 1,
621 100.5,
622 &mut rng_resp,
623 )
624 .unwrap();
625 assert_eq!(responder.state(), LinkState::Handshake);
626 assert_eq!(responder.link_id(), initiator.link_id());
627
628 let mut rng_lrrtt = make_rng(0x30);
630 let (lrrtt_encrypted, actions) = initiator
631 .handle_lrproof(&lrproof_data, &dest_sig_pub_bytes, 100.8, &mut rng_lrrtt)
632 .unwrap();
633 assert_eq!(initiator.state(), LinkState::Active);
634 assert!(initiator.rtt().is_some());
635 assert_eq!(actions.len(), 2); let actions = responder.handle_lrrtt(&lrrtt_encrypted, 101.0).unwrap();
639 assert_eq!(responder.state(), LinkState::Active);
640 assert!(responder.rtt().is_some());
641 assert_eq!(actions.len(), 2);
642 }
643
644 #[test]
645 fn test_encrypt_decrypt_after_handshake() {
646 let mut rng_id = make_rng(0x01);
647 let dest_sig_prv = Ed25519PrivateKey::generate(&mut rng_id);
648 let dest_sig_pub_bytes = dest_sig_prv.public_key().public_bytes();
649 let dest_hash = [0xDD; 16];
650
651 let mut rng_init = make_rng(0x10);
652 let (mut initiator, request_data) = LinkEngine::new_initiator(
653 &dest_hash,
654 1,
655 LinkMode::Aes256Cbc,
656 Some(500),
657 100.0,
658 &mut rng_init,
659 );
660 let mut hashable = Vec::new();
661 hashable.push(0x00);
662 hashable.push(0x00);
663 hashable.extend_from_slice(&dest_hash);
664 hashable.push(0x00);
665 hashable.extend_from_slice(&request_data);
666 initiator.set_link_id_from_hashable(&hashable, request_data.len());
667
668 let mut rng_resp = make_rng(0x20);
669 let (mut responder, lrproof_data) = LinkEngine::new_responder(
670 &dest_sig_prv,
671 &dest_sig_pub_bytes,
672 &request_data,
673 &hashable,
674 &dest_hash,
675 1,
676 100.5,
677 &mut rng_resp,
678 )
679 .unwrap();
680
681 let mut rng_lrrtt = make_rng(0x30);
682 let (lrrtt_encrypted, _) = initiator
683 .handle_lrproof(&lrproof_data, &dest_sig_pub_bytes, 100.8, &mut rng_lrrtt)
684 .unwrap();
685 responder.handle_lrrtt(&lrrtt_encrypted, 101.0).unwrap();
686
687 let mut rng_enc = make_rng(0x40);
689 let plaintext = b"Hello over encrypted link!";
690 let ciphertext = initiator.encrypt(plaintext, &mut rng_enc).unwrap();
691 let decrypted = responder.decrypt(&ciphertext).unwrap();
692 assert_eq!(decrypted, plaintext);
693
694 let mut rng_enc2 = make_rng(0x50);
696 let ciphertext2 = responder.encrypt(b"Reply!", &mut rng_enc2).unwrap();
697 let decrypted2 = initiator.decrypt(&ciphertext2).unwrap();
698 assert_eq!(decrypted2, b"Reply!");
699 }
700
701 #[test]
702 fn test_tick_establishment_timeout() {
703 let mut rng = make_rng(0x10);
704 let dest_hash = [0xDD; 16];
705 let (mut engine, _) =
706 LinkEngine::new_initiator(&dest_hash, 1, LinkMode::Aes256Cbc, None, 100.0, &mut rng);
707 let actions = engine.tick(110.0);
711 assert!(actions.is_empty());
712
713 let actions = engine.tick(113.0);
715 assert_eq!(actions.len(), 1);
716 assert_eq!(engine.state(), LinkState::Closed);
717 }
718
719 #[test]
720 fn test_tick_stale_and_close() {
721 let mut rng_id = make_rng(0x01);
722 let dest_sig_prv = Ed25519PrivateKey::generate(&mut rng_id);
723 let dest_sig_pub_bytes = dest_sig_prv.public_key().public_bytes();
724 let dest_hash = [0xDD; 16];
725
726 let mut rng_init = make_rng(0x10);
727 let (mut initiator, request_data) = LinkEngine::new_initiator(
728 &dest_hash,
729 1,
730 LinkMode::Aes256Cbc,
731 Some(500),
732 100.0,
733 &mut rng_init,
734 );
735 let mut hashable = Vec::new();
736 hashable.push(0x00);
737 hashable.push(0x00);
738 hashable.extend_from_slice(&dest_hash);
739 hashable.push(0x00);
740 hashable.extend_from_slice(&request_data);
741 initiator.set_link_id_from_hashable(&hashable, request_data.len());
742
743 let mut rng_resp = make_rng(0x20);
744 let (_, lrproof_data) = LinkEngine::new_responder(
745 &dest_sig_prv,
746 &dest_sig_pub_bytes,
747 &request_data,
748 &hashable,
749 &dest_hash,
750 1,
751 100.5,
752 &mut rng_resp,
753 )
754 .unwrap();
755
756 let mut rng_lrrtt = make_rng(0x30);
757 initiator
758 .handle_lrproof(&lrproof_data, &dest_sig_pub_bytes, 100.8, &mut rng_lrrtt)
759 .unwrap();
760 assert_eq!(initiator.state(), LinkState::Active);
761
762 let stale_time = initiator.stale_time;
764 let actions = initiator.tick(100.8 + stale_time + 1.0);
765 assert_eq!(initiator.state(), LinkState::Stale);
766 assert_eq!(actions.len(), 1);
767
768 let actions = initiator.tick(100.8 + stale_time + 2.0);
770 assert_eq!(initiator.state(), LinkState::Closed);
771 assert_eq!(actions.len(), 1);
772 }
773
774 #[test]
775 fn test_needs_keepalive() {
776 let mut rng_id = make_rng(0x01);
777 let dest_sig_prv = Ed25519PrivateKey::generate(&mut rng_id);
778 let dest_sig_pub_bytes = dest_sig_prv.public_key().public_bytes();
779 let dest_hash = [0xDD; 16];
780
781 let mut rng_init = make_rng(0x10);
782 let (mut initiator, request_data) = LinkEngine::new_initiator(
783 &dest_hash,
784 1,
785 LinkMode::Aes256Cbc,
786 Some(500),
787 100.0,
788 &mut rng_init,
789 );
790 let mut hashable = Vec::new();
791 hashable.push(0x00);
792 hashable.push(0x00);
793 hashable.extend_from_slice(&dest_hash);
794 hashable.push(0x00);
795 hashable.extend_from_slice(&request_data);
796 initiator.set_link_id_from_hashable(&hashable, request_data.len());
797
798 let mut rng_resp = make_rng(0x20);
799 let (_, lrproof_data) = LinkEngine::new_responder(
800 &dest_sig_prv,
801 &dest_sig_pub_bytes,
802 &request_data,
803 &hashable,
804 &dest_hash,
805 1,
806 100.5,
807 &mut rng_resp,
808 )
809 .unwrap();
810
811 let mut rng_lrrtt = make_rng(0x30);
812 initiator
813 .handle_lrproof(&lrproof_data, &dest_sig_pub_bytes, 100.8, &mut rng_lrrtt)
814 .unwrap();
815
816 let ka = initiator.keepalive_interval();
817 assert!(!initiator.needs_keepalive(100.8 + ka - 1.0));
819 assert!(initiator.needs_keepalive(100.8 + ka + 1.0));
821 }
822
823 #[test]
824 fn test_needs_keepalive_responder() {
825 let mut rng_id = make_rng(0x01);
826 let dest_sig_prv = Ed25519PrivateKey::generate(&mut rng_id);
827 let dest_sig_pub_bytes = dest_sig_prv.public_key().public_bytes();
828 let dest_hash = [0xDD; 16];
829
830 let mut rng_init = make_rng(0x10);
831 let (mut initiator, request_data) = LinkEngine::new_initiator(
832 &dest_hash,
833 1,
834 LinkMode::Aes256Cbc,
835 Some(500),
836 100.0,
837 &mut rng_init,
838 );
839 let mut hashable = Vec::new();
840 hashable.push(0x00);
841 hashable.push(0x00);
842 hashable.extend_from_slice(&dest_hash);
843 hashable.push(0x00);
844 hashable.extend_from_slice(&request_data);
845 initiator.set_link_id_from_hashable(&hashable, request_data.len());
846
847 let mut rng_resp = make_rng(0x20);
848 let (mut responder, lrproof_data) = LinkEngine::new_responder(
849 &dest_sig_prv,
850 &dest_sig_pub_bytes,
851 &request_data,
852 &hashable,
853 &dest_hash,
854 1,
855 100.5,
856 &mut rng_resp,
857 )
858 .unwrap();
859
860 let mut rng_lrrtt = make_rng(0x30);
861 let (lrrtt_encrypted, _) = initiator
862 .handle_lrproof(&lrproof_data, &dest_sig_pub_bytes, 100.8, &mut rng_lrrtt)
863 .unwrap();
864 responder.handle_lrrtt(&lrrtt_encrypted, 101.0).unwrap();
865
866 let ka = responder.keepalive_interval();
867 assert!(!responder.needs_keepalive(101.0 + ka - 1.0));
869 assert!(responder.needs_keepalive(101.0 + ka + 1.0));
870 }
871
872 #[test]
873 fn test_teardown() {
874 let mut rng = make_rng(0x10);
875 let (mut engine, _) =
876 LinkEngine::new_initiator(&[0xDD; 16], 1, LinkMode::Aes256Cbc, None, 100.0, &mut rng);
877 let actions = engine.teardown();
878 assert_eq!(engine.state(), LinkState::Closed);
879 assert_eq!(actions.len(), 1);
880
881 let actions = engine.teardown();
883 assert!(actions.is_empty());
884 }
885
886 #[test]
887 fn test_handle_teardown() {
888 let mut rng = make_rng(0x10);
889 let (mut engine, _) =
890 LinkEngine::new_initiator(&[0xDD; 16], 1, LinkMode::Aes256Cbc, None, 100.0, &mut rng);
891 let actions = engine.handle_teardown();
892 assert_eq!(engine.state(), LinkState::Closed);
893 assert_eq!(actions.len(), 1);
894 match &actions[0] {
895 LinkAction::StateChanged { reason, .. } => {
896 assert_eq!(*reason, Some(TeardownReason::DestinationClosed));
897 }
898 _ => panic!("Expected StateChanged"),
899 }
900 }
901
902 #[test]
903 fn test_identify_over_link() {
904 let mut rng_id = make_rng(0x01);
905 let dest_sig_prv = Ed25519PrivateKey::generate(&mut rng_id);
906 let dest_sig_pub_bytes = dest_sig_prv.public_key().public_bytes();
907 let dest_hash = [0xDD; 16];
908
909 let mut rng_init = make_rng(0x10);
910 let (mut initiator, request_data) = LinkEngine::new_initiator(
911 &dest_hash,
912 1,
913 LinkMode::Aes256Cbc,
914 Some(500),
915 100.0,
916 &mut rng_init,
917 );
918 let mut hashable = Vec::new();
919 hashable.push(0x00);
920 hashable.push(0x00);
921 hashable.extend_from_slice(&dest_hash);
922 hashable.push(0x00);
923 hashable.extend_from_slice(&request_data);
924 initiator.set_link_id_from_hashable(&hashable, request_data.len());
925
926 let mut rng_resp = make_rng(0x20);
927 let (mut responder, lrproof_data) = LinkEngine::new_responder(
928 &dest_sig_prv,
929 &dest_sig_pub_bytes,
930 &request_data,
931 &hashable,
932 &dest_hash,
933 1,
934 100.5,
935 &mut rng_resp,
936 )
937 .unwrap();
938
939 let mut rng_lrrtt = make_rng(0x30);
940 let (lrrtt_encrypted, _) = initiator
941 .handle_lrproof(&lrproof_data, &dest_sig_pub_bytes, 100.8, &mut rng_lrrtt)
942 .unwrap();
943 responder.handle_lrrtt(&lrrtt_encrypted, 101.0).unwrap();
944
945 let mut rng_ident = make_rng(0x40);
947 let my_identity = rns_crypto::identity::Identity::new(&mut rng_ident);
948
949 let mut rng_enc = make_rng(0x50);
951 let identify_encrypted = initiator
952 .build_identify(&my_identity, &mut rng_enc)
953 .unwrap();
954
955 let actions = responder.handle_identify(&identify_encrypted).unwrap();
956 assert_eq!(actions.len(), 1);
957 match &actions[0] {
958 LinkAction::RemoteIdentified {
959 identity_hash,
960 public_key,
961 ..
962 } => {
963 assert_eq!(identity_hash, my_identity.hash());
964 assert_eq!(public_key, &my_identity.get_public_key().unwrap());
965 }
966 _ => panic!("Expected RemoteIdentified"),
967 }
968 }
969
970 #[test]
971 fn test_aes128_mode_handshake() {
972 let mut rng_id = make_rng(0x01);
973 let dest_sig_prv = Ed25519PrivateKey::generate(&mut rng_id);
974 let dest_sig_pub_bytes = dest_sig_prv.public_key().public_bytes();
975 let dest_hash = [0xDD; 16];
976
977 let mut rng_init = make_rng(0x10);
978 let (mut initiator, request_data) = LinkEngine::new_initiator(
979 &dest_hash,
980 1,
981 LinkMode::Aes128Cbc,
982 Some(500),
983 100.0,
984 &mut rng_init,
985 );
986 let mut hashable = Vec::new();
987 hashable.push(0x00);
988 hashable.push(0x00);
989 hashable.extend_from_slice(&dest_hash);
990 hashable.push(0x00);
991 hashable.extend_from_slice(&request_data);
992 initiator.set_link_id_from_hashable(&hashable, request_data.len());
993
994 let mut rng_resp = make_rng(0x20);
995 let (mut responder, lrproof_data) = LinkEngine::new_responder(
996 &dest_sig_prv,
997 &dest_sig_pub_bytes,
998 &request_data,
999 &hashable,
1000 &dest_hash,
1001 1,
1002 100.5,
1003 &mut rng_resp,
1004 )
1005 .unwrap();
1006
1007 let mut rng_lrrtt = make_rng(0x30);
1008 let (lrrtt_encrypted, _) = initiator
1009 .handle_lrproof(&lrproof_data, &dest_sig_pub_bytes, 100.8, &mut rng_lrrtt)
1010 .unwrap();
1011 responder.handle_lrrtt(&lrrtt_encrypted, 101.0).unwrap();
1012
1013 assert_eq!(initiator.state(), LinkState::Active);
1014 assert_eq!(responder.state(), LinkState::Active);
1015 assert_eq!(initiator.mode(), LinkMode::Aes128Cbc);
1016
1017 let mut rng_enc = make_rng(0x40);
1019 let ct = initiator.encrypt(b"AES128 test", &mut rng_enc).unwrap();
1020 let pt = responder.decrypt(&ct).unwrap();
1021 assert_eq!(pt, b"AES128 test");
1022 }
1023}