1use std::time::SystemTime;
23
24use displaydoc::Display;
25use libsignal_core::try_scoped;
26use rand::{CryptoRng, Rng};
27
28use crate::consts::MAX_UNACKNOWLEDGED_SESSION_AGE;
29use crate::state::{InvalidSessionError, SessionState};
30use crate::triple_ratchet::{OutgoingTripleRatchet, TripleRatchet};
31use crate::{
32 CiphertextMessage, CiphertextMessageType, Direction, IdentityKeyStore, KyberPayload,
33 KyberPreKeyStore, PreKeySignalMessage, PreKeyStore, ProtocolAddress, Result, SessionNotFound,
34 SessionRecord, SessionStore, SignalMessage, SignalProtocolError, SignedPreKeyStore, session,
35};
36pub async fn message_encrypt<R: Rng + CryptoRng>(
44 ptext: &[u8],
45 remote_address: &ProtocolAddress,
46 local_address: &ProtocolAddress,
47 session_store: &mut dyn SessionStore,
48 identity_store: &mut dyn IdentityKeyStore,
49 now: SystemTime,
50 csprng: &mut R,
51) -> Result<CiphertextMessage> {
52 let no_session_error = || {
53 SignalProtocolError::SessionNotFound(SessionNotFound::new(
54 remote_address.clone(),
55 "message_encrypt",
56 ))
57 };
58 let mut session_record = session_store
59 .load_session(remote_address)
60 .await?
61 .ok_or_else(no_session_error)?;
62 let session_state = session_record
63 .session_state_mut()
64 .ok_or_else(no_session_error)?;
65
66 let mut session = OutgoingTripleRatchet::from_session_state(session_state).map_err(|e| {
67 log::error!("session state corrupt for {remote_address}: {e}");
68 e
69 })?;
70
71 let their_identity_key = session_state
72 .remote_identity_key()?
73 .expect("session was valid; must have remote identity key");
74
75 let message = if let Some(items) = session_state.unacknowledged_pre_key_message_items()? {
77 let timestamp_as_unix_time = items
78 .timestamp()
79 .duration_since(SystemTime::UNIX_EPOCH)
80 .unwrap_or_default()
81 .as_secs();
82 if items.timestamp() + MAX_UNACKNOWLEDGED_SESSION_AGE < now {
83 log::warn!(
84 "stale unacknowledged session for {remote_address} (created at {timestamp_as_unix_time})"
85 );
86 return Err(no_session_error());
87 }
88
89 let local_registration_id = session_state.local_registration_id();
90
91 log::info!(
92 "Building PreKeyWhisperMessage for: {} with preKeyId: {} (session created at {})",
93 remote_address,
94 items
95 .pre_key_id()
96 .map_or_else(|| "<none>".to_string(), |id| id.to_string()),
97 timestamp_as_unix_time,
98 );
99
100 let kyber_payload = items
101 .kyber_pre_key_id()
102 .zip(items.kyber_ciphertext())
103 .map(|(id, ciphertext)| KyberPayload::new(id, ciphertext.into()));
104 let signal_message = session.encrypt(ptext, Some(local_address), remote_address, csprng)?;
105
106 CiphertextMessage::PreKeySignalMessage(PreKeySignalMessage::new(
107 session.session_version(),
108 local_registration_id,
109 items.pre_key_id(),
110 items.signed_pre_key_id(),
111 kyber_payload,
112 *items.base_key(),
113 *session.local_identity_key(),
114 signal_message,
115 )?)
116 } else {
117 let signal_message = session.encrypt(ptext, None, remote_address, csprng)?;
118 CiphertextMessage::SignalMessage(signal_message)
119 };
120
121 if !identity_store
127 .is_trusted_identity(remote_address, &their_identity_key, Direction::Sending)
128 .await?
129 {
130 log::warn!(
131 "Identity key {} is not trusted for remote address {}",
132 hex::encode(their_identity_key.public_key().public_key_bytes()),
133 remote_address,
134 );
135 return Err(SignalProtocolError::UntrustedIdentity(
136 remote_address.clone(),
137 ));
138 }
139
140 identity_store
141 .save_identity(remote_address, &their_identity_key)
142 .await?;
143
144 session.apply_to_session_state(session_state);
146
147 session_store
148 .store_session(remote_address, &session_record)
149 .await?;
150 Ok(message)
151}
152
153#[allow(clippy::too_many_arguments)]
158pub async fn message_decrypt<R: Rng + CryptoRng>(
159 ciphertext: &CiphertextMessage,
160 remote_address: &ProtocolAddress,
161 local_address: &ProtocolAddress,
162 session_store: &mut dyn SessionStore,
163 identity_store: &mut dyn IdentityKeyStore,
164 pre_key_store: &mut dyn PreKeyStore,
165 signed_pre_key_store: &dyn SignedPreKeyStore,
166 kyber_pre_key_store: &mut dyn KyberPreKeyStore,
167 csprng: &mut R,
168) -> Result<Vec<u8>> {
169 match ciphertext {
170 CiphertextMessage::SignalMessage(m) => {
171 message_decrypt_signal(
172 m,
173 remote_address,
174 local_address,
175 session_store,
176 identity_store,
177 csprng,
178 )
179 .await
180 }
181 CiphertextMessage::PreKeySignalMessage(m) => {
182 message_decrypt_prekey(
183 m,
184 remote_address,
185 local_address,
186 session_store,
187 identity_store,
188 pre_key_store,
189 signed_pre_key_store,
190 kyber_pre_key_store,
191 csprng,
192 )
193 .await
194 }
195 _ => Err(SignalProtocolError::InvalidArgument(format!(
196 "message_decrypt cannot be used to decrypt {:?} messages",
197 ciphertext.message_type()
198 ))),
199 }
200}
201
202#[allow(clippy::too_many_arguments)]
207pub async fn message_decrypt_prekey<R: Rng + CryptoRng>(
208 ciphertext: &PreKeySignalMessage,
209 remote_address: &ProtocolAddress,
210 local_address: &ProtocolAddress,
211 session_store: &mut dyn SessionStore,
212 identity_store: &mut dyn IdentityKeyStore,
213 pre_key_store: &mut dyn PreKeyStore,
214 signed_pre_key_store: &dyn SignedPreKeyStore,
215 kyber_pre_key_store: &mut dyn KyberPreKeyStore,
216 csprng: &mut R,
217) -> Result<Vec<u8>> {
218 let mut session_record = session_store
219 .load_session(remote_address)
220 .await?
221 .unwrap_or_else(SessionRecord::new_fresh);
222
223 let process_prekey_result = session::process_prekey(
225 ciphertext,
226 remote_address,
227 local_address,
228 &mut session_record,
229 identity_store,
230 pre_key_store,
231 signed_pre_key_store,
232 kyber_pre_key_store,
233 )
234 .await;
235
236 let (pre_key_used, identity_to_save) = match process_prekey_result {
237 Ok(result) => result,
238 Err(e) => {
239 let errs = [e];
240 log::error!(
241 "{}",
242 format_decryption_failure_log(
243 remote_address,
244 &errs,
245 &session_record,
246 ciphertext.message()
247 )?
248 );
249 let [e] = errs;
250 return Err(e);
251 }
252 };
253
254 let ptext = try_decrypt_from_record(
255 &mut session_record,
256 remote_address,
257 local_address,
258 ciphertext.message(),
259 CiphertextMessageType::PreKey,
260 csprng,
261 )?;
262
263 identity_store
264 .save_identity(
265 identity_to_save.remote_address,
266 identity_to_save.their_identity_key,
267 )
268 .await?;
269
270 if let Some(pre_key_used) = pre_key_used {
271 if let Some(kyber_pre_key_id) = pre_key_used.kyber_pre_key_id {
272 kyber_pre_key_store
273 .mark_kyber_pre_key_used(
274 kyber_pre_key_id,
275 pre_key_used.signed_ec_pre_key_id,
276 ciphertext.base_key(),
277 )
278 .await?;
279 }
280
281 if let Some(pre_key_id) = pre_key_used.one_time_ec_pre_key_id {
282 pre_key_store.remove_pre_key(pre_key_id).await?;
283 }
284 }
285
286 session_store
287 .store_session(remote_address, &session_record)
288 .await?;
289
290 Ok(ptext)
291}
292
293pub async fn message_decrypt_signal<R: Rng + CryptoRng>(
298 ciphertext: &SignalMessage,
299 remote_address: &ProtocolAddress,
300 local_address: &ProtocolAddress,
301 session_store: &mut dyn SessionStore,
302 identity_store: &mut dyn IdentityKeyStore,
303 csprng: &mut R,
304) -> Result<Vec<u8>> {
305 let mut session_record = session_store
306 .load_session(remote_address)
307 .await?
308 .ok_or_else(|| {
309 SignalProtocolError::SessionNotFound(SessionNotFound::new(
310 remote_address.clone(),
311 "message_decrypt_signal",
312 ))
313 })?;
314
315 let ptext = try_decrypt_from_record(
316 &mut session_record,
317 remote_address,
318 local_address,
319 ciphertext,
320 CiphertextMessageType::Whisper,
321 csprng,
322 )?;
323
324 let their_identity_key = session_record
326 .session_state()
327 .expect("successfully decrypted; must have a current state")
328 .remote_identity_key()
329 .expect("successfully decrypted; must have a remote identity key")
330 .expect("successfully decrypted; must have a remote identity key");
331
332 if !identity_store
333 .is_trusted_identity(remote_address, &their_identity_key, Direction::Receiving)
334 .await?
335 {
336 log::warn!(
337 "Identity key {} is not trusted for remote address {}",
338 hex::encode(their_identity_key.public_key().public_key_bytes()),
339 remote_address,
340 );
341 return Err(SignalProtocolError::UntrustedIdentity(
342 remote_address.clone(),
343 ));
344 }
345
346 identity_store
347 .save_identity(remote_address, &their_identity_key)
348 .await?;
349
350 session_store
351 .store_session(remote_address, &session_record)
352 .await?;
353
354 Ok(ptext)
355}
356
357pub(crate) fn try_decrypt_from_record<R: Rng + CryptoRng>(
369 record: &mut SessionRecord,
370 remote_address: &ProtocolAddress,
371 local_address: &ProtocolAddress,
372 ciphertext: &SignalMessage,
373 original_message_type: CiphertextMessageType,
374 csprng: &mut R,
375) -> Result<Vec<u8>> {
376 debug_assert!(matches!(
377 original_message_type,
378 CiphertextMessageType::Whisper | CiphertextMessageType::PreKey
379 ));
380 let ciphertext_version = ciphertext.message_version() as u32;
381
382 let log_failure = |label: &str, state: &SessionState, error: &SignalProtocolError| {
383 log::warn!(
384 "Failed to decrypt {:?} message with ratchet key: {} and counter: {}. \
385 Session loaded for {}. {} session has base key: {} and counter: {}. {}",
386 original_message_type,
387 hex::encode(ciphertext.sender_ratchet_key().public_key_bytes()),
388 ciphertext.counter(),
389 remote_address,
390 label,
391 state
392 .sender_ratchet_key_for_logging()
393 .unwrap_or_else(|e| format!("<error: {e}>")),
394 state.previous_counter(),
395 error
396 );
397 };
398
399 let mut errs = vec![];
400
401 if let Some(current_state) = record.session_state() {
404 let mut current_state = current_state.clone();
405
406 if current_state.session_version()? != ciphertext_version {
407 let e = SignalProtocolError::UnrecognizedMessageVersion(ciphertext_version);
408 log_failure("Current", ¤t_state, &e);
409 errs.push(e);
410 } else {
411 match try_decrypt_with_state(
412 &mut current_state,
413 remote_address,
414 local_address,
415 ciphertext,
416 original_message_type,
417 CurrentOrPrevious::Current,
418 csprng,
419 ) {
420 Ok(ptext) => {
421 log::info!(
422 "decrypted {:?} message from {} with current session state (base key {})",
423 original_message_type,
424 remote_address,
425 current_state
426 .sender_ratchet_key_for_logging()
427 .expect("successful decrypt always has a valid base key"),
428 );
429 record.set_session_state(current_state);
430 return Ok(ptext);
431 }
432 Err(e @ SignalProtocolError::DuplicatedMessage(_, _)) => return Err(e),
433 Err(e) => {
434 log_failure("Current", ¤t_state, &e);
435 errs.push(e);
436 match original_message_type {
437 CiphertextMessageType::PreKey => {
438 log::error!(
441 "{}",
442 format_decryption_failure_log(
443 remote_address,
444 &errs,
445 record,
446 ciphertext,
447 )?
448 );
449 return Err(SignalProtocolError::InvalidMessage(
452 original_message_type,
453 "decryption failed".to_owned(),
454 ));
455 }
456 CiphertextMessageType::Whisper => {}
457 CiphertextMessageType::SenderKey | CiphertextMessageType::Plaintext => {
458 unreachable!("should not be using Double Ratchet for these")
459 }
460 }
461 }
462 }
463 }
464 }
465
466 let mut promoted = None;
469
470 for (idx, previous) in record.previous_session_states().enumerate() {
471 let mut previous = match previous {
472 Ok(previous) => previous,
473 Err(e) => {
474 let e: SignalProtocolError = e.into();
475 log::warn!(
476 "Skipping corrupt previous session {} for {}: {}",
477 idx,
478 remote_address,
479 e
480 );
481 errs.push(e);
482 continue;
483 }
484 };
485
486 if previous.session_version()? != ciphertext_version {
487 let e = SignalProtocolError::UnrecognizedMessageVersion(ciphertext_version);
488 log_failure("Previous", &previous, &e);
489 errs.push(e);
490 continue;
491 }
492
493 match try_decrypt_with_state(
494 &mut previous,
495 remote_address,
496 local_address,
497 ciphertext,
498 original_message_type,
499 CurrentOrPrevious::Previous,
500 csprng,
501 ) {
502 Ok(ptext) => {
503 log::info!(
504 "decrypted {:?} message from {} with PREVIOUS session state (base key {})",
505 original_message_type,
506 remote_address,
507 previous
508 .sender_ratchet_key_for_logging()
509 .expect("successful decrypt always has a valid base key"),
510 );
511 promoted = Some((ptext, idx, previous));
512 break;
513 }
514 Err(e @ SignalProtocolError::DuplicatedMessage(_, _)) => return Err(e),
515 Err(e) => {
516 log_failure("Previous", &previous, &e);
517 errs.push(e);
518 }
519 }
520 }
521
522 if let Some((ptext, idx, updated)) = promoted {
523 record.promote_old_session(idx, updated);
526 Ok(ptext)
527 } else {
528 let previous_state_count = || record.previous_session_states().len();
529 if let Some(current_state) = record.session_state() {
530 log::error!(
531 "No valid session for recipient: {}, current session base key {}, \
532 number of previous states: {}",
533 remote_address,
534 current_state
535 .sender_ratchet_key_for_logging()
536 .unwrap_or_else(|e| format!("<error: {e}>")),
537 previous_state_count(),
538 );
539 } else {
540 log::error!(
541 "No valid session for recipient: {}, (no current session state), \
542 number of previous states: {}",
543 remote_address,
544 previous_state_count(),
545 );
546 }
547 log::error!(
548 "{}",
549 format_decryption_failure_log(remote_address, &errs, record, ciphertext)?
550 );
551 Err(SignalProtocolError::InvalidMessage(
552 original_message_type,
553 "decryption failed".to_owned(),
554 ))
555 }
556}
557
558pub(crate) fn try_decrypt_with_state<R: Rng + CryptoRng>(
567 state: &mut SessionState,
568 remote_address: &ProtocolAddress,
569 local_address: &ProtocolAddress,
570 ciphertext: &SignalMessage,
571 original_message_type: CiphertextMessageType,
572 curr_or_prev_for_logging: CurrentOrPrevious,
573 csprng: &mut R,
574) -> Result<Vec<u8>> {
575 debug_assert_eq!(
576 state.session_version()?,
577 ciphertext.message_version() as u32
578 );
579
580 let self_session = try_scoped::<bool, InvalidSessionError>(|| {
581 Ok(state.local_identity_key()?.is_same_account(
582 local_address,
583 &state
584 .remote_identity_key()?
585 .ok_or(InvalidSessionError("missing remote identity key"))?,
586 remote_address,
587 ))
588 })
589 .inspect_err(|e| log::warn!("Failed to determine self_session: {}", e))
590 .unwrap_or_default();
591 let mut session = TripleRatchet::from_session_state(state, self_session)?;
592
593 let ptext = session.decrypt(
594 remote_address,
595 local_address,
596 ciphertext,
597 original_message_type,
598 curr_or_prev_for_logging,
599 csprng,
600 )?;
601
602 session.apply_to_session_state(state);
603 state.clear_unacknowledged_pre_key_message();
604
605 Ok(ptext)
606}
607
608pub(crate) fn format_decryption_failure_log(
611 remote_address_for_logging: &ProtocolAddress,
612 mut errs: &[SignalProtocolError],
613 record: &SessionRecord,
614 ciphertext: &SignalMessage,
615) -> Result<String> {
616 fn append_session_summary(
617 lines: &mut Vec<String>,
618 idx: usize,
619 state: std::result::Result<&SessionState, InvalidSessionError>,
620 err: Option<&SignalProtocolError>,
621 ) {
622 let chains = state.map(|state| state.all_receiver_chain_logging_info());
623 match (err, &chains) {
624 (Some(err), Ok(chains)) => {
625 lines.push(format!(
626 "Candidate session {} failed with '{}', had {} receiver chains",
627 idx,
628 err,
629 chains.len()
630 ));
631 }
632 (Some(err), Err(state_err)) => {
633 lines.push(format!(
634 "Candidate session {idx} failed with '{err}'; \
635 cannot get receiver chain info ({state_err})",
636 ));
637 }
638 (None, Ok(chains)) => {
639 lines.push(format!(
640 "Candidate session {} had {} receiver chains",
641 idx,
642 chains.len()
643 ));
644 }
645 (None, Err(state_err)) => {
646 lines.push(format!(
647 "Candidate session {idx}: cannot get receiver chain info ({state_err})",
648 ));
649 }
650 }
651
652 if let Ok(chains) = chains {
653 for chain in chains {
654 let chain_idx = match chain.1 {
655 Some(i) => i.to_string(),
656 None => "missing in protobuf".to_string(),
657 };
658 lines.push(format!(
659 "Receiver chain with sender ratchet public key {} chain key index {}",
660 hex::encode(chain.0),
661 chain_idx
662 ));
663 }
664 }
665 }
666
667 let mut lines = vec![];
668 lines.push(format!(
669 "Message from {} failed to decrypt; sender ratchet public key {} message counter {}",
670 remote_address_for_logging,
671 hex::encode(ciphertext.sender_ratchet_key().public_key_bytes()),
672 ciphertext.counter()
673 ));
674
675 if let Some(current_session) = record.session_state() {
676 let err = errs.first();
677 if err.is_some() {
678 errs = &errs[1..];
679 }
680 append_session_summary(&mut lines, 0, Ok(current_session), err);
681 } else {
682 lines.push("No current session".to_string());
683 }
684
685 for (idx, (state, err)) in record
686 .previous_session_states()
687 .zip(errs.iter().map(Some).chain(std::iter::repeat(None)))
688 .enumerate()
689 {
690 let state = match state {
691 Ok(ref state) => Ok(state),
692 Err(err) => Err(err),
693 };
694 append_session_summary(&mut lines, idx + 1, state, err);
695 }
696
697 Ok(lines.join("\n"))
698}
699
700#[derive(Clone, Copy, Display)]
701pub(crate) enum CurrentOrPrevious {
702 Current,
704 Previous,
706}
707
708#[cfg(test)]
713mod legacy_interop_tests {
714 use assert_matches::assert_matches;
715 use futures_util::FutureExt;
726 use libsignal_protocol_test_support::Event;
727 use proptest::prelude::*;
728 use prost::Message;
729 use rand::SeedableRng;
730 use rand_chacha::ChaCha8Rng;
731
732 use super::*;
733 use crate::proto::storage::RecordStructure;
734 use crate::ratchet::{
735 AliceSignalProtocolParameters, BobSignalProtocolParameters,
736 initialize_alice_session_record, initialize_bob_session_record,
737 };
738 use crate::{
739 DecryptionErrorMessage, DeviceId, GenericSignedPreKey, IdentityKeyPair,
740 InMemSignalProtocolStore, KeyPair, KyberPreKeyId, KyberPreKeyRecord, PlaintextContent,
741 PreKeyBundle, PreKeyId, PreKeyRecord, ProtocolAddress, SessionRecord,
742 SessionUsabilityRequirements, SignalProtocolError, SignedPreKeyId, SignedPreKeyRecord,
743 Timestamp, extract_decryption_error_message_from_serialized_content, process_prekey_bundle,
744 session_cipher_legacy as legacy,
745 };
746
747 #[derive(Clone, Copy, PartialEq, Eq, Debug)]
748 enum MessageStatus {
749 Sent,
750 Dropped,
751 Delivered,
752 }
753
754 #[derive(Clone)]
755 struct DualLocalState {
756 new_store: InMemSignalProtocolStore,
757 legacy_store: InMemSignalProtocolStore,
758 pre_key_count: u32,
759 }
760
761 struct DualParticipant {
762 address: ProtocolAddress,
763 message_queue: Vec<(CiphertextMessage, u64)>,
764 state: DualLocalState,
765 snapshots: Vec<DualLocalState>,
766 message_send_log: Vec<MessageStatus>,
767 }
768
769 fn setup_stores(
771 rng: &mut ChaCha8Rng,
772 ) -> (
773 InMemSignalProtocolStore,
774 InMemSignalProtocolStore,
775 ProtocolAddress,
776 ProtocolAddress,
777 ) {
778 let alice_identity = IdentityKeyPair::generate(rng);
779 let bob_identity = IdentityKeyPair::generate(rng);
780
781 let alice_base_key = KeyPair::generate(rng);
782 let bob_signed_pre_key = KeyPair::generate(rng);
783 let bob_kyber_key = crate::kem::KeyPair::generate(crate::kem::KeyType::Kyber1024, rng);
784
785 let alice_params = AliceSignalProtocolParameters::new(
786 alice_identity,
787 alice_base_key,
788 *bob_identity.identity_key(),
789 bob_signed_pre_key.public_key,
790 bob_signed_pre_key.public_key,
791 bob_kyber_key.public_key.clone(),
792 false,
793 );
794
795 let alice_record =
796 initialize_alice_session_record(&alice_params, rng).expect("alice session init");
797 let kyber_ct: Box<[u8]> = alice_record
798 .get_kyber_ciphertext()
799 .expect("session valid")
800 .expect("has kyber ciphertext")
801 .clone()
802 .into_boxed_slice();
803
804 let bob_params = BobSignalProtocolParameters::new(
805 bob_identity,
806 bob_signed_pre_key,
807 None,
808 bob_kyber_key,
809 *alice_identity.identity_key(),
810 alice_base_key.public_key,
811 &kyber_ct,
812 false,
813 );
814
815 let bob_record = initialize_bob_session_record(&bob_params, &bob_signed_pre_key)
816 .expect("bob session init");
817
818 let alice_address = ProtocolAddress::new(
819 "57721566-4901-5328-6060-651209008240".to_owned(),
820 DeviceId::new(1).unwrap(),
821 );
822 let bob_address = ProtocolAddress::new(
823 "26149721-2847-6427-8375-542683860869".to_owned(),
824 DeviceId::new(1).unwrap(),
825 );
826
827 let mut alice_store = InMemSignalProtocolStore::new(alice_identity, 1).unwrap();
828 let mut bob_store = InMemSignalProtocolStore::new(bob_identity, 2).unwrap();
829
830 alice_store
831 .session_store
832 .store_session(&bob_address, &alice_record)
833 .now_or_never()
834 .unwrap()
835 .unwrap();
836 bob_store
837 .session_store
838 .store_session(&alice_address, &bob_record)
839 .now_or_never()
840 .unwrap()
841 .unwrap();
842
843 (alice_store, bob_store, alice_address, bob_address)
844 }
845
846 fn create_bob_bundle(
852 bob_store: &mut InMemSignalProtocolStore,
853 pre_key_id: u32,
854 signed_pre_key_id: u32,
855 kyber_pre_key_id: u32,
856 rng: &mut ChaCha8Rng,
857 ) -> PreKeyBundle {
858 let identity_key_pair = bob_store
859 .get_identity_key_pair()
860 .now_or_never()
861 .unwrap()
862 .unwrap();
863
864 let pre_key = KeyPair::generate(rng);
865 let signed_pre_key = KeyPair::generate(rng);
866 let kyber_key = crate::kem::KeyPair::generate(crate::kem::KeyType::Kyber1024, rng);
867
868 let pk_id = PreKeyId::from(pre_key_id);
869 let spk_id = SignedPreKeyId::from(signed_pre_key_id);
870 let kpk_id = KyberPreKeyId::from(kyber_pre_key_id);
871
872 let spk_sig = identity_key_pair
873 .private_key()
874 .calculate_signature(&signed_pre_key.public_key.serialize(), rng)
875 .unwrap();
876 let kpk_sig = identity_key_pair
877 .private_key()
878 .calculate_signature(&kyber_key.public_key.serialize(), rng)
879 .unwrap();
880
881 bob_store
882 .save_pre_key(pk_id, &PreKeyRecord::new(pk_id, &pre_key))
883 .now_or_never()
884 .unwrap()
885 .unwrap();
886 bob_store
887 .save_signed_pre_key(
888 spk_id,
889 &SignedPreKeyRecord::new(
890 spk_id,
891 Timestamp::from_epoch_millis(42),
892 &signed_pre_key,
893 &spk_sig,
894 ),
895 )
896 .now_or_never()
897 .unwrap()
898 .unwrap();
899 bob_store
900 .save_kyber_pre_key(
901 kpk_id,
902 &KyberPreKeyRecord::new(
903 kpk_id,
904 Timestamp::from_epoch_millis(43),
905 &kyber_key,
906 &kpk_sig,
907 ),
908 )
909 .now_or_never()
910 .unwrap()
911 .unwrap();
912
913 let reg_id = bob_store
914 .get_local_registration_id()
915 .now_or_never()
916 .unwrap()
917 .unwrap();
918
919 PreKeyBundle::new(
920 reg_id,
921 DeviceId::new(1).unwrap(),
922 Some((pk_id, pre_key.public_key)),
923 spk_id,
924 signed_pre_key.public_key,
925 spk_sig.to_vec(),
926 kpk_id,
927 kyber_key.public_key.clone(),
928 kpk_sig.to_vec(),
929 *identity_key_pair.identity_key(),
930 )
931 .unwrap()
932 }
933
934 #[test]
935 fn encrypt_preserves_corruption_error_instead_of_session_not_found() {
936 let mut rng = ChaCha8Rng::seed_from_u64(0xC0FFEE);
937 let (mut alice_store, _bob_store, alice_address, bob_address) = setup_stores(&mut rng);
938 let now = SystemTime::now();
939
940 let good_record = alice_store
941 .session_store
942 .load_session(&bob_address)
943 .now_or_never()
944 .expect("sync")
945 .expect("load succeeded")
946 .expect("session exists");
947
948 let serialized = good_record.serialize().expect("serialize");
949 let mut record_pb = RecordStructure::decode(serialized.as_slice()).expect("decode record");
950 record_pb
951 .current_session
952 .as_mut()
953 .expect("current session")
954 .remote_identity_public = vec![0xFF];
955
956 let corrupted_record = SessionRecord::deserialize(record_pb.encode_to_vec().as_slice())
957 .expect("deserialize corrupted record");
958
959 alice_store
960 .session_store
961 .store_session(&bob_address, &corrupted_record)
962 .now_or_never()
963 .expect("sync")
964 .expect("store succeeded");
965
966 let legacy_err = legacy::legacy_message_encrypt(
967 b"test",
968 &bob_address,
969 &alice_address,
970 &mut alice_store.session_store,
971 &mut alice_store.identity_store,
972 now,
973 &mut rng,
974 )
975 .now_or_never()
976 .expect("sync")
977 .expect_err("legacy encrypt should fail on corrupted state");
978 assert!(
979 matches!(
980 legacy_err,
981 SignalProtocolError::InvalidSessionStructure("invalid remote identity key")
982 ),
983 "unexpected legacy error: {legacy_err:?}"
984 );
985
986 alice_store
987 .session_store
988 .store_session(&bob_address, &corrupted_record)
989 .now_or_never()
990 .expect("sync")
991 .expect("store succeeded");
992
993 let new_err = message_encrypt(
994 b"test",
995 &bob_address,
996 &alice_address,
997 &mut alice_store.session_store,
998 &mut alice_store.identity_store,
999 now,
1000 &mut rng,
1001 )
1002 .now_or_never()
1003 .expect("sync")
1004 .expect_err("new encrypt should fail on corrupted state");
1005 assert!(
1006 matches!(
1007 new_err,
1008 SignalProtocolError::InvalidSessionStructure("invalid remote identity key")
1009 ),
1010 "unexpected new error: {new_err:?}"
1011 );
1012 }
1013
1014 #[test]
1015 fn encrypt_ignores_corrupt_unused_receiver_chain() {
1016 let mut rng = ChaCha8Rng::seed_from_u64(0xACE55);
1017 let (mut alice_store, _bob_store, alice_address, bob_address) = setup_stores(&mut rng);
1018 let now = SystemTime::now();
1019
1020 let good_record = alice_store
1021 .session_store
1022 .load_session(&bob_address)
1023 .now_or_never()
1024 .expect("sync")
1025 .expect("load succeeded")
1026 .expect("session exists");
1027
1028 let serialized = good_record.serialize().expect("serialize");
1029 let mut record_pb = RecordStructure::decode(serialized.as_slice()).expect("decode record");
1030 let current_session = record_pb.current_session.as_mut().expect("current session");
1031 assert!(
1032 !current_session.receiver_chains.is_empty(),
1033 "expected at least one receiver chain"
1034 );
1035 current_session.receiver_chains[0].sender_ratchet_key = vec![0xFF];
1036
1037 let corrupted_record = SessionRecord::deserialize(record_pb.encode_to_vec().as_slice())
1038 .expect("deserialize corrupted record");
1039
1040 alice_store
1041 .session_store
1042 .store_session(&bob_address, &corrupted_record)
1043 .now_or_never()
1044 .expect("sync")
1045 .expect("store succeeded");
1046
1047 let mut legacy_rng = rng.clone();
1048 let legacy_ct = legacy::legacy_message_encrypt(
1049 b"test",
1050 &bob_address,
1051 &alice_address,
1052 &mut alice_store.session_store,
1053 &mut alice_store.identity_store,
1054 now,
1055 &mut legacy_rng,
1056 )
1057 .now_or_never()
1058 .expect("sync")
1059 .expect("legacy encrypt should ignore unused receiver-chain corruption");
1060
1061 alice_store
1062 .session_store
1063 .store_session(&bob_address, &corrupted_record)
1064 .now_or_never()
1065 .expect("sync")
1066 .expect("store succeeded");
1067
1068 let new_ct = message_encrypt(
1069 b"test",
1070 &bob_address,
1071 &alice_address,
1072 &mut alice_store.session_store,
1073 &mut alice_store.identity_store,
1074 now,
1075 &mut rng,
1076 )
1077 .now_or_never()
1078 .expect("sync")
1079 .expect("new encrypt should ignore unused receiver-chain corruption");
1080
1081 let legacy_msg = match legacy_ct {
1082 CiphertextMessage::SignalMessage(m) => m,
1083 other => panic!(
1084 "expected SignalMessage from legacy enc, got {:?}",
1085 other.message_type()
1086 ),
1087 };
1088 let new_msg = match new_ct {
1089 CiphertextMessage::SignalMessage(m) => m,
1090 other => panic!(
1091 "expected SignalMessage from new enc, got {:?}",
1092 other.message_type()
1093 ),
1094 };
1095
1096 assert_eq!(legacy_msg.serialized(), new_msg.serialized());
1097 }
1098
1099 #[test]
1100 fn decrypt_skips_corrupt_previous_session_and_uses_later_valid_previous() {
1101 let mut rng = ChaCha8Rng::seed_from_u64(0xBAD5EED);
1102 let (mut alice_store, mut bob_store, alice_address, bob_address) = setup_stores(&mut rng);
1103 let now = SystemTime::now();
1104
1105 let delayed_plaintext = b"delayed on session A".to_vec();
1106
1107 let delayed_ct = legacy::legacy_message_encrypt(
1108 &delayed_plaintext,
1109 &bob_address,
1110 &alice_address,
1111 &mut alice_store.session_store,
1112 &mut alice_store.identity_store,
1113 now,
1114 &mut rng,
1115 )
1116 .now_or_never()
1117 .expect("sync")
1118 .expect("delayed legacy enc");
1119
1120 let delayed_signal_msg = match delayed_ct {
1121 CiphertextMessage::SignalMessage(m) => m,
1122 other => panic!(
1123 "expected SignalMessage for delayed msg, got {:?}",
1124 other.message_type()
1125 ),
1126 };
1127
1128 let bundle = create_bob_bundle(&mut bob_store, 1, 1, 1, &mut rng);
1129 process_prekey_bundle(
1130 &bob_address,
1131 &alice_address,
1132 &mut alice_store.session_store,
1133 &mut alice_store.identity_store,
1134 &bundle,
1135 now,
1136 &mut rng,
1137 )
1138 .now_or_never()
1139 .expect("sync")
1140 .expect("process_prekey_bundle");
1141
1142 let session_b_init = message_encrypt(
1143 b"session B init",
1144 &bob_address,
1145 &alice_address,
1146 &mut alice_store.session_store,
1147 &mut alice_store.identity_store,
1148 now,
1149 &mut rng,
1150 )
1151 .now_or_never()
1152 .expect("sync")
1153 .expect("session B init enc");
1154
1155 message_decrypt(
1156 &session_b_init,
1157 &alice_address,
1158 &bob_address,
1159 &mut bob_store.session_store,
1160 &mut bob_store.identity_store,
1161 &mut bob_store.pre_key_store,
1162 &bob_store.signed_pre_key_store,
1163 &mut bob_store.kyber_pre_key_store,
1164 &mut rng,
1165 )
1166 .now_or_never()
1167 .expect("sync")
1168 .expect("session B init dec");
1169
1170 let session_b_ack = message_encrypt(
1171 b"session B ack",
1172 &alice_address,
1173 &bob_address,
1174 &mut bob_store.session_store,
1175 &mut bob_store.identity_store,
1176 now,
1177 &mut rng,
1178 )
1179 .now_or_never()
1180 .expect("sync")
1181 .expect("session B ack enc");
1182
1183 let session_b_ack_signal = match &session_b_ack {
1184 CiphertextMessage::SignalMessage(m) => m,
1185 other => panic!(
1186 "expected Whisper for session B ack, got {:?}",
1187 other.message_type()
1188 ),
1189 };
1190 message_decrypt_signal(
1191 session_b_ack_signal,
1192 &bob_address,
1193 &alice_address,
1194 &mut alice_store.session_store,
1195 &mut alice_store.identity_store,
1196 &mut rng,
1197 )
1198 .now_or_never()
1199 .expect("sync")
1200 .expect("session B ack dec");
1201
1202 let bob_record = bob_store
1203 .session_store
1204 .load_session(&alice_address)
1205 .now_or_never()
1206 .expect("sync")
1207 .expect("load succeeded")
1208 .expect("session exists");
1209 let serialized = bob_record.serialize().expect("serialize");
1210 let mut record_pb = RecordStructure::decode(serialized.as_slice()).expect("decode record");
1211 assert_eq!(
1212 record_pb.previous_sessions.len(),
1213 1,
1214 "expected one valid previous session"
1215 );
1216 record_pb.previous_sessions.insert(0, vec![0xFF]);
1217 let corrupted_record = SessionRecord::deserialize(record_pb.encode_to_vec().as_slice())
1218 .expect("deserialize mutated record");
1219 bob_store
1220 .session_store
1221 .store_session(&alice_address, &corrupted_record)
1222 .now_or_never()
1223 .expect("sync")
1224 .expect("store succeeded");
1225
1226 let ptext = message_decrypt_signal(
1227 &delayed_signal_msg,
1228 &alice_address,
1229 &bob_address,
1230 &mut bob_store.session_store,
1231 &mut bob_store.identity_store,
1232 &mut rng,
1233 )
1234 .now_or_never()
1235 .expect("sync")
1236 .expect("delayed msg dec via valid later previous session");
1237
1238 assert_eq!(ptext, delayed_plaintext);
1239 }
1240
1241 fn setup_two_alice_receiver_chains_on_bob(
1242 rng: &mut ChaCha8Rng,
1243 ) -> (
1244 InMemSignalProtocolStore,
1245 InMemSignalProtocolStore,
1246 ProtocolAddress,
1247 ProtocolAddress,
1248 SignalMessage,
1249 ) {
1250 let (mut alice_store, mut bob_store, alice_address, bob_address) = setup_stores(rng);
1251 let now = SystemTime::now();
1252
1253 let delayed_ct = message_encrypt(
1254 b"delayed old",
1255 &bob_address,
1256 &alice_address,
1257 &mut alice_store.session_store,
1258 &mut alice_store.identity_store,
1259 now,
1260 rng,
1261 )
1262 .now_or_never()
1263 .expect("sync")
1264 .expect("delayed old enc");
1265 let delayed_signal_msg = match delayed_ct {
1266 CiphertextMessage::SignalMessage(m) => m,
1267 other => panic!(
1268 "expected delayed SignalMessage, got {:?}",
1269 other.message_type()
1270 ),
1271 };
1272
1273 let trigger_ct = message_encrypt(
1274 b"trigger old chain advancement",
1275 &bob_address,
1276 &alice_address,
1277 &mut alice_store.session_store,
1278 &mut alice_store.identity_store,
1279 now,
1280 rng,
1281 )
1282 .now_or_never()
1283 .expect("sync")
1284 .expect("trigger enc");
1285 let trigger_signal_msg = match trigger_ct {
1286 CiphertextMessage::SignalMessage(m) => m,
1287 other => panic!(
1288 "expected trigger SignalMessage, got {:?}",
1289 other.message_type()
1290 ),
1291 };
1292 message_decrypt_signal(
1293 &trigger_signal_msg,
1294 &alice_address,
1295 &bob_address,
1296 &mut bob_store.session_store,
1297 &mut bob_store.identity_store,
1298 rng,
1299 )
1300 .now_or_never()
1301 .expect("sync")
1302 .expect("trigger dec");
1303
1304 let bob_reply_ct = message_encrypt(
1305 b"bob reply new ratchet",
1306 &alice_address,
1307 &bob_address,
1308 &mut bob_store.session_store,
1309 &mut bob_store.identity_store,
1310 now,
1311 rng,
1312 )
1313 .now_or_never()
1314 .expect("sync")
1315 .expect("bob reply enc");
1316 let bob_reply_signal_msg = match bob_reply_ct {
1317 CiphertextMessage::SignalMessage(m) => m,
1318 other => panic!(
1319 "expected bob reply SignalMessage, got {:?}",
1320 other.message_type()
1321 ),
1322 };
1323 message_decrypt_signal(
1324 &bob_reply_signal_msg,
1325 &bob_address,
1326 &alice_address,
1327 &mut alice_store.session_store,
1328 &mut alice_store.identity_store,
1329 rng,
1330 )
1331 .now_or_never()
1332 .expect("sync")
1333 .expect("bob reply dec");
1334
1335 let alice_new_chain_ct = message_encrypt(
1336 b"alice new chain",
1337 &bob_address,
1338 &alice_address,
1339 &mut alice_store.session_store,
1340 &mut alice_store.identity_store,
1341 now,
1342 rng,
1343 )
1344 .now_or_never()
1345 .expect("sync")
1346 .expect("alice new chain enc");
1347 let alice_new_chain_signal_msg = match alice_new_chain_ct {
1348 CiphertextMessage::SignalMessage(m) => m,
1349 other => panic!(
1350 "expected alice new chain SignalMessage, got {:?}",
1351 other.message_type()
1352 ),
1353 };
1354 message_decrypt_signal(
1355 &alice_new_chain_signal_msg,
1356 &alice_address,
1357 &bob_address,
1358 &mut bob_store.session_store,
1359 &mut bob_store.identity_store,
1360 rng,
1361 )
1362 .now_or_never()
1363 .expect("sync")
1364 .expect("alice new chain dec");
1365
1366 (
1367 alice_store,
1368 bob_store,
1369 alice_address,
1370 bob_address,
1371 delayed_signal_msg,
1372 )
1373 }
1374
1375 #[test]
1376 fn decrypt_ignores_corrupt_unmatched_receiver_chain() {
1377 let mut rng = ChaCha8Rng::seed_from_u64(0xD311A9);
1378 let (_alice_store, mut bob_store, alice_address, bob_address, delayed_signal_msg) =
1379 setup_two_alice_receiver_chains_on_bob(&mut rng);
1380
1381 let bob_record = bob_store
1382 .session_store
1383 .load_session(&alice_address)
1384 .now_or_never()
1385 .expect("sync")
1386 .expect("load succeeded")
1387 .expect("session exists");
1388 let serialized = bob_record.serialize().expect("serialize");
1389 let mut record_pb = RecordStructure::decode(serialized.as_slice()).expect("decode record");
1390 let current_session = record_pb.current_session.as_mut().expect("current session");
1391 assert!(
1392 current_session.receiver_chains.len() >= 2,
1393 "expected at least two receiver chains"
1394 );
1395
1396 let matched_key = delayed_signal_msg.sender_ratchet_key().serialize().to_vec();
1397 let matched_idx = current_session
1398 .receiver_chains
1399 .iter()
1400 .position(|chain| chain.sender_ratchet_key == matched_key)
1401 .expect("matching receiver chain present");
1402 let unmatched_idx = (0..current_session.receiver_chains.len())
1403 .find(|idx| *idx != matched_idx)
1404 .expect("unmatched receiver chain present");
1405
1406 current_session.receiver_chains[unmatched_idx].sender_ratchet_key = vec![0xFF];
1407
1408 let corrupted_record = SessionRecord::deserialize(record_pb.encode_to_vec().as_slice())
1409 .expect("deserialize mutated record");
1410 bob_store
1411 .session_store
1412 .store_session(&alice_address, &corrupted_record)
1413 .now_or_never()
1414 .expect("sync")
1415 .expect("store succeeded");
1416
1417 let ptext = message_decrypt_signal(
1418 &delayed_signal_msg,
1419 &alice_address,
1420 &bob_address,
1421 &mut bob_store.session_store,
1422 &mut bob_store.identity_store,
1423 &mut rng,
1424 )
1425 .now_or_never()
1426 .expect("sync")
1427 .expect("decrypt should ignore unmatched corrupt receiver chain");
1428
1429 assert_eq!(ptext, b"delayed old");
1430 }
1431
1432 #[test]
1433 fn decrypt_fails_on_corrupt_matched_receiver_chain() {
1434 let mut rng = ChaCha8Rng::seed_from_u64(0xD311AA);
1435 let (_alice_store, mut bob_store, alice_address, bob_address, delayed_signal_msg) =
1436 setup_two_alice_receiver_chains_on_bob(&mut rng);
1437
1438 let bob_record = bob_store
1439 .session_store
1440 .load_session(&alice_address)
1441 .now_or_never()
1442 .expect("sync")
1443 .expect("load succeeded")
1444 .expect("session exists");
1445 let serialized = bob_record.serialize().expect("serialize");
1446 let mut record_pb = RecordStructure::decode(serialized.as_slice()).expect("decode record");
1447 let current_session = record_pb.current_session.as_mut().expect("current session");
1448
1449 let matched_key = delayed_signal_msg.sender_ratchet_key().serialize().to_vec();
1450 let matched_idx = current_session
1451 .receiver_chains
1452 .iter()
1453 .position(|chain| chain.sender_ratchet_key == matched_key)
1454 .expect("matching receiver chain present");
1455 current_session.receiver_chains[matched_idx]
1456 .chain_key
1457 .as_mut()
1458 .expect("chain key present")
1459 .key = vec![0xFF];
1460
1461 let corrupted_record = SessionRecord::deserialize(record_pb.encode_to_vec().as_slice())
1462 .expect("deserialize mutated record");
1463 bob_store
1464 .session_store
1465 .store_session(&alice_address, &corrupted_record)
1466 .now_or_never()
1467 .expect("sync")
1468 .expect("store succeeded");
1469
1470 let err = message_decrypt_signal(
1471 &delayed_signal_msg,
1472 &alice_address,
1473 &bob_address,
1474 &mut bob_store.session_store,
1475 &mut bob_store.identity_store,
1476 &mut rng,
1477 )
1478 .now_or_never()
1479 .expect("sync")
1480 .expect_err("decrypt should fail on corrupt matched receiver chain");
1481
1482 assert_matches!(
1483 err,
1484 SignalProtocolError::InvalidMessage(
1485 CiphertextMessageType::Whisper,
1486 msg
1487 )
1488 if msg == "decryption failed"
1489 );
1490 }
1491
1492 fn assert_store_state_equivalent(
1500 new_store: &InMemSignalProtocolStore,
1501 leg_store: &InMemSignalProtocolStore,
1502 peer_addr: &ProtocolAddress,
1503 context: &str,
1504 ) {
1505 let new_session = new_store
1506 .session_store
1507 .load_session(peer_addr)
1508 .now_or_never()
1509 .expect("sync")
1510 .expect("new load session");
1511 let leg_session = leg_store
1512 .session_store
1513 .load_session(peer_addr)
1514 .now_or_never()
1515 .expect("sync")
1516 .expect("legacy load session");
1517
1518 let new_session_bytes = new_session.map(|record| record.serialize().expect("serialize"));
1519 let leg_session_bytes = leg_session.map(|record| record.serialize().expect("serialize"));
1520 assert_eq!(
1521 new_session_bytes, leg_session_bytes,
1522 "{context}: session records diverged"
1523 );
1524
1525 let new_identity = new_store
1526 .identity_store
1527 .get_identity(peer_addr)
1528 .now_or_never()
1529 .expect("sync")
1530 .expect("new load identity")
1531 .map(|identity| identity.serialize());
1532 let leg_identity = leg_store
1533 .identity_store
1534 .get_identity(peer_addr)
1535 .now_or_never()
1536 .expect("sync")
1537 .expect("legacy load identity")
1538 .map(|identity| identity.serialize());
1539 assert_eq!(
1540 new_identity, leg_identity,
1541 "{context}: trusted identities diverged"
1542 );
1543 }
1544
1545 fn dual_encrypt(
1548 plaintext: &[u8],
1549 recv_addr: &ProtocolAddress,
1550 send_addr: &ProtocolAddress,
1551 new_sender: &mut InMemSignalProtocolStore,
1552 leg_sender: &mut InMemSignalProtocolStore,
1553 now: SystemTime,
1554 rng: &mut ChaCha8Rng,
1555 ) -> SignalMessage {
1556 let mut leg_rng = rng.clone();
1557
1558 let new_ct = message_encrypt(
1559 plaintext,
1560 recv_addr,
1561 send_addr,
1562 &mut new_sender.session_store,
1563 &mut new_sender.identity_store,
1564 now,
1565 rng,
1566 )
1567 .now_or_never()
1568 .expect("sync")
1569 .expect("new encrypt");
1570
1571 let leg_ct = legacy::legacy_message_encrypt(
1572 plaintext,
1573 recv_addr,
1574 send_addr,
1575 &mut leg_sender.session_store,
1576 &mut leg_sender.identity_store,
1577 now,
1578 &mut leg_rng,
1579 )
1580 .now_or_never()
1581 .expect("sync")
1582 .expect("legacy encrypt");
1583
1584 assert_eq!(
1585 new_ct.serialize(),
1586 leg_ct.serialize(),
1587 "new and legacy produced different ciphertexts"
1588 );
1589 assert_eq!(
1590 new_ct.message_type(),
1591 leg_ct.message_type(),
1592 "new and legacy produced different ciphertext types"
1593 );
1594 assert_store_state_equivalent(new_sender, leg_sender, recv_addr, "encrypt");
1595
1596 match new_ct {
1597 CiphertextMessage::SignalMessage(m) => m,
1598 other => panic!(
1599 "expected SignalMessage from dual_encrypt, got {:?}",
1600 other.message_type()
1601 ),
1602 }
1603 }
1604
1605 fn dual_encrypt_any(
1608 plaintext: &[u8],
1609 recv_addr: &ProtocolAddress,
1610 send_addr: &ProtocolAddress,
1611 new_sender: &mut InMemSignalProtocolStore,
1612 leg_sender: &mut InMemSignalProtocolStore,
1613 now: SystemTime,
1614 rng: &mut ChaCha8Rng,
1615 ) -> CiphertextMessage {
1616 let mut leg_rng = rng.clone();
1617
1618 let new_ct = message_encrypt(
1619 plaintext,
1620 recv_addr,
1621 send_addr,
1622 &mut new_sender.session_store,
1623 &mut new_sender.identity_store,
1624 now,
1625 rng,
1626 )
1627 .now_or_never()
1628 .expect("sync")
1629 .expect("new encrypt");
1630
1631 let leg_ct = legacy::legacy_message_encrypt(
1632 plaintext,
1633 recv_addr,
1634 send_addr,
1635 &mut leg_sender.session_store,
1636 &mut leg_sender.identity_store,
1637 now,
1638 &mut leg_rng,
1639 )
1640 .now_or_never()
1641 .expect("sync")
1642 .expect("legacy encrypt");
1643
1644 assert_eq!(
1645 new_ct.serialize(),
1646 leg_ct.serialize(),
1647 "new and legacy produced different ciphertexts"
1648 );
1649 assert_eq!(
1650 new_ct.message_type(),
1651 leg_ct.message_type(),
1652 "new and legacy produced different ciphertext types"
1653 );
1654 assert_store_state_equivalent(new_sender, leg_sender, recv_addr, "encrypt");
1655 new_ct
1656 }
1657
1658 fn dual_decrypt(
1661 msg: &SignalMessage,
1662 sender_addr: &ProtocolAddress,
1663 recv_addr: &ProtocolAddress,
1664 new_receiver: &mut InMemSignalProtocolStore,
1665 leg_receiver: &mut InMemSignalProtocolStore,
1666 rng: &mut ChaCha8Rng,
1667 ) -> Vec<u8> {
1668 let mut leg_rng = rng.clone();
1669
1670 let new_pt = message_decrypt_signal(
1671 msg,
1672 sender_addr,
1673 recv_addr,
1674 &mut new_receiver.session_store,
1675 &mut new_receiver.identity_store,
1676 rng,
1677 )
1678 .now_or_never()
1679 .expect("sync")
1680 .expect("new decrypt");
1681
1682 let leg_pt = legacy::legacy_message_decrypt_signal(
1683 msg,
1684 sender_addr,
1685 &mut leg_receiver.session_store,
1686 &mut leg_receiver.identity_store,
1687 &mut leg_rng,
1688 )
1689 .now_or_never()
1690 .expect("sync")
1691 .expect("legacy decrypt");
1692
1693 assert_eq!(
1694 new_pt, leg_pt,
1695 "new and legacy produced different plaintexts"
1696 );
1697 assert_store_state_equivalent(new_receiver, leg_receiver, sender_addr, "decrypt");
1698 new_pt
1699 }
1700
1701 fn dual_decrypt_any(
1704 msg: &CiphertextMessage,
1705 sender_addr: &ProtocolAddress,
1706 receiver_addr: &ProtocolAddress,
1707 new_receiver: &mut InMemSignalProtocolStore,
1708 leg_receiver: &mut InMemSignalProtocolStore,
1709 rng: &mut ChaCha8Rng,
1710 ) -> Vec<u8> {
1711 let mut leg_rng = rng.clone();
1712
1713 let new_pt = message_decrypt(
1714 msg,
1715 sender_addr,
1716 receiver_addr,
1717 &mut new_receiver.session_store,
1718 &mut new_receiver.identity_store,
1719 &mut new_receiver.pre_key_store,
1720 &new_receiver.signed_pre_key_store,
1721 &mut new_receiver.kyber_pre_key_store,
1722 rng,
1723 )
1724 .now_or_never()
1725 .expect("sync")
1726 .expect("new decrypt");
1727
1728 let leg_pt = legacy::legacy_message_decrypt(
1729 msg,
1730 sender_addr,
1731 receiver_addr,
1732 &mut leg_receiver.session_store,
1733 &mut leg_receiver.identity_store,
1734 &mut leg_receiver.pre_key_store,
1735 &leg_receiver.signed_pre_key_store,
1736 &mut leg_receiver.kyber_pre_key_store,
1737 &mut leg_rng,
1738 )
1739 .now_or_never()
1740 .expect("sync")
1741 .expect("legacy decrypt");
1742
1743 assert_eq!(
1744 new_pt, leg_pt,
1745 "new and legacy produced different plaintexts"
1746 );
1747 assert_store_state_equivalent(new_receiver, leg_receiver, sender_addr, "decrypt");
1748 new_pt
1749 }
1750
1751 fn dual_decrypt_any_result(
1752 msg: &CiphertextMessage,
1753 sender_addr: &ProtocolAddress,
1754 receiver_addr: &ProtocolAddress,
1755 new_receiver: &mut InMemSignalProtocolStore,
1756 leg_receiver: &mut InMemSignalProtocolStore,
1757 rng: &mut ChaCha8Rng,
1758 ) -> Result<Vec<u8>> {
1759 let mut leg_rng = rng.clone();
1760
1761 let new_result = message_decrypt(
1762 msg,
1763 sender_addr,
1764 receiver_addr,
1765 &mut new_receiver.session_store,
1766 &mut new_receiver.identity_store,
1767 &mut new_receiver.pre_key_store,
1768 &new_receiver.signed_pre_key_store,
1769 &mut new_receiver.kyber_pre_key_store,
1770 rng,
1771 )
1772 .now_or_never()
1773 .expect("sync");
1774
1775 let leg_result = legacy::legacy_message_decrypt(
1776 msg,
1777 sender_addr,
1778 receiver_addr,
1779 &mut leg_receiver.session_store,
1780 &mut leg_receiver.identity_store,
1781 &mut leg_receiver.pre_key_store,
1782 &leg_receiver.signed_pre_key_store,
1783 &mut leg_receiver.kyber_pre_key_store,
1784 &mut leg_rng,
1785 )
1786 .now_or_never()
1787 .expect("sync");
1788
1789 match (new_result, leg_result) {
1790 (Ok(new_pt), Ok(leg_pt)) => {
1791 assert_eq!(
1792 new_pt, leg_pt,
1793 "new and legacy produced different plaintexts"
1794 );
1795 assert_store_state_equivalent(new_receiver, leg_receiver, sender_addr, "decrypt");
1796 Ok(new_pt)
1797 }
1798 (Err(new_err), Err(leg_err)) => {
1799 assert_eq!(
1800 std::mem::discriminant(&new_err),
1801 std::mem::discriminant(&leg_err),
1802 "error variants differ: new={new_err:?}, legacy={leg_err:?}"
1803 );
1804 assert_store_state_equivalent(new_receiver, leg_receiver, sender_addr, "decrypt");
1805 Err(new_err)
1806 }
1807 (new_result, leg_result) => panic!(
1808 "new and legacy disagreed on decrypt result: new={new_result:?}, legacy={leg_result:?}"
1809 ),
1810 }
1811 }
1812
1813 fn dual_decrypt_expect_err(
1815 msg: &SignalMessage,
1816 sender_addr: &ProtocolAddress,
1817 recv_addr: &ProtocolAddress,
1818 new_receiver: &mut InMemSignalProtocolStore,
1819 leg_receiver: &mut InMemSignalProtocolStore,
1820 rng: &mut ChaCha8Rng,
1821 ) -> SignalProtocolError {
1822 let mut leg_rng = rng.clone();
1823
1824 let new_err = message_decrypt_signal(
1825 msg,
1826 sender_addr,
1827 recv_addr,
1828 &mut new_receiver.session_store,
1829 &mut new_receiver.identity_store,
1830 rng,
1831 )
1832 .now_or_never()
1833 .expect("sync")
1834 .expect_err("expected new decrypt to fail");
1835
1836 let leg_err = legacy::legacy_message_decrypt_signal(
1837 msg,
1838 sender_addr,
1839 &mut leg_receiver.session_store,
1840 &mut leg_receiver.identity_store,
1841 &mut leg_rng,
1842 )
1843 .now_or_never()
1844 .expect("sync")
1845 .expect_err("expected legacy decrypt to fail");
1846
1847 assert_eq!(
1848 std::mem::discriminant(&new_err),
1849 std::mem::discriminant(&leg_err),
1850 "error variants differ: new={new_err:?}, legacy={leg_err:?}"
1851 );
1852 new_err
1853 }
1854
1855 struct DualSession {
1859 na: InMemSignalProtocolStore,
1860 nb: InMemSignalProtocolStore,
1861 la: InMemSignalProtocolStore,
1862 lb: InMemSignalProtocolStore,
1863 alice: ProtocolAddress,
1864 bob: ProtocolAddress,
1865 rng: ChaCha8Rng,
1866 now: SystemTime,
1867 }
1868
1869 impl DualSession {
1870 fn new(seed: u64) -> Self {
1871 let mut rng = ChaCha8Rng::seed_from_u64(seed);
1872 let (na, nb, alice, bob) = setup_stores(&mut rng);
1873 let (la, lb) = (na.clone(), nb.clone());
1874 Self {
1875 na,
1876 nb,
1877 la,
1878 lb,
1879 alice,
1880 bob,
1881 rng,
1882 now: SystemTime::now(),
1883 }
1884 }
1885
1886 fn alice_sends(&mut self, plaintext: &[u8]) -> SignalMessage {
1887 dual_encrypt(
1888 plaintext,
1889 &self.bob,
1890 &self.alice,
1891 &mut self.na,
1892 &mut self.la,
1893 self.now,
1894 &mut self.rng,
1895 )
1896 }
1897
1898 fn bob_sends(&mut self, plaintext: &[u8]) -> SignalMessage {
1899 dual_encrypt(
1900 plaintext,
1901 &self.alice,
1902 &self.bob,
1903 &mut self.nb,
1904 &mut self.lb,
1905 self.now,
1906 &mut self.rng,
1907 )
1908 }
1909
1910 fn bob_receives(&mut self, msg: &SignalMessage) -> Vec<u8> {
1911 dual_decrypt(
1912 msg,
1913 &self.alice,
1914 &self.bob,
1915 &mut self.nb,
1916 &mut self.lb,
1917 &mut self.rng,
1918 )
1919 }
1920
1921 fn alice_receives(&mut self, msg: &SignalMessage) -> Vec<u8> {
1922 dual_decrypt(
1923 msg,
1924 &self.bob,
1925 &self.alice,
1926 &mut self.na,
1927 &mut self.la,
1928 &mut self.rng,
1929 )
1930 }
1931
1932 fn bob_receives_err(&mut self, msg: &SignalMessage) -> SignalProtocolError {
1933 dual_decrypt_expect_err(
1934 msg,
1935 &self.alice,
1936 &self.bob,
1937 &mut self.nb,
1938 &mut self.lb,
1939 &mut self.rng,
1940 )
1941 }
1942
1943 #[allow(dead_code)]
1944 fn alice_receives_err(&mut self, msg: &SignalMessage) -> SignalProtocolError {
1945 dual_decrypt_expect_err(
1946 msg,
1947 &self.bob,
1948 &self.alice,
1949 &mut self.na,
1950 &mut self.la,
1951 &mut self.rng,
1952 )
1953 }
1954 }
1955
1956 impl DualParticipant {
1957 fn new(
1958 _name: &'static str,
1959 address: ProtocolAddress,
1960 rng: &mut (impl rand::Rng + rand::CryptoRng),
1961 ) -> Self {
1962 let identity = IdentityKeyPair::generate(rng);
1963 let store = InMemSignalProtocolStore::new(identity, rng.random()).unwrap();
1964 Self {
1965 address,
1966 message_queue: Vec::new(),
1967 state: DualLocalState {
1968 new_store: store.clone(),
1969 legacy_store: store,
1970 pre_key_count: 0,
1971 },
1972 snapshots: Vec::new(),
1973 message_send_log: Vec::new(),
1974 }
1975 }
1976
1977 fn address(&self) -> &ProtocolAddress {
1978 &self.address
1979 }
1980
1981 fn has_pending_incoming_messages(&self) -> bool {
1982 !self.message_queue.is_empty()
1983 }
1984
1985 fn assert_equivalent_with(&self, them: &Self, context: &str) {
1986 assert_store_state_equivalent(
1987 &self.state.new_store,
1988 &self.state.legacy_store,
1989 &them.address,
1990 context,
1991 );
1992 }
1993
1994 async fn process_pre_key(
1995 &mut self,
1996 them: &mut Self,
1997 use_one_time_pre_key: bool,
1998 rng: &mut ChaCha8Rng,
1999 ) {
2000 let their_signed_pre_key_pair = KeyPair::generate(rng);
2001 let their_signed_pre_key_public = their_signed_pre_key_pair.public_key.serialize();
2002 let identity_key_pair = them.state.new_store.get_identity_key_pair().await.unwrap();
2003 let their_signed_pre_key_signature = identity_key_pair
2004 .private_key()
2005 .calculate_signature(&their_signed_pre_key_public, rng)
2006 .unwrap();
2007
2008 them.state.pre_key_count += 1;
2009 let signed_pre_key_id: SignedPreKeyId = them.state.pre_key_count.into();
2010 let signed_pre_key_record = SignedPreKeyRecord::new(
2011 signed_pre_key_id,
2012 Timestamp::from_epoch_millis(42),
2013 &their_signed_pre_key_pair,
2014 &their_signed_pre_key_signature,
2015 );
2016 them.state
2017 .new_store
2018 .save_signed_pre_key(signed_pre_key_id, &signed_pre_key_record)
2019 .await
2020 .unwrap();
2021 them.state
2022 .legacy_store
2023 .save_signed_pre_key(signed_pre_key_id, &signed_pre_key_record)
2024 .await
2025 .unwrap();
2026
2027 them.state.pre_key_count += 1;
2028 let pre_key_id: PreKeyId = them.state.pre_key_count.into();
2029 let pre_key_info = if use_one_time_pre_key {
2030 let one_time_pre_key = KeyPair::generate(rng);
2031 let pre_key_record = PreKeyRecord::new(pre_key_id, &one_time_pre_key);
2032 them.state
2033 .new_store
2034 .save_pre_key(pre_key_id, &pre_key_record)
2035 .await
2036 .unwrap();
2037 them.state
2038 .legacy_store
2039 .save_pre_key(pre_key_id, &pre_key_record)
2040 .await
2041 .unwrap();
2042 Some((pre_key_id, one_time_pre_key.public_key))
2043 } else {
2044 None
2045 };
2046
2047 let their_kyber_pre_key_pair =
2048 crate::kem::KeyPair::generate(crate::kem::KeyType::Kyber1024, rng);
2049 let their_kyber_pre_key_public = their_kyber_pre_key_pair.public_key.serialize();
2050 let their_kyber_pre_key_signature = identity_key_pair
2051 .private_key()
2052 .calculate_signature(&their_kyber_pre_key_public, rng)
2053 .unwrap();
2054
2055 them.state.pre_key_count += 1;
2056 let kyber_pre_key_id: KyberPreKeyId = them.state.pre_key_count.into();
2057 let kyber_pre_key_record = KyberPreKeyRecord::new(
2058 kyber_pre_key_id,
2059 Timestamp::from_epoch_millis(42),
2060 &their_kyber_pre_key_pair,
2061 &their_kyber_pre_key_signature,
2062 );
2063 them.state
2064 .new_store
2065 .save_kyber_pre_key(kyber_pre_key_id, &kyber_pre_key_record)
2066 .await
2067 .unwrap();
2068 them.state
2069 .legacy_store
2070 .save_kyber_pre_key(kyber_pre_key_id, &kyber_pre_key_record)
2071 .await
2072 .unwrap();
2073
2074 let their_pre_key_bundle = PreKeyBundle::new(
2075 them.state
2076 .new_store
2077 .get_local_registration_id()
2078 .await
2079 .unwrap(),
2080 DeviceId::new(1).unwrap(),
2081 pre_key_info,
2082 signed_pre_key_id,
2083 their_signed_pre_key_pair.public_key,
2084 their_signed_pre_key_signature.into_vec(),
2085 kyber_pre_key_id,
2086 their_kyber_pre_key_pair.public_key,
2087 their_kyber_pre_key_signature.into_vec(),
2088 *identity_key_pair.identity_key(),
2089 )
2090 .unwrap();
2091
2092 let mut legacy_rng = rng.clone();
2093 process_prekey_bundle(
2094 &them.address,
2095 &self.address,
2096 &mut self.state.new_store.session_store,
2097 &mut self.state.new_store.identity_store,
2098 &their_pre_key_bundle,
2099 SystemTime::UNIX_EPOCH,
2100 rng,
2101 )
2102 .await
2103 .unwrap();
2104 process_prekey_bundle(
2105 &them.address,
2106 &self.address,
2107 &mut self.state.legacy_store.session_store,
2108 &mut self.state.legacy_store.identity_store,
2109 &their_pre_key_bundle,
2110 SystemTime::UNIX_EPOCH,
2111 &mut legacy_rng,
2112 )
2113 .await
2114 .unwrap();
2115
2116 self.assert_equivalent_with(them, "process_pre_key/self");
2117 them.assert_equivalent_with(self, "process_pre_key/them");
2118 assert!(
2119 self.state
2120 .new_store
2121 .load_session(&them.address)
2122 .await
2123 .unwrap()
2124 .expect("just created")
2125 .has_usable_sender_chain(
2126 SystemTime::UNIX_EPOCH,
2127 SessionUsabilityRequirements::all(),
2128 )
2129 .unwrap()
2130 );
2131 }
2132
2133 async fn send_message(&mut self, them: &mut Self, rng: &mut ChaCha8Rng) {
2134 self.send_message_with_id(them, self.message_send_log.len().try_into().unwrap(), rng)
2135 .await;
2136 self.message_send_log.push(MessageStatus::Sent);
2137 }
2138
2139 async fn send_message_with_id(&mut self, them: &mut Self, id: u64, rng: &mut ChaCha8Rng) {
2140 let has_usable_sender_chain = self
2141 .state
2142 .new_store
2143 .load_session(&them.address)
2144 .await
2145 .unwrap()
2146 .and_then(|session| {
2147 session
2148 .has_usable_sender_chain(
2149 SystemTime::UNIX_EPOCH,
2150 SessionUsabilityRequirements::all(),
2151 )
2152 .ok()
2153 })
2154 .unwrap_or(false);
2155
2156 if !has_usable_sender_chain {
2157 self.process_pre_key(them, rng.random_bool(0.75), rng).await;
2158 }
2159
2160 let buffer = id.to_le_bytes();
2161 let outgoing_message = dual_encrypt_any(
2162 &buffer,
2163 &them.address,
2164 &self.address,
2165 &mut self.state.new_store,
2166 &mut self.state.legacy_store,
2167 SystemTime::UNIX_EPOCH,
2168 rng,
2169 );
2170
2171 let incoming_message = match outgoing_message.message_type() {
2172 CiphertextMessageType::PreKey => CiphertextMessage::PreKeySignalMessage(
2173 PreKeySignalMessage::try_from(outgoing_message.serialize()).unwrap(),
2174 ),
2175 CiphertextMessageType::Whisper => CiphertextMessage::SignalMessage(
2176 SignalMessage::try_from(outgoing_message.serialize()).unwrap(),
2177 ),
2178 other_type => panic!("unexpected type {other_type:?}"),
2179 };
2180
2181 them.message_queue.push((incoming_message, id));
2182 self.assert_equivalent_with(them, "send");
2183 }
2184
2185 async fn receive_messages(&mut self, them: &mut Self, rng: &mut ChaCha8Rng) {
2186 for (incoming_message, expected) in self.message_queue.split_off(0) {
2187 match incoming_message {
2188 CiphertextMessage::SignalMessage(_)
2189 | CiphertextMessage::PreKeySignalMessage(_) => {
2190 match dual_decrypt_any_result(
2191 &incoming_message,
2192 &them.address,
2193 &self.address,
2194 &mut self.state.new_store,
2195 &mut self.state.legacy_store,
2196 rng,
2197 ) {
2198 Ok(decrypted) => {
2199 assert_eq!(expected.to_le_bytes(), &decrypted[..]);
2200 them.ack(expected);
2201 }
2202 Err(_) => {
2203 let error_msg = DecryptionErrorMessage::for_original(
2204 incoming_message.serialize(),
2205 incoming_message.message_type(),
2206 Timestamp::from_epoch_millis(expected),
2207 1,
2208 )
2209 .expect("can encode DEM");
2210 them.message_queue.push((
2211 CiphertextMessage::PlaintextContent(error_msg.into()),
2212 u64::MAX,
2213 ));
2214 }
2215 }
2216 }
2217 CiphertextMessage::SenderKeyMessage(_) => unreachable!(),
2218 CiphertextMessage::PlaintextContent(content) => {
2219 self.handle_decryption_error(them, content, rng).await;
2220 }
2221 }
2222 }
2223 self.assert_equivalent_with(them, "receive");
2224 them.assert_equivalent_with(self, "receive/peer");
2225 }
2226
2227 fn drop_message(&mut self, them: &mut Self) {
2228 match self.message_queue.pop() {
2229 None | Some((CiphertextMessage::PlaintextContent(_), _)) => {}
2230 Some((_, id)) => them.nack(id),
2231 }
2232 }
2233
2234 fn shuffle_messages(&mut self, rng: &mut impl rand::Rng) {
2235 use rand::seq::SliceRandom as _;
2236 self.message_queue.shuffle(rng);
2237 }
2238
2239 async fn handle_decryption_error(
2240 &mut self,
2241 them: &mut Self,
2242 content: PlaintextContent,
2243 rng: &mut ChaCha8Rng,
2244 ) {
2245 let dem = extract_decryption_error_message_from_serialized_content(content.body())
2246 .expect("all PlaintextContent is DEM");
2247 assert_eq!(dem.device_id(), 1);
2248
2249 let id = dem.timestamp().epoch_millis();
2250 let Some(status) = self.message_send_log.get(usize::try_from(id).unwrap()) else {
2251 panic!(
2252 "failed to decrypt an unsent message {id} ({} total sent)",
2253 self.message_send_log.len()
2254 )
2255 };
2256 match status {
2257 MessageStatus::Sent => {}
2258 MessageStatus::Dropped => {
2259 panic!("got a decryption error for dropped message {id}");
2260 }
2261 MessageStatus::Delivered => {
2262 panic!("got a decryption error for successfully delivered message {id}");
2263 }
2264 }
2265
2266 let ratchet_key = dem
2267 .ratchet_key()
2268 .expect("all DEMs for 1:1 messages have ratchet keys");
2269 if self
2270 .state
2271 .new_store
2272 .load_session(&them.address)
2273 .await
2274 .unwrap()
2275 .is_some_and(|session| {
2276 session
2277 .current_ratchet_key_matches(ratchet_key)
2278 .expect("structurally valid session")
2279 })
2280 {
2281 self.archive_session(&them.address).await;
2282 }
2283
2284 self.send_message_with_id(them, id, rng).await;
2285 }
2286
2287 async fn archive_session(&mut self, their_address: &ProtocolAddress) {
2288 for store in [&mut self.state.new_store, &mut self.state.legacy_store] {
2289 if let Some(mut session) = store.load_session(their_address).await.unwrap() {
2290 session.archive_current_state().unwrap();
2291 store.store_session(their_address, &session).await.unwrap();
2292 }
2293 }
2294 }
2295
2296 fn snapshot_state(&mut self) {
2297 self.snapshots.push(self.state.clone());
2298 }
2299
2300 fn restore_from_snapshot_if_exists(&mut self, i: u8) {
2301 let i = usize::from(i);
2302 if i < self.snapshots.len() {
2303 self.state = self.snapshots.remove(i);
2304 }
2305 }
2306
2307 fn ack(&mut self, id: u64) {
2308 self.update_status(id, MessageStatus::Delivered);
2309 }
2310
2311 fn nack(&mut self, id: u64) {
2312 self.update_status(id, MessageStatus::Dropped);
2313 }
2314
2315 fn update_status(&mut self, id: u64, updated_status: MessageStatus) {
2316 let Some(status) = self.message_send_log.get_mut(usize::try_from(id).unwrap()) else {
2317 panic!(
2318 "tried to update unsent message {id} ({} total sent)",
2319 self.message_send_log.len()
2320 )
2321 };
2322 match status {
2323 MessageStatus::Sent => *status = updated_status,
2324 MessageStatus::Dropped => panic!("updated dropped message {id}"),
2325 MessageStatus::Delivered => panic!("updated delivered message {id}"),
2326 }
2327 }
2328
2329 async fn run_event(&mut self, them: &mut Self, event: Event, rng: &mut ChaCha8Rng) {
2330 match event {
2331 Event::Archive => self.archive_session(them.address()).await,
2332 Event::Snapshot => self.snapshot_state(),
2333 Event::Restore { index } => self.restore_from_snapshot_if_exists(index),
2334 Event::Receive => self.receive_messages(them, rng).await,
2335 Event::Drop => self.drop_message(them),
2336 Event::Shuffle => self.shuffle_messages(rng),
2337 Event::Send { count_times_eight } => {
2338 for _ in 0..(count_times_eight / 8) {
2339 self.send_message(them, rng).await;
2340 }
2341 }
2342 }
2343 self.assert_equivalent_with(them, "event");
2344 them.assert_equivalent_with(self, "event/peer");
2345 }
2346 }
2347
2348 #[test]
2354 fn scenario_interleaved_delivery_with_gaps_and_recovery() {
2355 let mut s = DualSession::new(0xBEEF_0001);
2356
2357 let a_msgs: Vec<_> = (0u8..4)
2360 .map(|i| (s.alice_sends(&[b'A', i]), vec![b'A', i]))
2361 .collect();
2362 assert_eq!(s.bob_receives(&a_msgs[0].0), a_msgs[0].1, "alice msg 0");
2363 assert_eq!(s.bob_receives(&a_msgs[2].0), a_msgs[2].1, "alice msg 2");
2364
2365 let b_msgs: Vec<_> = (0u8..3)
2369 .map(|i| (s.bob_sends(&[b'B', i]), vec![b'B', i]))
2370 .collect();
2371 assert_eq!(s.alice_receives(&b_msgs[2].0), b_msgs[2].1, "bob msg 2");
2372
2373 assert_eq!(s.bob_receives(&a_msgs[1].0), a_msgs[1].1, "alice msg 1");
2375 assert_eq!(s.bob_receives(&a_msgs[3].0), a_msgs[3].1, "alice msg 3");
2376 assert_eq!(s.alice_receives(&b_msgs[0].0), b_msgs[0].1, "bob msg 0");
2377 assert_eq!(s.alice_receives(&b_msgs[1].0), b_msgs[1].1, "bob msg 1");
2378
2379 let alice_followup = s.alice_sends(b"alice steady");
2382 assert_eq!(s.bob_receives(&alice_followup), b"alice steady");
2383 let bob_followup = s.bob_sends(b"bob steady");
2384 assert_eq!(s.alice_receives(&bob_followup), b"bob steady");
2385 }
2386
2387 #[test]
2391 fn scenario_chain_jump_over_limit() {
2392 let mut rng = ChaCha8Rng::seed_from_u64(0xBEEF_0005);
2393 let (mut na, mut nb, alice, bob) = setup_stores(&mut rng);
2394 let mut lb = nb.clone();
2395 let now = SystemTime::now();
2396
2397 let count = crate::consts::MAX_FORWARD_JUMPS + 2;
2398 let mut last = None;
2399 for _ in 0..count {
2400 let ct = message_encrypt(
2401 b"x",
2402 &bob,
2403 &alice,
2404 &mut na.session_store,
2405 &mut na.identity_store,
2406 now,
2407 &mut rng,
2408 )
2409 .now_or_never()
2410 .expect("sync")
2411 .expect("encrypt");
2412 last = Some(match ct {
2413 CiphertextMessage::SignalMessage(m) => m,
2414 _ => panic!("not SignalMessage"),
2415 });
2416 }
2417 let msg = last.unwrap();
2418
2419 let mut leg_rng = rng.clone();
2421 let new_err = message_decrypt_signal(
2422 &msg,
2423 &alice,
2424 &bob,
2425 &mut nb.session_store,
2426 &mut nb.identity_store,
2427 &mut rng,
2428 )
2429 .now_or_never()
2430 .expect("sync")
2431 .expect_err("should exceed jump limit");
2432
2433 let leg_err = legacy::legacy_message_decrypt_signal(
2434 &msg,
2435 &alice,
2436 &mut lb.session_store,
2437 &mut lb.identity_store,
2438 &mut leg_rng,
2439 )
2440 .now_or_never()
2441 .expect("sync")
2442 .expect_err("should exceed jump limit");
2443
2444 assert_eq!(
2445 std::mem::discriminant(&new_err),
2446 std::mem::discriminant(&leg_err),
2447 "error variants differ: new={new_err:?}, legacy={leg_err:?}"
2448 );
2449 assert!(
2450 matches!(new_err, SignalProtocolError::InvalidMessage(..)),
2451 "expected InvalidMessage, got {new_err:?}"
2452 );
2453 }
2454
2455 #[test]
2459 fn scenario_prekey_session_establishment_equivalence() {
2460 let mut rng = ChaCha8Rng::seed_from_u64(0xBEEF_0008);
2461 let alice_identity = IdentityKeyPair::generate(&mut rng);
2462 let bob_identity = IdentityKeyPair::generate(&mut rng);
2463 let alice = ProtocolAddress::new(
2464 "9d0652a3-dcc3-4d11-975f-74d61598733f".to_owned(),
2465 DeviceId::new(1).unwrap(),
2466 );
2467 let bob = ProtocolAddress::new(
2468 "796abedb-ca4e-4f18-8803-1fde5b921f9f".to_owned(),
2469 DeviceId::new(1).unwrap(),
2470 );
2471 let now = SystemTime::now();
2472
2473 let alice_base = InMemSignalProtocolStore::new(alice_identity, 1).expect("alice store");
2474 let mut bob_base = InMemSignalProtocolStore::new(bob_identity, 2).expect("bob store");
2475 let bundle = create_bob_bundle(&mut bob_base, 1, 1, 1, &mut rng);
2476
2477 let (mut alice_new, mut alice_legacy) = (alice_base.clone(), alice_base.clone());
2478 let (mut bob_new, mut bob_legacy) = (bob_base.clone(), bob_base.clone());
2479
2480 let mut legacy_rng = rng.clone();
2481 process_prekey_bundle(
2482 &bob,
2483 &alice,
2484 &mut alice_new.session_store,
2485 &mut alice_new.identity_store,
2486 &bundle,
2487 now,
2488 &mut rng,
2489 )
2490 .now_or_never()
2491 .expect("sync")
2492 .expect("new process_prekey_bundle");
2493 process_prekey_bundle(
2494 &bob,
2495 &alice,
2496 &mut alice_legacy.session_store,
2497 &mut alice_legacy.identity_store,
2498 &bundle,
2499 now,
2500 &mut legacy_rng,
2501 )
2502 .now_or_never()
2503 .expect("sync")
2504 .expect("legacy process_prekey_bundle");
2505 assert_store_state_equivalent(&alice_new, &alice_legacy, &bob, "post-bundle");
2506
2507 let init = dual_encrypt_any(
2508 b"session init",
2509 &bob,
2510 &alice,
2511 &mut alice_new,
2512 &mut alice_legacy,
2513 now,
2514 &mut rng,
2515 );
2516 assert!(
2517 matches!(init, CiphertextMessage::PreKeySignalMessage(_)),
2518 "expected first message after bundle processing to be PreKey"
2519 );
2520
2521 assert_eq!(
2522 dual_decrypt_any(&init, &alice, &bob, &mut bob_new, &mut bob_legacy, &mut rng),
2523 b"session init"
2524 );
2525
2526 let ack = dual_encrypt_any(
2527 b"session ack",
2528 &alice,
2529 &bob,
2530 &mut bob_new,
2531 &mut bob_legacy,
2532 now,
2533 &mut rng,
2534 );
2535 assert!(
2536 matches!(ack, CiphertextMessage::SignalMessage(_)),
2537 "expected ack to be a SignalMessage"
2538 );
2539 assert_eq!(
2540 dual_decrypt_any(
2541 &ack,
2542 &bob,
2543 &alice,
2544 &mut alice_new,
2545 &mut alice_legacy,
2546 &mut rng
2547 ),
2548 b"session ack"
2549 );
2550
2551 let followup = dual_encrypt_any(
2552 b"steady state",
2553 &bob,
2554 &alice,
2555 &mut alice_new,
2556 &mut alice_legacy,
2557 now,
2558 &mut rng,
2559 );
2560 assert!(
2561 matches!(followup, CiphertextMessage::SignalMessage(_)),
2562 "expected acknowledged session to emit SignalMessage"
2563 );
2564 assert_eq!(
2565 dual_decrypt_any(
2566 &followup,
2567 &alice,
2568 &bob,
2569 &mut bob_new,
2570 &mut bob_legacy,
2571 &mut rng
2572 ),
2573 b"steady state"
2574 );
2575 }
2576
2577 #[test]
2580 fn scenario_corrupted_ciphertext() {
2581 let mut s = DualSession::new(0xBEEF_0006);
2582
2583 let msg = s.alice_sends(b"hello");
2585 assert_eq!(s.bob_receives(&msg), b"hello");
2586 let msg = s.bob_sends(b"hi");
2587 assert_eq!(s.alice_receives(&msg), b"hi");
2588
2589 let msg = s.alice_sends(b"secret");
2591 let mut corrupted_bytes = msg.serialized().to_vec();
2592 let len = corrupted_bytes.len();
2593 corrupted_bytes[len - 1] ^= 0xFF;
2594 let corrupted =
2595 SignalMessage::try_from(corrupted_bytes.as_slice()).expect("parse corrupted message");
2596
2597 let err = s.bob_receives_err(&corrupted);
2598 assert!(
2599 matches!(
2600 err,
2601 SignalProtocolError::InvalidMessage(CiphertextMessageType::Whisper, _)
2602 ),
2603 "expected InvalidMessage(Whisper, _), got {err:?}"
2604 );
2605
2606 assert_eq!(s.bob_receives(&msg), b"secret");
2609 }
2610
2611 #[test]
2614 fn scenario_replay_message() {
2615 let mut s = DualSession::new(0xBEEF_0007);
2616
2617 let msg = s.alice_sends(b"once");
2618 assert_eq!(s.bob_receives(&msg), b"once");
2619
2620 let err = s.bob_receives_err(&msg);
2622 assert!(
2623 matches!(err, SignalProtocolError::DuplicatedMessage(..)),
2624 "expected DuplicatedMessage, got {err:?}"
2625 );
2626 }
2627
2628 proptest! {
2629 #[test]
2632 fn proptest_event_model_matches_legacy(
2633 actions in prop::collection::vec(
2634 (prop::bool::ANY, proptest_arbitrary_interop::arb::<Event>()),
2635 0..40,
2636 ),
2637 ) {
2638 let mut rng = ChaCha8Rng::seed_from_u64(0);
2639 let mut alice = DualParticipant::new(
2640 "alice",
2641 ProtocolAddress::new("9d0652a3-dcc3-4d11-975f-74d61598733f".to_owned(), DeviceId::new(1).unwrap()),
2642 &mut rng,
2643 );
2644 let mut bob = DualParticipant::new(
2645 "bob",
2646 ProtocolAddress::new("796abedb-ca4e-4f18-8803-1fde5b921f9f".to_owned(), DeviceId::new(1).unwrap()),
2647 &mut rng,
2648 );
2649
2650 for (who, event) in actions {
2651 let (me, them) = if who {
2652 (&mut alice, &mut bob)
2653 } else {
2654 (&mut bob, &mut alice)
2655 };
2656 me.run_event(them, event, &mut rng)
2657 .now_or_never()
2658 .expect("sync");
2659 }
2660
2661 while alice.has_pending_incoming_messages() || bob.has_pending_incoming_messages() {
2662 alice
2663 .receive_messages(&mut bob, &mut rng)
2664 .now_or_never()
2665 .expect("sync");
2666 bob.receive_messages(&mut alice, &mut rng)
2667 .now_or_never()
2668 .expect("sync");
2669 }
2670
2671 for _ in 0..8 {
2672 alice
2673 .send_message(&mut bob, &mut rng)
2674 .now_or_never()
2675 .expect("sync");
2676 bob.receive_messages(&mut alice, &mut rng)
2677 .now_or_never()
2678 .expect("sync");
2679 bob.send_message(&mut alice, &mut rng)
2680 .now_or_never()
2681 .expect("sync");
2682 alice
2683 .receive_messages(&mut bob, &mut rng)
2684 .now_or_never()
2685 .expect("sync");
2686 }
2687
2688 alice.assert_equivalent_with(&bob, "final/alice");
2689 bob.assert_equivalent_with(&alice, "final/bob");
2690 }
2691 }
2692
2693 proptest! {
2694 #[test]
2702 fn proptest_legacy_handover_to_new(
2703 seed in 0u64..u64::MAX,
2704 legacy_actions in prop::collection::vec(
2705 (prop::bool::ANY, prop::collection::vec(any::<u8>(), 0..=64)),
2706 1..=10,
2707 ),
2708 new_actions in prop::collection::vec(
2709 (prop::bool::ANY, prop::collection::vec(any::<u8>(), 0..=64)),
2710 1..=10,
2711 ),
2712 ) {
2713 let mut rng = ChaCha8Rng::seed_from_u64(seed);
2714 let (mut alice_store, mut bob_store, alice_address, bob_address) =
2715 setup_stores(&mut rng);
2716 let now = std::time::SystemTime::now();
2717
2718 for (alice_sends, plaintext) in &legacy_actions {
2720 let (sender, receiver, recv_addr, send_addr) = if *alice_sends {
2721 (&mut alice_store, &mut bob_store, &bob_address, &alice_address)
2722 } else {
2723 (&mut bob_store, &mut alice_store, &alice_address, &bob_address)
2724 };
2725
2726 let ct = legacy::legacy_message_encrypt(
2727 plaintext,
2728 recv_addr,
2729 send_addr,
2730 &mut sender.session_store,
2731 &mut sender.identity_store,
2732 now,
2733 &mut rng,
2734 )
2735 .now_or_never()
2736 .expect("sync")
2737 .expect("legacy enc");
2738
2739 let signal_msg = match &ct {
2740 CiphertextMessage::SignalMessage(m) => m,
2741 other => panic!(
2742 "expected SignalMessage in legacy phase, got {:?}",
2743 other.message_type()
2744 ),
2745 };
2746
2747 let ptext = legacy::legacy_message_decrypt_signal(
2748 signal_msg,
2749 send_addr,
2750 &mut receiver.session_store,
2751 &mut receiver.identity_store,
2752 &mut rng,
2753 )
2754 .now_or_never()
2755 .expect("sync")
2756 .expect("legacy dec");
2757
2758 prop_assert_eq!(ptext, plaintext.clone(), "legacy phase: wrong plaintext");
2759 }
2760
2761 for (alice_sends, plaintext) in &new_actions {
2763 let (sender, receiver, recv_addr, send_addr) = if *alice_sends {
2764 (&mut alice_store, &mut bob_store, &bob_address, &alice_address)
2765 } else {
2766 (&mut bob_store, &mut alice_store, &alice_address, &bob_address)
2767 };
2768
2769 let ct = message_encrypt(
2770 plaintext,
2771 recv_addr,
2772 send_addr,
2773 &mut sender.session_store,
2774 &mut sender.identity_store,
2775 now,
2776 &mut rng,
2777 )
2778 .now_or_never()
2779 .expect("sync")
2780 .expect("new enc");
2781
2782 let signal_msg = match &ct {
2783 CiphertextMessage::SignalMessage(m) => m,
2784 other => panic!(
2785 "expected SignalMessage in new phase, got {:?}",
2786 other.message_type()
2787 ),
2788 };
2789
2790 let ptext = message_decrypt_signal(
2791 signal_msg,
2792 send_addr,
2793 recv_addr,
2794 &mut receiver.session_store,
2795 &mut receiver.identity_store,
2796 &mut rng,
2797 )
2798 .now_or_never()
2799 .expect("sync")
2800 .expect("new dec");
2801
2802 prop_assert_eq!(ptext, plaintext.clone(), "new phase: wrong plaintext");
2803 }
2804 }
2805
2806 #[test]
2824 fn proptest_delayed_message_via_previous_session(
2825 seed in 0u64..u64::MAX,
2826 pre_actions in prop::collection::vec(
2827 (prop::bool::ANY, prop::collection::vec(any::<u8>(), 0..=6)),
2828 0..=6,
2829 ),
2830 post_actions in prop::collection::vec(
2831 (prop::bool::ANY, prop::collection::vec(any::<u8>(), 0..=64)),
2832 0..=6,
2833 ),
2834 delayed_plaintext in prop::collection::vec(any::<u8>(), 1..=64),
2835 ) {
2836 let mut rng = ChaCha8Rng::seed_from_u64(seed);
2837 let (mut alice_store, mut bob_store, alice_address, bob_address) =
2838 setup_stores(&mut rng);
2839 let now = std::time::SystemTime::now();
2840
2841 for (alice_sends, plaintext) in &pre_actions {
2844 let (sender, receiver, recv_addr, send_addr) = if *alice_sends {
2845 (&mut alice_store, &mut bob_store, &bob_address, &alice_address)
2846 } else {
2847 (&mut bob_store, &mut alice_store, &alice_address, &bob_address)
2848 };
2849
2850 let ct = legacy::legacy_message_encrypt(
2851 plaintext,
2852 recv_addr,
2853 send_addr,
2854 &mut sender.session_store,
2855 &mut sender.identity_store,
2856 now,
2857 &mut rng,
2858 )
2859 .now_or_never()
2860 .expect("sync")
2861 .expect("pre legacy enc");
2862
2863 let signal_msg = match &ct {
2864 CiphertextMessage::SignalMessage(m) => m,
2865 other => panic!(
2866 "expected SignalMessage in pre phase, got {:?}",
2867 other.message_type()
2868 ),
2869 };
2870
2871 let ptext = legacy::legacy_message_decrypt_signal(
2872 signal_msg,
2873 send_addr,
2874 &mut receiver.session_store,
2875 &mut receiver.identity_store,
2876 &mut rng,
2877 )
2878 .now_or_never()
2879 .expect("sync")
2880 .expect("pre legacy dec");
2881
2882 prop_assert_eq!(ptext, plaintext.clone(), "pre phase: wrong plaintext");
2883 }
2884
2885 let delayed_ct = legacy::legacy_message_encrypt(
2891 &delayed_plaintext,
2892 &bob_address,
2893 &alice_address,
2894 &mut alice_store.session_store,
2895 &mut alice_store.identity_store,
2896 now,
2897 &mut rng,
2898 )
2899 .now_or_never()
2900 .expect("sync")
2901 .expect("delayed legacy enc");
2902
2903 let delayed_signal_msg = match delayed_ct {
2904 CiphertextMessage::SignalMessage(m) => m,
2905 other => panic!(
2906 "expected SignalMessage for delayed msg, got {:?}",
2907 other.message_type()
2908 ),
2909 };
2910
2911 let bundle = create_bob_bundle(&mut bob_store, 1, 1, 1, &mut rng);
2916 process_prekey_bundle(
2917 &bob_address,
2918 &alice_address,
2919 &mut alice_store.session_store,
2920 &mut alice_store.identity_store,
2921 &bundle,
2922 now,
2923 &mut rng,
2924 )
2925 .now_or_never()
2926 .expect("sync")
2927 .expect("process_prekey_bundle");
2928
2929 let session_b_init = message_encrypt(
2933 b"session B init",
2934 &bob_address,
2935 &alice_address,
2936 &mut alice_store.session_store,
2937 &mut alice_store.identity_store,
2938 now,
2939 &mut rng,
2940 )
2941 .now_or_never()
2942 .expect("sync")
2943 .expect("session B init enc");
2944
2945 message_decrypt(
2946 &session_b_init,
2947 &alice_address,
2948 &bob_address,
2949 &mut bob_store.session_store,
2950 &mut bob_store.identity_store,
2951 &mut bob_store.pre_key_store,
2952 &bob_store.signed_pre_key_store,
2953 &mut bob_store.kyber_pre_key_store,
2954 &mut rng,
2955 )
2956 .now_or_never()
2957 .expect("sync")
2958 .expect("session B init dec");
2959 let session_b_ack = message_encrypt(
2966 b"session B ack",
2967 &alice_address,
2968 &bob_address,
2969 &mut bob_store.session_store,
2970 &mut bob_store.identity_store,
2971 now,
2972 &mut rng,
2973 )
2974 .now_or_never()
2975 .expect("sync")
2976 .expect("session B ack enc");
2977
2978 let session_b_ack_signal = match &session_b_ack {
2979 CiphertextMessage::SignalMessage(m) => m,
2980 other => panic!(
2981 "expected Whisper for session B ack, got {:?}",
2982 other.message_type()
2983 ),
2984 };
2985 message_decrypt_signal(
2986 session_b_ack_signal,
2987 &bob_address,
2988 &alice_address,
2989 &mut alice_store.session_store,
2990 &mut alice_store.identity_store,
2991 &mut rng,
2992 )
2993 .now_or_never()
2994 .expect("sync")
2995 .expect("session B ack dec");
2996 for (alice_sends, plaintext) in &post_actions {
3001 let (sender, receiver, recv_addr, send_addr) = if *alice_sends {
3002 (&mut alice_store, &mut bob_store, &bob_address, &alice_address)
3003 } else {
3004 (&mut bob_store, &mut alice_store, &alice_address, &bob_address)
3005 };
3006
3007 let ct = message_encrypt(
3008 plaintext,
3009 recv_addr,
3010 send_addr,
3011 &mut sender.session_store,
3012 &mut sender.identity_store,
3013 now,
3014 &mut rng,
3015 )
3016 .now_or_never()
3017 .expect("sync")
3018 .expect("post new enc");
3019
3020 let signal_msg = match &ct {
3021 CiphertextMessage::SignalMessage(m) => m,
3022 other => panic!(
3023 "expected SignalMessage in post phase, got {:?}",
3024 other.message_type()
3025 ),
3026 };
3027
3028 let ptext = message_decrypt_signal(
3029 signal_msg,
3030 send_addr,
3031 recv_addr,
3032 &mut receiver.session_store,
3033 &mut receiver.identity_store,
3034 &mut rng,
3035 )
3036 .now_or_never()
3037 .expect("sync")
3038 .expect("post new dec");
3039
3040 prop_assert_eq!(ptext, plaintext.clone(), "post phase: wrong plaintext");
3041 }
3042
3043 let ptext = message_decrypt_signal(
3051 &delayed_signal_msg,
3052 &alice_address,
3053 &bob_address,
3054 &mut bob_store.session_store,
3055 &mut bob_store.identity_store,
3056 &mut rng,
3057 )
3058 .now_or_never()
3059 .expect("sync")
3060 .expect("delayed msg dec via previous session");
3061
3062 prop_assert_eq!(
3063 ptext,
3064 delayed_plaintext.clone(),
3065 "delayed message: wrong plaintext"
3066 );
3067 }
3068
3069 #[test]
3080 fn proptest_ciphertext_equality(
3081 seed in 0u64..u64::MAX,
3082 actions in prop::collection::vec(
3083 (prop::bool::ANY, prop::collection::vec(any::<u8>(), 0..=64)),
3084 1..=20,
3085 ),
3086 ) {
3087 let mut rng = ChaCha8Rng::seed_from_u64(seed);
3088 let (mut alice_new, mut bob_new, alice_address, bob_address) =
3089 setup_stores(&mut rng);
3090 let (mut alice_legacy, mut bob_legacy) = (alice_new.clone(), bob_new.clone());
3093 let now = SystemTime::now();
3094
3095 for (alice_sends, plaintext) in &actions {
3096 let (
3098 (sender_new, receiver_new),
3099 (sender_legacy, receiver_legacy),
3100 sender_addr,
3101 receiver_addr,
3102 ) = if *alice_sends {
3103 (
3104 (&mut alice_new, &mut bob_new),
3105 (&mut alice_legacy, &mut bob_legacy),
3106 &alice_address,
3107 &bob_address,
3108 )
3109 } else {
3110 (
3111 (&mut bob_new, &mut alice_new),
3112 (&mut bob_legacy, &mut alice_legacy),
3113 &bob_address,
3114 &alice_address,
3115 )
3116 };
3117
3118 let mut enc_rng = rng.clone();
3120
3121 let new_ct = message_encrypt(
3122 plaintext,
3123 receiver_addr,
3124 sender_addr,
3125 &mut sender_new.session_store,
3126 &mut sender_new.identity_store,
3127 now,
3128 &mut rng,
3129 )
3130 .now_or_never()
3131 .expect("sync")
3132 .expect("new encrypt succeeded");
3133
3134 let legacy_ct = legacy::legacy_message_encrypt(
3135 plaintext,
3136 receiver_addr,
3137 sender_addr,
3138 &mut sender_legacy.session_store,
3139 &mut sender_legacy.identity_store,
3140 now,
3141 &mut enc_rng,
3142 )
3143 .now_or_never()
3144 .expect("sync")
3145 .expect("legacy encrypt succeeded");
3146
3147 let new_msg = match &new_ct {
3148 CiphertextMessage::SignalMessage(m) => m,
3149 other => panic!(
3150 "expected SignalMessage from new enc, got {:?}",
3151 other.message_type()
3152 ),
3153 };
3154 let legacy_msg = match &legacy_ct {
3155 CiphertextMessage::SignalMessage(m) => m,
3156 other => panic!(
3157 "expected SignalMessage from legacy enc, got {:?}",
3158 other.message_type()
3159 ),
3160 };
3161
3162 prop_assert_eq!(
3163 new_msg.serialized(),
3164 legacy_msg.serialized(),
3165 "new and legacy produced different ciphertexts from the same RNG state"
3166 );
3167
3168 let mut dec_rng = rng.clone();
3171
3172 let _ = message_decrypt_signal(
3173 new_msg,
3174 sender_addr,
3175 receiver_addr,
3176 &mut receiver_new.session_store,
3177 &mut receiver_new.identity_store,
3178 &mut rng,
3179 )
3180 .now_or_never()
3181 .expect("sync")
3182 .expect("new decrypt succeeded");
3183
3184 let _ = legacy::legacy_message_decrypt_signal(
3185 legacy_msg,
3186 sender_addr,
3187 &mut receiver_legacy.session_store,
3188 &mut receiver_legacy.identity_store,
3189 &mut dec_rng,
3190 )
3191 .now_or_never()
3192 .expect("sync")
3193 .expect("legacy decrypt succeeded");
3194 }
3195 }
3196 }
3197}