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