1pub mod types;
2pub mod handshake;
3pub mod crypto;
4pub mod keepalive;
5pub mod identify;
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_KEEPALIVE_MAX, MTU,
16 LINK_ESTABLISHMENT_TIMEOUT_PER_HOP,
17};
18
19pub use types::{LinkAction, LinkError, LinkId, LinkMode, LinkState, TeardownReason};
20
21use handshake::{
22 build_linkrequest_data, compute_link_id, pack_rtt, parse_linkrequest_data,
23 perform_key_exchange, unpack_rtt, validate_lrproof,
24};
25use crypto::{create_session_token, link_decrypt, link_encrypt};
26use keepalive::{
27 compute_establishment_timeout, compute_keepalive, compute_stale_time,
28 is_establishment_timeout, 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 pub_bytes: [u8; 32],
44 sig_prv: Ed25519PrivateKey,
45 sig_pub_bytes: [u8; 32],
46
47 peer_pub_bytes: Option<[u8; 32]>,
49 peer_sig_pub_bytes: Option<[u8; 32]>,
50
51 derived_key: Option<Vec<u8>>,
53 token: Option<Token>,
54
55 request_time: f64,
57 activated_at: Option<f64>,
58 last_inbound: f64,
59 last_outbound: f64,
60 last_keepalive: f64,
61 last_proof: f64,
62 rtt: Option<f64>,
63 keepalive_interval: f64,
64 stale_time: f64,
65 establishment_timeout: f64,
66
67 remote_identity: Option<([u8; 16], [u8; 64])>,
69 destination_hash: [u8; 16],
70
71 mtu: u32,
73 mdu: usize,
74}
75
76impl LinkEngine {
77 pub fn new_initiator(
82 dest_hash: &[u8; 16],
83 hops: u8,
84 mode: LinkMode,
85 mtu: Option<u32>,
86 now: f64,
87 rng: &mut dyn Rng,
88 ) -> (Self, Vec<u8>) {
89 let prv = X25519PrivateKey::generate(rng);
90 let pub_bytes = prv.public_key().public_bytes();
91 let sig_prv = Ed25519PrivateKey::generate(rng);
92 let sig_pub_bytes = sig_prv.public_key().public_bytes();
93
94 let request_data = build_linkrequest_data(&pub_bytes, &sig_pub_bytes, mtu, mode);
95
96 let link_mtu = mtu.unwrap_or(MTU as u32);
97
98 let engine = LinkEngine {
99 link_id: [0u8; 16], state: LinkState::Pending,
101 is_initiator: true,
102 mode,
103 prv,
104 pub_bytes,
105 sig_prv,
106 sig_pub_bytes,
107 peer_pub_bytes: None,
108 peer_sig_pub_bytes: None,
109 derived_key: None,
110 token: None,
111 request_time: now,
112 activated_at: None,
113 last_inbound: now,
114 last_outbound: now,
115 last_keepalive: now,
116 last_proof: 0.0,
117 rtt: None,
118 keepalive_interval: LINK_KEEPALIVE_MAX,
119 stale_time: LINK_KEEPALIVE_MAX * 2.0,
120 establishment_timeout: compute_establishment_timeout(
121 LINK_ESTABLISHMENT_TIMEOUT_PER_HOP,
122 hops,
123 ),
124 remote_identity: None,
125 destination_hash: *dest_hash,
126 mtu: link_mtu,
127 mdu: compute_mdu(link_mtu as usize),
128 };
129
130 (engine, request_data)
131 }
132
133 pub fn set_link_id_from_hashable(&mut self, hashable_part: &[u8], data_len: usize) {
138 let extra = if data_len > LINK_ECPUBSIZE {
139 data_len - LINK_ECPUBSIZE
140 } else {
141 0
142 };
143 self.link_id = compute_link_id(hashable_part, extra);
144 }
145
146 pub fn new_responder(
151 owner_sig_prv: &Ed25519PrivateKey,
152 owner_sig_pub_bytes: &[u8; 32],
153 linkrequest_data: &[u8],
154 hashable_part: &[u8],
155 dest_hash: &[u8; 16],
156 hops: u8,
157 now: f64,
158 rng: &mut dyn Rng,
159 ) -> Result<(Self, Vec<u8>), LinkError> {
160 let (peer_pub, peer_sig_pub, peer_mtu, mode) = parse_linkrequest_data(linkrequest_data)?;
161
162 let extra = if linkrequest_data.len() > LINK_ECPUBSIZE {
163 linkrequest_data.len() - LINK_ECPUBSIZE
164 } else {
165 0
166 };
167 let link_id = compute_link_id(hashable_part, extra);
168
169 let prv = X25519PrivateKey::generate(rng);
171 let pub_bytes = prv.public_key().public_bytes();
172 let sig_prv_clone = Ed25519PrivateKey::from_bytes(&owner_sig_prv.private_bytes());
173 let sig_pub_bytes = *owner_sig_pub_bytes;
174
175 let derived_key = perform_key_exchange(&prv, &peer_pub, &link_id, mode)?;
177 let token = create_session_token(&derived_key)?;
178
179 let link_mtu = peer_mtu.unwrap_or(MTU as u32);
180
181 let lrproof_data = handshake::build_lrproof(
183 &link_id,
184 &pub_bytes,
185 &sig_pub_bytes,
186 owner_sig_prv,
187 peer_mtu,
188 mode,
189 );
190
191 let engine = LinkEngine {
192 link_id,
193 state: LinkState::Handshake,
194 is_initiator: false,
195 mode,
196 prv,
197 pub_bytes,
198 sig_prv: sig_prv_clone,
199 sig_pub_bytes,
200 peer_pub_bytes: Some(peer_pub),
201 peer_sig_pub_bytes: Some(peer_sig_pub),
202 derived_key: Some(derived_key),
203 token: Some(token),
204 request_time: now,
205 activated_at: None,
206 last_inbound: now,
207 last_outbound: now,
208 last_keepalive: now,
209 last_proof: 0.0,
210 rtt: None,
211 keepalive_interval: LINK_KEEPALIVE_MAX,
212 stale_time: LINK_KEEPALIVE_MAX * 2.0,
213 establishment_timeout: compute_establishment_timeout(
214 LINK_ESTABLISHMENT_TIMEOUT_PER_HOP,
215 hops,
216 ),
217 remote_identity: None,
218 destination_hash: *dest_hash,
219 mtu: link_mtu,
220 mdu: compute_mdu(link_mtu as usize),
221 };
222
223 Ok((engine, lrproof_data))
224 }
225
226 pub fn handle_lrproof(
231 &mut self,
232 proof_data: &[u8],
233 peer_sig_pub_bytes: &[u8; 32],
234 now: f64,
235 rng: &mut dyn Rng,
236 ) -> Result<(Vec<u8>, Vec<LinkAction>), LinkError> {
237 if self.state != LinkState::Pending || !self.is_initiator {
238 return Err(LinkError::InvalidState);
239 }
240
241 let peer_sig_pub = Ed25519PublicKey::from_bytes(peer_sig_pub_bytes);
242
243 let (peer_pub, confirmed_mtu, confirmed_mode) =
244 validate_lrproof(proof_data, &self.link_id, &peer_sig_pub, peer_sig_pub_bytes)?;
245
246 if confirmed_mode != self.mode {
247 return Err(LinkError::UnsupportedMode);
248 }
249
250 self.peer_pub_bytes = Some(peer_pub);
251 self.peer_sig_pub_bytes = Some(*peer_sig_pub_bytes);
252
253 let derived_key = perform_key_exchange(&self.prv, &peer_pub, &self.link_id, self.mode)?;
255 let token = create_session_token(&derived_key)?;
256
257 self.derived_key = Some(derived_key);
258 self.token = Some(token);
259
260 if let Some(mtu) = confirmed_mtu {
262 self.mtu = mtu;
263 self.mdu = compute_mdu(mtu as usize);
264 }
265
266 let rtt = now - self.request_time;
268 self.rtt = Some(rtt);
269 self.state = LinkState::Active;
270 self.activated_at = Some(now);
271 self.last_inbound = now;
272 self.update_keepalive();
273
274 let rtt_packed = pack_rtt(rtt);
276 let rtt_encrypted = self.encrypt(&rtt_packed, rng)?;
277
278 let mut actions = Vec::new();
279 actions.push(LinkAction::StateChanged {
280 link_id: self.link_id,
281 new_state: LinkState::Active,
282 reason: None,
283 });
284 actions.push(LinkAction::LinkEstablished {
285 link_id: self.link_id,
286 rtt,
287 is_initiator: true,
288 });
289
290 Ok((rtt_encrypted, actions))
291 }
292
293 pub fn handle_lrrtt(
297 &mut self,
298 encrypted_data: &[u8],
299 now: f64,
300 ) -> Result<Vec<LinkAction>, LinkError> {
301 if self.state != LinkState::Handshake || self.is_initiator {
302 return Err(LinkError::InvalidState);
303 }
304
305 let plaintext = self.decrypt(encrypted_data)?;
306 let initiator_rtt = unpack_rtt(&plaintext).ok_or(LinkError::InvalidData)?;
307
308 let measured_rtt = now - self.request_time;
309 let rtt = if measured_rtt > initiator_rtt { measured_rtt } else { initiator_rtt };
310
311 self.rtt = Some(rtt);
312 self.state = LinkState::Active;
313 self.activated_at = Some(now);
314 self.last_inbound = now;
315 self.update_keepalive();
316
317 let mut actions = Vec::new();
318 actions.push(LinkAction::StateChanged {
319 link_id: self.link_id,
320 new_state: LinkState::Active,
321 reason: None,
322 });
323 actions.push(LinkAction::LinkEstablished {
324 link_id: self.link_id,
325 rtt,
326 is_initiator: false,
327 });
328
329 Ok(actions)
330 }
331
332 pub fn encrypt(&self, plaintext: &[u8], rng: &mut dyn Rng) -> Result<Vec<u8>, LinkError> {
334 let token = self.token.as_ref().ok_or(LinkError::NoSessionKey)?;
335 Ok(link_encrypt(token, plaintext, rng))
336 }
337
338 pub fn decrypt(&self, ciphertext: &[u8]) -> Result<Vec<u8>, LinkError> {
340 let token = self.token.as_ref().ok_or(LinkError::NoSessionKey)?;
341 link_decrypt(token, ciphertext)
342 }
343
344 pub fn build_identify(
346 &self,
347 identity: &rns_crypto::identity::Identity,
348 rng: &mut dyn Rng,
349 ) -> Result<Vec<u8>, LinkError> {
350 if self.state != LinkState::Active {
351 return Err(LinkError::InvalidState);
352 }
353 let plaintext = identify::build_identify_data(identity, &self.link_id)?;
354 self.encrypt(&plaintext, rng)
355 }
356
357 pub fn handle_identify(&mut self, encrypted_data: &[u8]) -> Result<Vec<LinkAction>, LinkError> {
361 if self.state != LinkState::Active || self.is_initiator {
362 return Err(LinkError::InvalidState);
363 }
364
365 let plaintext = self.decrypt(encrypted_data)?;
366 let (identity_hash, public_key) = identify::validate_identify_data(&plaintext, &self.link_id)?;
367 self.remote_identity = Some((identity_hash, public_key));
368
369 Ok(alloc::vec![LinkAction::RemoteIdentified {
370 link_id: self.link_id,
371 identity_hash,
372 public_key,
373 }])
374 }
375
376 pub fn record_inbound(&mut self, now: f64) -> Vec<LinkAction> {
380 self.last_inbound = now;
381 if self.state == LinkState::Stale {
382 self.state = LinkState::Active;
383 return alloc::vec![LinkAction::StateChanged {
384 link_id: self.link_id,
385 new_state: LinkState::Active,
386 reason: None,
387 }];
388 }
389 Vec::new()
390 }
391
392 pub fn record_proof(&mut self, now: f64) {
394 self.last_proof = now;
395 }
396
397 pub fn record_outbound(&mut self, now: f64, is_keepalive: bool) {
399 self.last_outbound = now;
400 if is_keepalive {
401 self.last_keepalive = now;
402 }
403 }
404
405 pub fn tick(&mut self, now: f64) -> Vec<LinkAction> {
407 let mut actions = Vec::new();
408
409 match self.state {
410 LinkState::Pending | LinkState::Handshake => {
411 if is_establishment_timeout(self.request_time, self.establishment_timeout, now) {
412 self.state = LinkState::Closed;
413 actions.push(LinkAction::StateChanged {
414 link_id: self.link_id,
415 new_state: LinkState::Closed,
416 reason: Some(TeardownReason::Timeout),
417 });
418 }
419 }
420 LinkState::Active => {
421 let activated = self.activated_at.unwrap_or(0.0);
422 let last_inbound = self.last_inbound.max(self.last_proof).max(activated);
424
425 if should_go_stale(last_inbound, self.stale_time, now) {
426 self.state = LinkState::Stale;
427 actions.push(LinkAction::StateChanged {
428 link_id: self.link_id,
429 new_state: LinkState::Stale,
430 reason: None,
431 });
432 }
433 }
434 LinkState::Stale => {
435 self.state = LinkState::Closed;
437 actions.push(LinkAction::StateChanged {
438 link_id: self.link_id,
439 new_state: LinkState::Closed,
440 reason: Some(TeardownReason::Timeout),
441 });
442 }
443 LinkState::Closed => {}
444 }
445
446 actions
447 }
448
449 pub fn needs_keepalive(&self, now: f64) -> bool {
451 if self.state != LinkState::Active || !self.is_initiator {
452 return false;
453 }
454 let activated = self.activated_at.unwrap_or(0.0);
455 let last_inbound = self.last_inbound.max(self.last_proof).max(activated);
456
457 if now < last_inbound + self.keepalive_interval {
459 return false;
460 }
461
462 should_send_keepalive(self.last_keepalive, self.keepalive_interval, now)
463 }
464
465 pub fn teardown(&mut self) -> Vec<LinkAction> {
467 if self.state == LinkState::Closed {
468 return Vec::new();
469 }
470 self.state = LinkState::Closed;
471 let reason = if self.is_initiator {
472 TeardownReason::InitiatorClosed
473 } else {
474 TeardownReason::DestinationClosed
475 };
476 alloc::vec![LinkAction::StateChanged {
477 link_id: self.link_id,
478 new_state: LinkState::Closed,
479 reason: Some(reason),
480 }]
481 }
482
483 pub fn handle_teardown(&mut self) -> Vec<LinkAction> {
485 if self.state == LinkState::Closed {
486 return Vec::new();
487 }
488 self.state = LinkState::Closed;
489 let reason = if self.is_initiator {
490 TeardownReason::DestinationClosed
491 } else {
492 TeardownReason::InitiatorClosed
493 };
494 alloc::vec![LinkAction::StateChanged {
495 link_id: self.link_id,
496 new_state: LinkState::Closed,
497 reason: Some(reason),
498 }]
499 }
500
501 pub fn link_id(&self) -> &LinkId {
504 &self.link_id
505 }
506
507 pub fn state(&self) -> LinkState {
508 self.state
509 }
510
511 pub fn rtt(&self) -> Option<f64> {
512 self.rtt
513 }
514
515 pub fn mdu(&self) -> usize {
516 self.mdu
517 }
518
519 pub fn is_initiator(&self) -> bool {
520 self.is_initiator
521 }
522
523 pub fn mode(&self) -> LinkMode {
524 self.mode
525 }
526
527 pub fn remote_identity(&self) -> Option<&([u8; 16], [u8; 64])> {
528 self.remote_identity.as_ref()
529 }
530
531 pub fn destination_hash(&self) -> &[u8; 16] {
532 &self.destination_hash
533 }
534
535 pub fn keepalive_interval(&self) -> f64 {
536 self.keepalive_interval
537 }
538
539 fn update_keepalive(&mut self) {
542 if let Some(rtt) = self.rtt {
543 self.keepalive_interval = compute_keepalive(rtt);
544 self.stale_time = compute_stale_time(self.keepalive_interval);
545 }
546 }
547}
548
549fn compute_mdu(mtu: usize) -> usize {
553 use crate::constants::{AES128_BLOCKSIZE, HEADER_MINSIZE, IFAC_MIN_SIZE, TOKEN_OVERHEAD};
554 let numerator = mtu.saturating_sub(IFAC_MIN_SIZE + HEADER_MINSIZE + TOKEN_OVERHEAD);
555 (numerator / AES128_BLOCKSIZE) * AES128_BLOCKSIZE - 1
556}
557
558#[cfg(test)]
559mod tests {
560 use super::*;
561 use crate::constants::LINK_MDU;
562 use rns_crypto::FixedRng;
563
564 fn make_rng(seed: u8) -> FixedRng {
565 FixedRng::new(&[seed; 128])
566 }
567
568 #[test]
569 fn test_compute_mdu_default() {
570 assert_eq!(compute_mdu(500), LINK_MDU);
571 }
572
573 #[test]
574 fn test_full_handshake() {
575 let mut rng_id = make_rng(0x01);
577 let dest_sig_prv = Ed25519PrivateKey::generate(&mut rng_id);
578 let dest_sig_pub_bytes = dest_sig_prv.public_key().public_bytes();
579
580 let dest_hash = [0xDD; 16];
581 let mode = LinkMode::Aes256Cbc;
582
583 let mut rng_init = make_rng(0x10);
585 let (mut initiator, request_data) = LinkEngine::new_initiator(
586 &dest_hash, 1, mode, Some(500), 100.0, &mut rng_init,
587 );
588 assert_eq!(initiator.state(), LinkState::Pending);
589
590 let mut hashable = Vec::new();
593 hashable.push(0x00); hashable.push(0x00); hashable.extend_from_slice(&dest_hash);
596 hashable.push(0x00); hashable.extend_from_slice(&request_data);
598
599 initiator.set_link_id_from_hashable(&hashable, request_data.len());
600 assert_ne!(initiator.link_id(), &[0u8; 16]);
601
602 let mut rng_resp = make_rng(0x20);
604 let (mut responder, lrproof_data) = LinkEngine::new_responder(
605 &dest_sig_prv,
606 &dest_sig_pub_bytes,
607 &request_data,
608 &hashable,
609 &dest_hash,
610 1,
611 100.5,
612 &mut rng_resp,
613 ).unwrap();
614 assert_eq!(responder.state(), LinkState::Handshake);
615 assert_eq!(responder.link_id(), initiator.link_id());
616
617 let mut rng_lrrtt = make_rng(0x30);
619 let (lrrtt_encrypted, actions) = initiator.handle_lrproof(
620 &lrproof_data,
621 &dest_sig_pub_bytes,
622 100.8,
623 &mut rng_lrrtt,
624 ).unwrap();
625 assert_eq!(initiator.state(), LinkState::Active);
626 assert!(initiator.rtt().is_some());
627 assert_eq!(actions.len(), 2); let actions = responder.handle_lrrtt(&lrrtt_encrypted, 101.0).unwrap();
631 assert_eq!(responder.state(), LinkState::Active);
632 assert!(responder.rtt().is_some());
633 assert_eq!(actions.len(), 2);
634 }
635
636 #[test]
637 fn test_encrypt_decrypt_after_handshake() {
638 let mut rng_id = make_rng(0x01);
639 let dest_sig_prv = Ed25519PrivateKey::generate(&mut rng_id);
640 let dest_sig_pub_bytes = dest_sig_prv.public_key().public_bytes();
641 let dest_hash = [0xDD; 16];
642
643 let mut rng_init = make_rng(0x10);
644 let (mut initiator, request_data) = LinkEngine::new_initiator(
645 &dest_hash, 1, LinkMode::Aes256Cbc, Some(500), 100.0, &mut rng_init,
646 );
647 let mut hashable = Vec::new();
648 hashable.push(0x00);
649 hashable.push(0x00);
650 hashable.extend_from_slice(&dest_hash);
651 hashable.push(0x00);
652 hashable.extend_from_slice(&request_data);
653 initiator.set_link_id_from_hashable(&hashable, request_data.len());
654
655 let mut rng_resp = make_rng(0x20);
656 let (mut responder, lrproof_data) = LinkEngine::new_responder(
657 &dest_sig_prv, &dest_sig_pub_bytes, &request_data, &hashable,
658 &dest_hash, 1, 100.5, &mut rng_resp,
659 ).unwrap();
660
661 let mut rng_lrrtt = make_rng(0x30);
662 let (lrrtt_encrypted, _) = initiator.handle_lrproof(
663 &lrproof_data, &dest_sig_pub_bytes, 100.8, &mut rng_lrrtt,
664 ).unwrap();
665 responder.handle_lrrtt(&lrrtt_encrypted, 101.0).unwrap();
666
667 let mut rng_enc = make_rng(0x40);
669 let plaintext = b"Hello over encrypted link!";
670 let ciphertext = initiator.encrypt(plaintext, &mut rng_enc).unwrap();
671 let decrypted = responder.decrypt(&ciphertext).unwrap();
672 assert_eq!(decrypted, plaintext);
673
674 let mut rng_enc2 = make_rng(0x50);
676 let ciphertext2 = responder.encrypt(b"Reply!", &mut rng_enc2).unwrap();
677 let decrypted2 = initiator.decrypt(&ciphertext2).unwrap();
678 assert_eq!(decrypted2, b"Reply!");
679 }
680
681 #[test]
682 fn test_tick_establishment_timeout() {
683 let mut rng = make_rng(0x10);
684 let dest_hash = [0xDD; 16];
685 let (mut engine, _) = LinkEngine::new_initiator(
686 &dest_hash, 1, LinkMode::Aes256Cbc, None, 100.0, &mut rng,
687 );
688 let actions = engine.tick(110.0);
692 assert!(actions.is_empty());
693
694 let actions = engine.tick(113.0);
696 assert_eq!(actions.len(), 1);
697 assert_eq!(engine.state(), LinkState::Closed);
698 }
699
700 #[test]
701 fn test_tick_stale_and_close() {
702 let mut rng_id = make_rng(0x01);
703 let dest_sig_prv = Ed25519PrivateKey::generate(&mut rng_id);
704 let dest_sig_pub_bytes = dest_sig_prv.public_key().public_bytes();
705 let dest_hash = [0xDD; 16];
706
707 let mut rng_init = make_rng(0x10);
708 let (mut initiator, request_data) = LinkEngine::new_initiator(
709 &dest_hash, 1, LinkMode::Aes256Cbc, Some(500), 100.0, &mut rng_init,
710 );
711 let mut hashable = Vec::new();
712 hashable.push(0x00);
713 hashable.push(0x00);
714 hashable.extend_from_slice(&dest_hash);
715 hashable.push(0x00);
716 hashable.extend_from_slice(&request_data);
717 initiator.set_link_id_from_hashable(&hashable, request_data.len());
718
719 let mut rng_resp = make_rng(0x20);
720 let (_, lrproof_data) = LinkEngine::new_responder(
721 &dest_sig_prv, &dest_sig_pub_bytes, &request_data, &hashable,
722 &dest_hash, 1, 100.5, &mut rng_resp,
723 ).unwrap();
724
725 let mut rng_lrrtt = make_rng(0x30);
726 initiator.handle_lrproof(&lrproof_data, &dest_sig_pub_bytes, 100.8, &mut rng_lrrtt).unwrap();
727 assert_eq!(initiator.state(), LinkState::Active);
728
729 let stale_time = initiator.stale_time;
731 let actions = initiator.tick(100.8 + stale_time + 1.0);
732 assert_eq!(initiator.state(), LinkState::Stale);
733 assert_eq!(actions.len(), 1);
734
735 let actions = initiator.tick(100.8 + stale_time + 2.0);
737 assert_eq!(initiator.state(), LinkState::Closed);
738 assert_eq!(actions.len(), 1);
739 }
740
741 #[test]
742 fn test_needs_keepalive() {
743 let mut rng_id = make_rng(0x01);
744 let dest_sig_prv = Ed25519PrivateKey::generate(&mut rng_id);
745 let dest_sig_pub_bytes = dest_sig_prv.public_key().public_bytes();
746 let dest_hash = [0xDD; 16];
747
748 let mut rng_init = make_rng(0x10);
749 let (mut initiator, request_data) = LinkEngine::new_initiator(
750 &dest_hash, 1, LinkMode::Aes256Cbc, Some(500), 100.0, &mut rng_init,
751 );
752 let mut hashable = Vec::new();
753 hashable.push(0x00);
754 hashable.push(0x00);
755 hashable.extend_from_slice(&dest_hash);
756 hashable.push(0x00);
757 hashable.extend_from_slice(&request_data);
758 initiator.set_link_id_from_hashable(&hashable, request_data.len());
759
760 let mut rng_resp = make_rng(0x20);
761 let (_, lrproof_data) = LinkEngine::new_responder(
762 &dest_sig_prv, &dest_sig_pub_bytes, &request_data, &hashable,
763 &dest_hash, 1, 100.5, &mut rng_resp,
764 ).unwrap();
765
766 let mut rng_lrrtt = make_rng(0x30);
767 initiator.handle_lrproof(&lrproof_data, &dest_sig_pub_bytes, 100.8, &mut rng_lrrtt).unwrap();
768
769 let ka = initiator.keepalive_interval();
770 assert!(!initiator.needs_keepalive(100.8 + ka - 1.0));
772 assert!(initiator.needs_keepalive(100.8 + ka + 1.0));
774 }
775
776 #[test]
777 fn test_teardown() {
778 let mut rng = make_rng(0x10);
779 let (mut engine, _) = LinkEngine::new_initiator(
780 &[0xDD; 16], 1, LinkMode::Aes256Cbc, None, 100.0, &mut rng,
781 );
782 let actions = engine.teardown();
783 assert_eq!(engine.state(), LinkState::Closed);
784 assert_eq!(actions.len(), 1);
785
786 let actions = engine.teardown();
788 assert!(actions.is_empty());
789 }
790
791 #[test]
792 fn test_handle_teardown() {
793 let mut rng = make_rng(0x10);
794 let (mut engine, _) = LinkEngine::new_initiator(
795 &[0xDD; 16], 1, LinkMode::Aes256Cbc, None, 100.0, &mut rng,
796 );
797 let actions = engine.handle_teardown();
798 assert_eq!(engine.state(), LinkState::Closed);
799 assert_eq!(actions.len(), 1);
800 match &actions[0] {
801 LinkAction::StateChanged { reason, .. } => {
802 assert_eq!(*reason, Some(TeardownReason::DestinationClosed));
803 }
804 _ => panic!("Expected StateChanged"),
805 }
806 }
807
808 #[test]
809 fn test_identify_over_link() {
810 let mut rng_id = make_rng(0x01);
811 let dest_sig_prv = Ed25519PrivateKey::generate(&mut rng_id);
812 let dest_sig_pub_bytes = dest_sig_prv.public_key().public_bytes();
813 let dest_hash = [0xDD; 16];
814
815 let mut rng_init = make_rng(0x10);
816 let (mut initiator, request_data) = LinkEngine::new_initiator(
817 &dest_hash, 1, LinkMode::Aes256Cbc, Some(500), 100.0, &mut rng_init,
818 );
819 let mut hashable = Vec::new();
820 hashable.push(0x00);
821 hashable.push(0x00);
822 hashable.extend_from_slice(&dest_hash);
823 hashable.push(0x00);
824 hashable.extend_from_slice(&request_data);
825 initiator.set_link_id_from_hashable(&hashable, request_data.len());
826
827 let mut rng_resp = make_rng(0x20);
828 let (mut responder, lrproof_data) = LinkEngine::new_responder(
829 &dest_sig_prv, &dest_sig_pub_bytes, &request_data, &hashable,
830 &dest_hash, 1, 100.5, &mut rng_resp,
831 ).unwrap();
832
833 let mut rng_lrrtt = make_rng(0x30);
834 let (lrrtt_encrypted, _) = initiator.handle_lrproof(
835 &lrproof_data, &dest_sig_pub_bytes, 100.8, &mut rng_lrrtt,
836 ).unwrap();
837 responder.handle_lrrtt(&lrrtt_encrypted, 101.0).unwrap();
838
839 let mut rng_ident = make_rng(0x40);
841 let my_identity = rns_crypto::identity::Identity::new(&mut rng_ident);
842
843 let mut rng_enc = make_rng(0x50);
845 let identify_encrypted = initiator.build_identify(&my_identity, &mut rng_enc).unwrap();
846
847 let actions = responder.handle_identify(&identify_encrypted).unwrap();
848 assert_eq!(actions.len(), 1);
849 match &actions[0] {
850 LinkAction::RemoteIdentified { identity_hash, public_key, .. } => {
851 assert_eq!(identity_hash, my_identity.hash());
852 assert_eq!(public_key, &my_identity.get_public_key().unwrap());
853 }
854 _ => panic!("Expected RemoteIdentified"),
855 }
856 }
857
858 #[test]
859 fn test_aes128_mode_handshake() {
860 let mut rng_id = make_rng(0x01);
861 let dest_sig_prv = Ed25519PrivateKey::generate(&mut rng_id);
862 let dest_sig_pub_bytes = dest_sig_prv.public_key().public_bytes();
863 let dest_hash = [0xDD; 16];
864
865 let mut rng_init = make_rng(0x10);
866 let (mut initiator, request_data) = LinkEngine::new_initiator(
867 &dest_hash, 1, LinkMode::Aes128Cbc, Some(500), 100.0, &mut rng_init,
868 );
869 let mut hashable = Vec::new();
870 hashable.push(0x00);
871 hashable.push(0x00);
872 hashable.extend_from_slice(&dest_hash);
873 hashable.push(0x00);
874 hashable.extend_from_slice(&request_data);
875 initiator.set_link_id_from_hashable(&hashable, request_data.len());
876
877 let mut rng_resp = make_rng(0x20);
878 let (mut responder, lrproof_data) = LinkEngine::new_responder(
879 &dest_sig_prv, &dest_sig_pub_bytes, &request_data, &hashable,
880 &dest_hash, 1, 100.5, &mut rng_resp,
881 ).unwrap();
882
883 let mut rng_lrrtt = make_rng(0x30);
884 let (lrrtt_encrypted, _) = initiator.handle_lrproof(
885 &lrproof_data, &dest_sig_pub_bytes, 100.8, &mut rng_lrrtt,
886 ).unwrap();
887 responder.handle_lrrtt(&lrrtt_encrypted, 101.0).unwrap();
888
889 assert_eq!(initiator.state(), LinkState::Active);
890 assert_eq!(responder.state(), LinkState::Active);
891 assert_eq!(initiator.mode(), LinkMode::Aes128Cbc);
892
893 let mut rng_enc = make_rng(0x40);
895 let ct = initiator.encrypt(b"AES128 test", &mut rng_enc).unwrap();
896 let pt = responder.decrypt(&ct).unwrap();
897 assert_eq!(pt, b"AES128 test");
898 }
899}