1use hkdf::Hkdf;
2use rand_core::{OsRng, RngCore};
3use sha2::{Digest, Sha256};
4use zeroize::Zeroizing;
5
6use crate::{
7 errors::CryptoError,
8 exchange::{
9 encryptor::Encryptor,
10 pair::{self, KEMPair},
11 prekey::SignedPrekey,
12 },
13 signatures::keypair::{self, PublicKeyBytes, SignerPair, VerifierPair, ViewOperations},
14};
15
16const PROTOCOL_LABEL: &[u8] = b"pq-msg/v2";
18const SESSION_FORMAT_VERSION: u8 = 2;
20const INITIATOR_TO_RESPONDER: u8 = 0;
22const RESPONDER_TO_INITIATOR: u8 = 1;
23const SESSION_BYTES: usize = 2
25 + keypair::PUBLIC_KEY_BYTES
26 + keypair::SECRET_KEY_BYTES
27 + keypair::PUBLIC_KEY_BYTES
28 + 32 * 3
29 + 16
30 + 8 * 2;
31
32pub struct MessageSession {
48 ds_pair: SignerPair,
50 target_verifier: VerifierPair,
52 is_initiator: bool,
54 transcript: [u8; 32],
56 send_chain: Zeroizing<[u8; 32]>,
58 recv_chain: Zeroizing<[u8; 32]>,
60 session_id: [u8; 16],
62 send_counter: u64,
64 recv_counter: u64,
66}
67
68impl MessageSession {
69 pub fn to_bytes(&self) -> Zeroizing<Vec<u8>> {
78 let mut bytes = Zeroizing::new(Vec::with_capacity(SESSION_BYTES));
79 bytes.push(SESSION_FORMAT_VERSION);
80 bytes.push(self.is_initiator as u8);
81 bytes.extend_from_slice(&self.ds_pair.to_bytes_uniform());
82 bytes.extend_from_slice(self.target_verifier.pub_key_bytes());
83 bytes.extend_from_slice(&self.transcript);
84 bytes.extend_from_slice(&self.send_chain[..]);
85 bytes.extend_from_slice(&self.recv_chain[..]);
86 bytes.extend_from_slice(&self.session_id);
87 bytes.extend_from_slice(&self.send_counter.to_le_bytes());
88 bytes.extend_from_slice(&self.recv_counter.to_le_bytes());
89 bytes
90 }
91
92 pub fn from_bytes(bytes: &[u8]) -> Result<Self, CryptoError> {
103 if bytes.len() != SESSION_BYTES {
104 return Err(CryptoError::IncongruentLength(SESSION_BYTES, bytes.len()));
105 }
106 if bytes[0] != SESSION_FORMAT_VERSION || bytes[1] > 1 {
107 return Err(CryptoError::InvalidSession);
108 }
109
110 let mut rest = &bytes[2..];
111 let mut take = |n: usize| {
112 let (head, tail) = rest.split_at(n);
113 rest = tail;
114 head
115 };
116
117 Ok(Self {
118 is_initiator: bytes[1] == 1,
119 ds_pair: SignerPair::from_bytes_uniform(take(
120 keypair::PUBLIC_KEY_BYTES + keypair::SECRET_KEY_BYTES,
121 ))?,
122 target_verifier: VerifierPair::new(take(keypair::PUBLIC_KEY_BYTES))?,
123 transcript: take(32).try_into()?,
124 send_chain: Zeroizing::new(take(32).try_into()?),
125 recv_chain: Zeroizing::new(take(32).try_into()?),
126 session_id: take(16).try_into()?,
127 send_counter: u64::from_le_bytes(take(8).try_into()?),
128 recv_counter: u64::from_le_bytes(take(8).try_into()?),
129 })
130 }
131
132 pub fn new_initiator(
150 my_signer: SignerPair,
151 base_nonce: [u8; 24],
152 target_prekey: &SignedPrekey,
153 target_verifier: &PublicKeyBytes,
154 ) -> Result<(Self, [u8; pair::CIPHERTEXT_BYTES]), CryptoError> {
155 let target_verifier = VerifierPair::new(target_verifier)?;
156 if !target_prekey.verify(target_verifier.pub_key_bytes()) {
157 return Err(CryptoError::InvalidSignature);
158 }
159
160 let (shared_secret, ciphertext) = KEMPair::encapsulate(target_prekey.kem_pub_key())?;
162
163 let session = Self::establish(
164 my_signer,
165 target_verifier,
166 true,
167 base_nonce,
168 target_prekey.kem_pub_key(),
169 &ciphertext,
170 &shared_secret,
171 );
172 Ok((session, ciphertext))
173 }
174
175 pub fn new_responder(
190 my_prekey: &KEMPair,
191 my_signer: SignerPair,
192 base_nonce: [u8; 24],
193 ciphertext: &[u8; pair::CIPHERTEXT_BYTES],
194 sender_verifier: &PublicKeyBytes,
195 ) -> Result<Self, CryptoError> {
196 let target_verifier = VerifierPair::new(sender_verifier)?;
197 let shared_secret = my_prekey.decapsulate(ciphertext);
198
199 Ok(Self::establish(
200 my_signer,
201 target_verifier,
202 false,
203 base_nonce,
204 &my_prekey.pub_key_bytes(),
205 ciphertext,
206 &shared_secret,
207 ))
208 }
209
210 fn establish(
212 ds_pair: SignerPair,
213 target_verifier: VerifierPair,
214 is_initiator: bool,
215 base_nonce: [u8; 24],
216 responder_kem_pubkey: &[u8; pair::PUBLIC_KEY_BYTES],
217 ciphertext: &[u8; pair::CIPHERTEXT_BYTES],
218 shared_secret: &[u8; pair::SHARED_SECRET_BYTES],
219 ) -> Self {
220 let (initiator, responder) = if is_initiator {
221 (ds_pair.pub_key_bytes(), target_verifier.pub_key_bytes())
222 } else {
223 (target_verifier.pub_key_bytes(), ds_pair.pub_key_bytes())
224 };
225
226 let transcript: [u8; 32] = Sha256::new()
228 .chain_update(PROTOCOL_LABEL)
229 .chain_update(base_nonce)
230 .chain_update(initiator)
231 .chain_update(responder)
232 .chain_update(responder_kem_pubkey)
233 .chain_update(ciphertext)
234 .finalize()
235 .into();
236
237 let hkdf = Hkdf::<Sha256>::new(Some(&transcript), shared_secret);
238 let mut i2r = Zeroizing::new([0u8; 32]);
239 let mut r2i = Zeroizing::new([0u8; 32]);
240 hkdf.expand(b"pq-msg/v2 initiator->responder", &mut i2r[..])
241 .expect("32 bytes is a valid HKDF-SHA256 output length");
242 hkdf.expand(b"pq-msg/v2 responder->initiator", &mut r2i[..])
243 .expect("32 bytes is a valid HKDF-SHA256 output length");
244 let (send_chain, recv_chain) = if is_initiator { (i2r, r2i) } else { (r2i, i2r) };
245
246 let counter = u64::from_le_bytes(base_nonce[16..].try_into().unwrap());
247 Self {
248 ds_pair,
249 target_verifier,
250 is_initiator,
251 transcript,
252 send_chain,
253 recv_chain,
254 session_id: base_nonce[..16].try_into().unwrap(),
255 send_counter: counter,
256 recv_counter: counter,
257 }
258 }
259
260 pub fn craft_message(&mut self, message: &[u8]) -> Result<Vec<u8>, CryptoError> {
272 let counter = self
273 .send_counter
274 .checked_add(1)
275 .ok_or(CryptoError::NonceExhausted)?;
276 let direction = self.send_direction();
277
278 let signed_data = self.signed_data(direction, counter, message);
280 let sig = self.ds_pair.sign(&signed_data)?;
281
282 let plaintext = Zeroizing::new([&sig[..], message].concat());
283 let (message_key, next_chain) = ratchet(&self.send_chain);
284 let ciphertext = Encryptor::new(&message_key)
285 .encrypt(&plaintext, &create_nonce(&self.session_id, counter))?;
286
287 self.send_chain = next_chain;
289 self.send_counter = counter;
290 Ok(ciphertext)
291 }
292
293 pub fn validate_message(&mut self, ciphertext: &[u8]) -> Result<Vec<u8>, CryptoError> {
306 let counter = self
307 .recv_counter
308 .checked_add(1)
309 .ok_or(CryptoError::NonceExhausted)?;
310 let direction = 1 - self.send_direction();
311
312 let (message_key, next_chain) = ratchet(&self.recv_chain);
313 let mut plaintext = Encryptor::new(&message_key)
314 .decrypt(ciphertext, &create_nonce(&self.session_id, counter))?;
315 if plaintext.len() < keypair::SIGNATURE_BYTES {
316 return Err(CryptoError::InvalidSignature);
317 }
318
319 let (sig, message) = plaintext.split_at(keypair::SIGNATURE_BYTES);
320 if !self
321 .target_verifier
322 .verify(&self.signed_data(direction, counter, message), sig)
323 {
324 return Err(CryptoError::InvalidSignature);
325 }
326
327 self.recv_chain = next_chain;
328 self.recv_counter = counter;
329 plaintext.drain(..keypair::SIGNATURE_BYTES);
330 Ok(plaintext)
331 }
332
333 fn send_direction(&self) -> u8 {
335 if self.is_initiator {
336 INITIATOR_TO_RESPONDER
337 } else {
338 RESPONDER_TO_INITIATOR
339 }
340 }
341
342 fn signed_data(&self, direction: u8, counter: u64, message: &[u8]) -> Vec<u8> {
344 [
345 &self.transcript[..],
346 &[direction],
347 &counter.to_le_bytes(),
348 message,
349 ]
350 .concat()
351 }
352
353 pub fn send_counter(&self) -> u64 {
355 self.send_counter
356 }
357
358 pub fn recv_counter(&self) -> u64 {
360 self.recv_counter
361 }
362}
363
364fn ratchet(chain: &[u8; 32]) -> (Zeroizing<[u8; 32]>, Zeroizing<[u8; 32]>) {
368 let hkdf = Hkdf::<Sha256>::from_prk(chain).expect("32 bytes is a valid HKDF-SHA256 PRK");
369 let mut message_key = Zeroizing::new([0u8; 32]);
370 let mut next_chain = Zeroizing::new([0u8; 32]);
371 hkdf.expand(b"pq-msg/v2 message key", &mut message_key[..])
372 .expect("32 bytes is a valid HKDF-SHA256 output length");
373 hkdf.expand(b"pq-msg/v2 chain key", &mut next_chain[..])
374 .expect("32 bytes is a valid HKDF-SHA256 output length");
375 (message_key, next_chain)
376}
377
378pub fn gen_session_id() -> [u8; 16] {
384 let mut session_id = [0u8; 16];
385 OsRng.fill_bytes(&mut session_id);
386
387 session_id
388}
389
390pub fn create_nonce(session_id: &[u8; 16], counter: u64) -> [u8; 24] {
400 let mut nonce = [0u8; 24];
401 nonce[..16].copy_from_slice(session_id);
402 nonce[16..24].copy_from_slice(&counter.to_le_bytes());
403 nonce
404}
405
406#[cfg(test)]
407mod tests {
408 use super::*;
409
410 fn signed_prekey(owner: &mut SignerPair) -> (KEMPair, SignedPrekey) {
412 let prekey = KEMPair::create();
413 let signed = SignedPrekey::new(owner, &prekey).unwrap();
414 (prekey, signed)
415 }
416
417 fn session_pair(base_counter: u64) -> (MessageSession, MessageSession) {
419 let alice_ds = SignerPair::create();
420 let mut bob_ds = SignerPair::create();
421 let (bob_prekey, bob_signed_prekey) = signed_prekey(&mut bob_ds);
422 let base_nonce = create_nonce(&gen_session_id(), base_counter);
423
424 let (alice, ciphertext) = MessageSession::new_initiator(
425 alice_ds.clone(),
426 base_nonce,
427 &bob_signed_prekey,
428 bob_ds.pub_key_bytes(),
429 )
430 .unwrap();
431 let bob = MessageSession::new_responder(
432 &bob_prekey,
433 bob_ds,
434 base_nonce,
435 &ciphertext,
436 alice_ds.pub_key_bytes(),
437 )
438 .unwrap();
439 (alice, bob)
440 }
441
442 #[test]
443 fn test_message_session_serialization() {
444 let (mut alice, mut bob) = session_pair(0);
445 bob.validate_message(&alice.craft_message(b"before").unwrap())
446 .unwrap();
447
448 let mut restored = MessageSession::from_bytes(&alice.to_bytes()).unwrap();
450 assert_eq!(alice.to_bytes(), restored.to_bytes());
451 assert_eq!(restored.send_counter(), 1);
452
453 let msg = bob.validate_message(&restored.craft_message(b"after").unwrap());
454 assert_eq!(msg.unwrap(), b"after");
455 let reply = restored.validate_message(&bob.craft_message(b"reply").unwrap());
456 assert_eq!(reply.unwrap(), b"reply");
457 }
458
459 #[test]
460 fn test_session_deserialization_rejects_bad_input() {
461 let (alice, _) = session_pair(0);
462 let bytes = alice.to_bytes();
463
464 assert!(matches!(
465 MessageSession::from_bytes(&bytes[1..]),
466 Err(CryptoError::IncongruentLength(SESSION_BYTES, _))
467 ));
468
469 let mut wrong_version = bytes.to_vec();
470 wrong_version[0] = 1;
471 assert!(matches!(
472 MessageSession::from_bytes(&wrong_version),
473 Err(CryptoError::InvalidSession)
474 ));
475
476 let mut wrong_role = bytes.to_vec();
477 wrong_role[1] = 2;
478 assert!(matches!(
479 MessageSession::from_bytes(&wrong_role),
480 Err(CryptoError::InvalidSession)
481 ));
482
483 let mut wrong_own_key = bytes.to_vec();
485 wrong_own_key[3] ^= 1;
486 assert!(matches!(
487 MessageSession::from_bytes(&wrong_own_key),
488 Err(CryptoError::InvalidKey)
489 ));
490
491 let mut wrong_peer_key = bytes.to_vec();
493 wrong_peer_key[2 + keypair::PUBLIC_KEY_BYTES + keypair::SECRET_KEY_BYTES] ^= 0xFF;
494 assert!(matches!(
495 MessageSession::from_bytes(&wrong_peer_key),
496 Err(CryptoError::InvalidKey)
497 ));
498 }
499
500 #[test]
501 fn test_wrong_peer_identity_rejected() {
502 let alice_ds = SignerPair::create();
504 let carol_ds = SignerPair::create();
505 let mut bob_ds = SignerPair::create();
506 let (bob_prekey, bob_signed_prekey) = signed_prekey(&mut bob_ds);
507 let base_nonce = create_nonce(&gen_session_id(), 0);
508
509 let (mut alice, ct) = MessageSession::new_initiator(
510 alice_ds,
511 base_nonce,
512 &bob_signed_prekey,
513 bob_ds.pub_key_bytes(),
514 )
515 .unwrap();
516 let mut bob = MessageSession::new_responder(
517 &bob_prekey,
518 bob_ds,
519 base_nonce,
520 &ct,
521 carol_ds.pub_key_bytes(),
522 )
523 .unwrap();
524
525 assert!(
526 bob.validate_message(&alice.craft_message(b"hi").unwrap())
527 .is_err()
528 );
529 }
530
531 #[test]
532 fn test_invalid_peer_keys_rejected() {
533 let mut bob_ds = SignerPair::create();
534 let (bob_prekey, bob_signed_prekey) = signed_prekey(&mut bob_ds);
535 let base_nonce = create_nonce(&gen_session_id(), 0);
536 let mut undecodable = *bob_ds.pub_key_bytes();
537 undecodable[0] ^= 0xFF;
538
539 let initiator = MessageSession::new_initiator(
540 SignerPair::create(),
541 base_nonce,
542 &bob_signed_prekey,
543 &undecodable,
544 );
545 assert!(matches!(initiator, Err(CryptoError::InvalidKey)));
546
547 let responder = MessageSession::new_responder(
548 &bob_prekey,
549 bob_ds,
550 base_nonce,
551 &[0u8; pair::CIPHERTEXT_BYTES],
552 &undecodable,
553 );
554 assert!(matches!(responder, Err(CryptoError::InvalidKey)));
555 }
556
557 #[test]
558 fn test_plaintext_too_short_for_signature() {
559 let (alice, mut bob) = session_pair(0);
561 let short = Encryptor::new(&ratchet(&alice.send_chain).0)
562 .encrypt(b"no signature here", &create_nonce(&alice.session_id, 1))
563 .unwrap();
564
565 assert!(matches!(
566 bob.validate_message(&short),
567 Err(CryptoError::InvalidSignature)
568 ));
569 assert_eq!(bob.recv_counter(), 0);
570 }
571
572 #[test]
573 fn test_full_message_exchange() {
574 let (mut alice, mut bob) = session_pair(0);
575
576 assert_eq!(alice.send_chain, bob.recv_chain);
578 assert_eq!(alice.recv_chain, bob.send_chain);
579 assert_ne!(alice.send_chain, alice.recv_chain);
580 assert_eq!(alice.transcript, bob.transcript);
581
582 let message = b"Hello, Bob! This is a secret message.";
584 let encrypted_message = alice.craft_message(message).unwrap();
585 assert_eq!(alice.send_counter(), 1);
586 assert_eq!(bob.recv_counter(), 0);
587
588 let raw_message = bob.validate_message(&encrypted_message).unwrap();
590 assert_eq!(bob.recv_counter(), 1);
591 assert_eq!(raw_message, message);
592
593 let reply = b"Hello, Alice! I received your message safely.";
595 let encrypted_reply = bob.craft_message(reply).unwrap();
596 let raw_reply = alice.validate_message(&encrypted_reply).unwrap();
597 assert_eq!(raw_reply, reply);
598
599 assert_eq!(alice.send_counter(), bob.recv_counter());
600 assert_eq!(alice.recv_counter(), bob.send_counter());
601 }
602
603 #[test]
604 fn test_concurrent_sends() {
605 let (mut alice, mut bob) = session_pair(0);
608
609 let (ma, mb) = (
610 b"attack at dawn, bring snacks!!",
611 b"meet me at the north gate 9pm.",
612 );
613 let ca = alice.craft_message(ma).unwrap();
614 let cb = bob.craft_message(mb).unwrap();
615
616 let ct_xor: Vec<u8> = ca.iter().zip(&cb).map(|(x, y)| x ^ y).collect();
617 let pt_xor: Vec<u8> = ma.iter().zip(mb).map(|(x, y)| x ^ y).collect();
618 assert!(!ct_xor.windows(pt_xor.len()).any(|w| w == pt_xor));
619
620 assert_eq!(bob.validate_message(&ca).unwrap(), ma);
622 assert_eq!(alice.validate_message(&cb).unwrap(), mb);
623 let next = alice.craft_message(b"next").unwrap();
624 assert_eq!(bob.validate_message(&next).unwrap(), b"next");
625 }
626
627 #[test]
628 fn test_replay_and_garbage_do_not_desync() {
629 let (mut alice, mut bob) = session_pair(0);
630
631 let first = alice.craft_message(b"first").unwrap();
632 bob.validate_message(&first).unwrap();
633
634 assert!(bob.validate_message(&first).is_err());
636 assert!(bob.validate_message(&[0u8; 2000]).is_err());
637 assert_eq!(bob.recv_counter(), 1);
638
639 let second = alice.craft_message(b"second").unwrap();
640 assert_eq!(bob.validate_message(&second).unwrap(), b"second");
641 }
642
643 #[test]
644 fn test_out_of_order_rejected() {
645 let (mut alice, mut bob) = session_pair(0);
646
647 let first = alice.craft_message(b"first").unwrap();
648 let second = alice.craft_message(b"second").unwrap();
649
650 assert!(bob.validate_message(&second).is_err());
651 assert_eq!(bob.validate_message(&first).unwrap(), b"first");
652 assert_eq!(bob.validate_message(&second).unwrap(), b"second");
653 }
654
655 #[test]
656 fn test_reflected_message_rejected() {
657 let (mut alice, _) = session_pair(0);
658
659 let msg = alice.craft_message(b"hello").unwrap();
661 assert!(alice.validate_message(&msg).is_err());
662 }
663
664 #[test]
665 fn test_forwarded_message_rejected() {
666 let alice_ds = SignerPair::create();
670 let mut bob_ds = SignerPair::create();
671 let (bob_prekey, bob_signed_prekey) = signed_prekey(&mut bob_ds);
672 let mut carol_ds = SignerPair::create();
673 let (carol_prekey, carol_signed_prekey) = signed_prekey(&mut carol_ds);
674 let base_nonce = create_nonce(&gen_session_id(), 0);
675
676 let (mut alice, ct) = MessageSession::new_initiator(
677 alice_ds.clone(),
678 base_nonce,
679 &bob_signed_prekey,
680 bob_ds.pub_key_bytes(),
681 )
682 .unwrap();
683 let bob = MessageSession::new_responder(
684 &bob_prekey,
685 bob_ds.clone(),
686 base_nonce,
687 &ct,
688 alice_ds.pub_key_bytes(),
689 )
690 .unwrap();
691
692 let sent = alice.craft_message(b"I owe Bob 100 euros").unwrap();
694 let signed = Encryptor::new(&ratchet(&bob.recv_chain).0)
695 .decrypt(&sent, &create_nonce(&bob.session_id, 1))
696 .unwrap();
697
698 let (_, ct) = MessageSession::new_initiator(
702 bob_ds,
703 base_nonce,
704 &carol_signed_prekey,
705 carol_ds.pub_key_bytes(),
706 )
707 .unwrap();
708 let mut carol = MessageSession::new_responder(
709 &carol_prekey,
710 carol_ds,
711 base_nonce,
712 &ct,
713 alice_ds.pub_key_bytes(),
714 )
715 .unwrap();
716 let forged = Encryptor::new(&ratchet(&carol.recv_chain).0)
717 .encrypt(&signed, &create_nonce(&carol.session_id, 1))
718 .unwrap();
719
720 assert!(matches!(
721 carol.validate_message(&forged),
722 Err(CryptoError::InvalidSignature)
723 ));
724 }
725
726 #[test]
727 fn test_stolen_session_cannot_read_past_messages() {
728 let (mut alice, mut bob) = session_pair(0);
729
730 let first = alice.craft_message(b"first").unwrap();
731 bob.validate_message(&first).unwrap();
732
733 let mut stolen = MessageSession::from_bytes(&bob.to_bytes()).unwrap();
736 stolen.recv_counter = 0;
737 assert!(stolen.validate_message(&first).is_err());
738 }
739
740 #[test]
741 fn test_deleted_prekey_gives_forward_secrecy() {
742 let alice_ds = SignerPair::create();
743 let mut bob_ds = SignerPair::create();
744 let (last_resort, _) = signed_prekey(&mut bob_ds);
745 let (one_time, one_time_signed) = signed_prekey(&mut bob_ds);
746 let base_nonce = create_nonce(&gen_session_id(), 0);
747
748 let (mut alice, ct) = MessageSession::new_initiator(
749 alice_ds.clone(),
750 base_nonce,
751 &one_time_signed,
752 bob_ds.pub_key_bytes(),
753 )
754 .unwrap();
755 let mut bob = MessageSession::new_responder(
756 &one_time,
757 bob_ds.clone(),
758 base_nonce,
759 &ct,
760 alice_ds.pub_key_bytes(),
761 )
762 .unwrap();
763 drop(one_time); let recorded = alice.craft_message(b"secret").unwrap();
767 assert_eq!(bob.validate_message(&recorded).unwrap(), b"secret");
768
769 let mut attacker = MessageSession::new_responder(
771 &last_resort,
772 bob_ds,
773 base_nonce,
774 &ct,
775 alice_ds.pub_key_bytes(),
776 )
777 .unwrap();
778 assert!(attacker.validate_message(&recorded).is_err());
779 }
780
781 #[test]
782 fn test_initiator_rejects_prekey_not_signed_by_target() {
783 let bob_ds = SignerPair::create();
785 let mut mallory_ds = SignerPair::create();
786 let (_, fake) = signed_prekey(&mut mallory_ds);
787
788 let result = MessageSession::new_initiator(
789 SignerPair::create(),
790 create_nonce(&gen_session_id(), 0),
791 &fake,
792 bob_ds.pub_key_bytes(),
793 );
794 assert!(matches!(result, Err(CryptoError::InvalidSignature)));
795 }
796
797 #[test]
798 fn test_counter_exhaustion() {
799 let (mut alice, mut bob) = session_pair(u64::MAX - 1);
800
801 let last = alice.craft_message(b"last").unwrap();
802 assert_eq!(alice.send_counter(), u64::MAX);
803 assert_eq!(bob.validate_message(&last).unwrap(), b"last");
804
805 assert!(matches!(
807 alice.craft_message(b"one more"),
808 Err(CryptoError::NonceExhausted)
809 ));
810 assert!(matches!(
811 bob.validate_message(&last),
812 Err(CryptoError::NonceExhausted)
813 ));
814 }
815
816 #[test]
817 fn test_empty_message() {
818 let (mut alice, mut bob) = session_pair(0);
819 let encrypted = alice.craft_message(b"").unwrap();
820 assert_eq!(bob.validate_message(&encrypted).unwrap(), b"");
821 }
822
823 #[test]
824 fn test_session_ids_are_random() {
825 assert_ne!(gen_session_id(), gen_session_id());
826 }
827
828 #[test]
829 fn test_nonce_creation() {
830 let session_id = gen_session_id();
832
833 let counter = 5;
835 let nonce = create_nonce(&session_id, counter);
836
837 assert_eq!(&nonce[..16], &session_id[..]);
839 assert_eq!(&nonce[16..], &counter.to_le_bytes()[..]);
840 }
841
842 #[test]
843 fn test_base_counter() {
844 let (mut alice, mut bob) = session_pair(42);
845 assert_eq!(alice.send_counter(), 42);
846 assert_eq!(bob.recv_counter(), 42);
847
848 bob.validate_message(&alice.craft_message(b"hi").unwrap())
849 .unwrap();
850 assert_eq!(alice.send_counter(), 43);
851 assert_eq!(bob.recv_counter(), 43);
852 }
853}