1use std::cell::RefCell;
7
8use rand::{CryptoRng, Rng};
9
10use crate::crypto::DecryptionError as DecryptionErrorCrypto;
11use crate::crypto::{
12 aes_256_cbc_decrypt_in_place, aes_256_cbc_decrypt_into, aes_256_cbc_encrypt_into,
13};
14
15thread_local! {
17 static ENCRYPTION_BUFFER: RefCell<EncryptionBuffer> = RefCell::new(EncryptionBuffer::new());
18 static DECRYPTION_BUFFER: RefCell<EncryptionBuffer> = RefCell::new(EncryptionBuffer::new());
19}
20
21struct EncryptionBuffer {
23 buffer: Vec<u8>,
24 usage_count: usize,
25}
26
27impl EncryptionBuffer {
28 const INITIAL_CAPACITY: usize = 1024;
29 const MAX_CAPACITY: usize = 16 * 1024; const SHRINK_THRESHOLD: usize = 100; fn new() -> Self {
33 Self {
34 buffer: Vec::with_capacity(Self::INITIAL_CAPACITY),
35 usage_count: 0,
36 }
37 }
38
39 fn get_buffer(&mut self) -> &mut Vec<u8> {
40 self.usage_count += 1;
41
42 if self.usage_count.is_multiple_of(Self::SHRINK_THRESHOLD) {
44 if self.buffer.capacity() > Self::MAX_CAPACITY {
46 self.buffer = Vec::with_capacity(Self::INITIAL_CAPACITY);
47 } else if self.buffer.is_empty() && self.buffer.capacity() > Self::INITIAL_CAPACITY * 2
48 {
49 self.buffer.shrink_to(Self::INITIAL_CAPACITY);
51 }
52 }
53
54 &mut self.buffer
55 }
56}
57
58#[cfg(test)]
61fn encryption_buffer_capacity() -> usize {
62 ENCRYPTION_BUFFER.with(|b| b.borrow().buffer.capacity())
63}
64use crate::protocol::consts::MAX_FORWARD_JUMPS;
65
66const fn forward_jump_limit(is_self: bool) -> usize {
70 if is_self {
71 crate::protocol::consts::MAX_FORWARD_JUMPS_SELF
72 } else {
73 MAX_FORWARD_JUMPS
74 }
75}
76use crate::protocol::ratchet::keys::MessageKeyGenerator;
77use crate::protocol::ratchet::{ChainKey, RootKey, UsePQRatchet};
78use crate::protocol::state::{DecryptSnapshot, PreKeyId, ReceiverChainState, SessionState};
79use crate::protocol::{
80 CiphertextMessage, CiphertextMessageType, Direction, IdentityChange, IdentityKeyStore, KeyPair,
81 PreKeySignalMessage, PreKeyStore, ProtocolAddress, PublicKey, Result, SessionCheckout,
82 SessionRecord, SessionStore, SignalMessage, SignalProtocolError, SignedPreKeyStore, session,
83};
84
85#[derive(Debug)]
90pub struct DecryptionResult {
91 pub plaintext: Vec<u8>,
92 pub identity_change: IdentityChange,
93 pub consumed_prekey_id: Option<PreKeyId>,
100}
101
102#[derive(Debug)]
110pub struct OwnedCiphertextMessage {
111 inner: Option<CiphertextMessage>,
112}
113
114impl OwnedCiphertextMessage {
115 pub fn new(message: CiphertextMessage) -> Self {
116 Self {
117 inner: Some(message),
118 }
119 }
120
121 pub fn is_available(&self) -> bool {
122 self.inner.is_some()
123 }
124
125 fn message_type(&self) -> Option<CiphertextMessageType> {
126 self.inner.as_ref().map(CiphertextMessage::message_type)
127 }
128
129 fn pre_key_message(&self) -> Option<&PreKeySignalMessage> {
130 match self.inner.as_ref()? {
131 CiphertextMessage::PreKeySignalMessage(message) => Some(message),
132 _ => None,
133 }
134 }
135
136 fn signal_message(&self) -> Option<&SignalMessage> {
137 match self.inner.as_ref()? {
138 CiphertextMessage::SignalMessage(message) => Some(message),
139 CiphertextMessage::PreKeySignalMessage(message) => Some(message.message()),
140 _ => None,
141 }
142 }
143
144 fn take_signal_message(&mut self) -> Option<SignalMessage> {
145 match self.inner.take()? {
146 CiphertextMessage::SignalMessage(message) => Some(message),
147 CiphertextMessage::PreKeySignalMessage(message) => Some(message.into_message()),
148 other => {
149 self.inner = Some(other);
150 None
151 }
152 }
153 }
154}
155
156impl From<CiphertextMessage> for OwnedCiphertextMessage {
157 fn from(message: CiphertextMessage) -> Self {
158 Self::new(message)
159 }
160}
161
162enum SignalDecryptInput<'a> {
166 Borrowed(&'a SignalMessage),
167 Owned(&'a mut OwnedCiphertextMessage),
168}
169
170impl SignalDecryptInput<'_> {
171 #[inline(always)]
172 fn signal_message(&self) -> &SignalMessage {
173 match self {
174 Self::Borrowed(message) => message,
175 Self::Owned(message) => message
176 .signal_message()
177 .expect("owned Signal decrypt input has a Signal envelope"),
178 }
179 }
180
181 #[inline(always)]
182 fn is_available(&self) -> bool {
183 match self {
184 Self::Borrowed(_) => true,
185 Self::Owned(message) => message.is_available(),
186 }
187 }
188
189 fn decrypt(
190 &mut self,
191 key: &[u8],
192 iv: &[u8],
193 ) -> std::result::Result<Vec<u8>, DecryptionErrorCrypto> {
194 match self {
195 Self::Borrowed(message) => decrypt_borrowed_signal(message, key, iv),
196 Self::Owned(message) => {
197 let message = message
198 .take_signal_message()
199 .expect("authenticated owned input remains available");
200 match message.try_into_ciphertext_vec() {
201 Ok(mut ciphertext) => {
202 aes_256_cbc_decrypt_in_place(&mut ciphertext, key, iv)?;
203 Ok(ciphertext)
204 }
205 Err(shared) => decrypt_borrowed_signal(&shared, key, iv),
206 }
207 }
208 }
209 }
210}
211
212#[inline(always)]
213fn decrypt_borrowed_signal(
214 message: &SignalMessage,
215 key: &[u8],
216 iv: &[u8],
217) -> std::result::Result<Vec<u8>, DecryptionErrorCrypto> {
218 DECRYPTION_BUFFER.with(|buffer| {
219 let mut buf_wrapper = buffer.borrow_mut();
220 let buf = buf_wrapper.get_buffer();
221 let ciphertext = message
222 .body()
223 .map_err(|_| DecryptionErrorCrypto::BadCiphertext("invalid Signal ciphertext body"))?;
224 aes_256_cbc_decrypt_into(ciphertext, key, iv, buf)?;
225 let result = std::mem::take(buf);
226 buf.reserve(EncryptionBuffer::INITIAL_CAPACITY);
229 Ok(result)
230 })
231}
232
233enum PreKeyDecryptInput<'a> {
234 Borrowed(&'a PreKeySignalMessage),
235 Owned(&'a mut OwnedCiphertextMessage),
236}
237
238impl<'a> PreKeyDecryptInput<'a> {
239 #[inline]
240 fn pre_key_message(&self) -> &PreKeySignalMessage {
241 match self {
242 Self::Borrowed(message) => message,
243 Self::Owned(message) => message
244 .pre_key_message()
245 .expect("owned PreKey decrypt input has a PreKey envelope"),
246 }
247 }
248
249 #[inline]
250 fn into_signal_input(self) -> SignalDecryptInput<'a> {
251 match self {
252 Self::Borrowed(message) => SignalDecryptInput::Borrowed(message.message()),
253 Self::Owned(message) => SignalDecryptInput::Owned(message),
254 }
255 }
256}
257
258pub async fn message_encrypt(
259 ptext: &[u8],
260 remote_address: &ProtocolAddress,
261 session_store: &mut dyn SessionStore,
262 identity_store: &mut dyn IdentityKeyStore,
263) -> Result<CiphertextMessage> {
264 let mut session = SessionCheckout::load(session_store, remote_address)
265 .await?
266 .ok_or_else(|| SignalProtocolError::SessionNotFound(remote_address.clone()))?;
267
268 let result =
269 message_encrypt_inner(ptext, remote_address, session.record_mut(), identity_store).await;
270
271 session.commit().await?;
274
275 result
276}
277
278async fn message_encrypt_inner(
279 ptext: &[u8],
280 remote_address: &ProtocolAddress,
281 session_record: &mut SessionRecord,
282 identity_store: &mut dyn IdentityKeyStore,
283) -> Result<CiphertextMessage> {
284 let session_state = session_record
285 .session_state_mut()
286 .ok_or_else(|| SignalProtocolError::SessionNotFound(remote_address.clone()))?;
287
288 let chain_key = session_state.get_sender_chain_key()?;
289
290 let (message_keys_gen, next_chain_key) = chain_key.step_with_message_keys()?;
291 let message_keys = message_keys_gen.generate_keys();
292
293 let sender_ephemeral = session_state.sender_ratchet_key()?;
294 let previous_counter = session_state.previous_counter();
295 let session_version = session_state
296 .session_version()?
297 .try_into()
298 .map_err(|_| SignalProtocolError::InvalidSessionStructure("version does not fit in u8"))?;
299
300 let local_identity_key = session_state.local_identity_key()?;
301 let their_identity_key = session_state.remote_identity_key()?.ok_or_else(|| {
302 SignalProtocolError::InvalidState(
303 "message_encrypt",
304 format!("no remote identity key for {remote_address}"),
305 )
306 })?;
307
308 if !crate::protocol::storage::is_trusted_identity(
310 identity_store,
311 remote_address,
312 &their_identity_key,
313 Direction::Sending,
314 )
315 .await?
316 {
317 log::warn!(
318 "Identity key {} is not trusted for remote address {}",
319 hex::encode(their_identity_key.public_key().public_key_bytes()),
320 remote_address,
321 );
322 return Err(SignalProtocolError::UntrustedIdentity(
323 remote_address.clone(),
324 ));
325 }
326
327 let message = ENCRYPTION_BUFFER.with(|buffer| {
334 let mut buf_wrapper = buffer.borrow_mut();
335 let buf = buf_wrapper.get_buffer();
336 buf.clear();
337 aes_256_cbc_encrypt_into(ptext, message_keys.cipher_key(), message_keys.iv(), buf)
338 .map_err(|_| {
339 log::error!("session state corrupt for {remote_address}");
340 SignalProtocolError::InvalidSessionStructure("invalid sender chain message keys")
341 })?;
342 let ctext = buf.as_slice();
343
344 let message = if let Some(items) = session_state.unacknowledged_pre_key_message_items()? {
345 let local_registration_id = session_state.local_registration_id();
346
347 log::debug!(
348 "Building PreKeyWhisperMessage for: {} with preKeyId: {}",
349 remote_address,
350 items
351 .pre_key_id()
352 .map_or_else(|| "<none>".to_string(), |id| id.to_string()),
353 );
354
355 let message = SignalMessage::new(
356 session_version,
357 message_keys.mac_key(),
358 sender_ephemeral,
359 chain_key.index(),
360 previous_counter,
361 ctext,
362 &local_identity_key,
363 &their_identity_key,
364 )?;
365
366 CiphertextMessage::PreKeySignalMessage(PreKeySignalMessage::new(
367 session_version,
368 local_registration_id,
369 items.pre_key_id(),
370 items.signed_pre_key_id(),
371 *items.base_key(),
372 local_identity_key,
373 message,
374 )?)
375 } else {
376 CiphertextMessage::SignalMessage(SignalMessage::new(
377 session_version,
378 message_keys.mac_key(),
379 sender_ephemeral,
380 chain_key.index(),
381 previous_counter,
382 ctext,
383 &local_identity_key,
384 &their_identity_key,
385 )?)
386 };
387 if buf.capacity() > EncryptionBuffer::MAX_CAPACITY {
392 *buf = Vec::with_capacity(EncryptionBuffer::INITIAL_CAPACITY);
393 }
394 Ok::<CiphertextMessage, SignalProtocolError>(message)
395 })?;
396
397 crate::protocol::storage::save_identity(identity_store, remote_address, &their_identity_key)
398 .await?;
399
400 session_state.set_sender_chain_key(&next_chain_key)?;
401
402 session_record.reserve_sender_chain_counters(chain_key.index());
407
408 Ok(message)
409}
410
411#[allow(clippy::too_many_arguments)]
412pub async fn message_decrypt<R: Rng + CryptoRng>(
413 ciphertext: &CiphertextMessage,
414 remote_address: &ProtocolAddress,
415 session_store: &mut dyn SessionStore,
416 identity_store: &mut dyn IdentityKeyStore,
417 pre_key_store: &mut dyn PreKeyStore,
418 signed_pre_key_store: &dyn SignedPreKeyStore,
419 csprng: &mut R,
420 use_pq_ratchet: UsePQRatchet,
421) -> Result<DecryptionResult> {
422 match ciphertext {
423 CiphertextMessage::SignalMessage(m) => {
424 message_decrypt_signal(m, remote_address, session_store, identity_store, csprng).await
425 }
426 CiphertextMessage::PreKeySignalMessage(m) => {
427 message_decrypt_prekey(
428 m,
429 remote_address,
430 session_store,
431 identity_store,
432 pre_key_store,
433 signed_pre_key_store,
434 csprng,
435 use_pq_ratchet,
436 )
437 .await
438 }
439 _ => Err(SignalProtocolError::InvalidArgument(format!(
440 "message_decrypt cannot be used to decrypt {:?} messages",
441 ciphertext.message_type()
442 ))),
443 }
444}
445
446#[allow(clippy::too_many_arguments)]
449pub async fn message_decrypt_owned<R: Rng + CryptoRng>(
450 ciphertext: &mut OwnedCiphertextMessage,
451 remote_address: &ProtocolAddress,
452 session_store: &mut dyn SessionStore,
453 identity_store: &mut dyn IdentityKeyStore,
454 pre_key_store: &mut dyn PreKeyStore,
455 signed_pre_key_store: &dyn SignedPreKeyStore,
456 csprng: &mut R,
457 use_pq_ratchet: UsePQRatchet,
458) -> Result<DecryptionResult> {
459 match ciphertext.message_type() {
460 Some(CiphertextMessageType::Whisper) => {
461 message_decrypt_signal_input(
462 SignalDecryptInput::Owned(ciphertext),
463 remote_address,
464 session_store,
465 identity_store,
466 csprng,
467 )
468 .await
469 }
470 Some(CiphertextMessageType::PreKey) => {
471 message_decrypt_prekey_input(
472 PreKeyDecryptInput::Owned(ciphertext),
473 remote_address,
474 session_store,
475 identity_store,
476 pre_key_store,
477 signed_pre_key_store,
478 csprng,
479 use_pq_ratchet,
480 )
481 .await
482 }
483 Some(kind) => Err(SignalProtocolError::InvalidArgument(format!(
484 "message_decrypt_owned cannot decrypt {kind:?} messages"
485 ))),
486 None => Err(SignalProtocolError::InvalidArgument(
487 "owned ciphertext was already consumed".to_owned(),
488 )),
489 }
490}
491
492#[allow(clippy::too_many_arguments)]
493pub async fn message_decrypt_prekey<R: Rng + CryptoRng>(
494 ciphertext: &PreKeySignalMessage,
495 remote_address: &ProtocolAddress,
496 session_store: &mut dyn SessionStore,
497 identity_store: &mut dyn IdentityKeyStore,
498 pre_key_store: &mut dyn PreKeyStore,
499 signed_pre_key_store: &dyn SignedPreKeyStore,
500 csprng: &mut R,
501 use_pq_ratchet: UsePQRatchet,
502) -> Result<DecryptionResult> {
503 message_decrypt_prekey_input(
504 PreKeyDecryptInput::Borrowed(ciphertext),
505 remote_address,
506 session_store,
507 identity_store,
508 pre_key_store,
509 signed_pre_key_store,
510 csprng,
511 use_pq_ratchet,
512 )
513 .await
514}
515
516#[allow(clippy::too_many_arguments)]
517async fn message_decrypt_prekey_input<R: Rng + CryptoRng>(
518 ciphertext: PreKeyDecryptInput<'_>,
519 remote_address: &ProtocolAddress,
520 session_store: &mut dyn SessionStore,
521 identity_store: &mut dyn IdentityKeyStore,
522 pre_key_store: &mut dyn PreKeyStore,
523 signed_pre_key_store: &dyn SignedPreKeyStore,
524 csprng: &mut R,
525 use_pq_ratchet: UsePQRatchet,
526) -> Result<DecryptionResult> {
527 let mut session = SessionCheckout::load_or_create(session_store, remote_address).await?;
528 session.snapshot_for_rollback();
529
530 let result = message_decrypt_prekey_inner(
531 ciphertext,
532 remote_address,
533 session.record_mut(),
534 identity_store,
535 pre_key_store,
536 signed_pre_key_store,
537 csprng,
538 use_pq_ratchet,
539 )
540 .await;
541
542 if result.is_ok() {
543 session.commit().await?;
544 } else {
545 session.rollback().await?;
546 }
547
548 let (plaintext, pre_key_used, identity_change) = result?;
549
550 Ok(DecryptionResult {
554 plaintext,
555 identity_change,
556 consumed_prekey_id: pre_key_used,
557 })
558}
559
560#[allow(clippy::too_many_arguments)]
561async fn message_decrypt_prekey_inner<R: Rng + CryptoRng>(
562 ciphertext: PreKeyDecryptInput<'_>,
563 remote_address: &ProtocolAddress,
564 session_record: &mut SessionRecord,
565 identity_store: &mut dyn IdentityKeyStore,
566 pre_key_store: &mut dyn PreKeyStore,
567 signed_pre_key_store: &dyn SignedPreKeyStore,
568 csprng: &mut R,
569 use_pq_ratchet: UsePQRatchet,
570) -> Result<(Vec<u8>, Option<PreKeyId>, IdentityChange)> {
571 let process_prekey_result = session::process_prekey(
572 ciphertext.pre_key_message(),
573 remote_address,
574 session_record,
575 identity_store,
576 pre_key_store,
577 signed_pre_key_store,
578 use_pq_ratchet,
579 )
580 .await;
581
582 let (pre_key_used, identity_to_save, reused_existing_session) = match process_prekey_result {
583 Ok(result) => result,
584 Err(e) => {
585 let errs = [e];
586 log::error!(
587 "{}",
588 create_decryption_failure_log(
589 remote_address,
590 &errs,
591 session_record,
592 ciphertext.pre_key_message().message()
593 )?
594 );
595 let [e] = errs;
596 return Err(e);
597 }
598 };
599 let their_identity_key = *identity_to_save.their_identity_key;
600 let mut ciphertext = ciphertext.into_signal_input();
601
602 let decrypt = decrypt_message_with_record(
603 remote_address,
604 session_record,
605 &mut ciphertext,
606 CiphertextMessageType::PreKey,
607 csprng,
608 )?;
609
610 let saved = identity_store
611 .save_identity(remote_address, &their_identity_key)
612 .await?;
613 let plaintext = decrypt.commit();
614
615 let identity_change = if reused_existing_session {
620 IdentityChange::NewOrUnchanged
621 } else {
622 saved
623 };
624
625 Ok((plaintext, pre_key_used.pre_key_id, identity_change))
626}
627
628pub async fn message_decrypt_signal<R: Rng + CryptoRng>(
629 ciphertext: &SignalMessage,
630 remote_address: &ProtocolAddress,
631 session_store: &mut dyn SessionStore,
632 identity_store: &mut dyn IdentityKeyStore,
633 csprng: &mut R,
634) -> Result<DecryptionResult> {
635 message_decrypt_signal_input(
636 SignalDecryptInput::Borrowed(ciphertext),
637 remote_address,
638 session_store,
639 identity_store,
640 csprng,
641 )
642 .await
643}
644
645async fn message_decrypt_signal_input<R: Rng + CryptoRng>(
646 mut ciphertext: SignalDecryptInput<'_>,
647 remote_address: &ProtocolAddress,
648 session_store: &mut dyn SessionStore,
649 identity_store: &mut dyn IdentityKeyStore,
650 csprng: &mut R,
651) -> Result<DecryptionResult> {
652 let mut session = SessionCheckout::load(session_store, remote_address)
653 .await?
654 .ok_or_else(|| SignalProtocolError::SessionNotFound(remote_address.clone()))?;
655
656 let result = message_decrypt_signal_inner(
657 &mut ciphertext,
658 remote_address,
659 session.record_mut(),
660 identity_store,
661 csprng,
662 )
663 .await;
664
665 session.commit().await?;
666
667 let (plaintext, identity_change) = result?;
668 Ok(DecryptionResult {
669 plaintext,
670 identity_change,
671 consumed_prekey_id: None,
672 })
673}
674
675async fn message_decrypt_signal_inner<R: Rng + CryptoRng>(
676 ciphertext: &mut SignalDecryptInput<'_>,
677 remote_address: &ProtocolAddress,
678 session_record: &mut SessionRecord,
679 identity_store: &mut dyn IdentityKeyStore,
680 csprng: &mut R,
681) -> Result<(Vec<u8>, IdentityChange)> {
682 if session_record.session_state().is_none() && session_record.previous_session_count() == 0 {
687 log::warn!(
688 "Session record for {} exists but has no usable state (no current, 0 previous). \
689 Treating as SessionNotFound.",
690 remote_address
691 );
692 return Err(SignalProtocolError::SessionNotFound(remote_address.clone()));
693 }
694
695 let decrypt = decrypt_message_with_record(
696 remote_address,
697 session_record,
698 ciphertext,
699 CiphertextMessageType::Whisper,
700 csprng,
701 )?;
702
703 let their_identity_key = decrypt
704 .state()
705 .remote_identity_key()
706 .expect("successfully decrypted; must have a remote identity key")
707 .expect("successfully decrypted; must have a remote identity key");
708
709 let identity_change = if decrypt.used_previous_session() {
720 log::debug!(
721 "Saving identity for {} from previous session (skipping trust check)",
722 remote_address,
723 );
724 crate::protocol::storage::save_identity(
729 identity_store,
730 remote_address,
731 &their_identity_key,
732 )
733 .await?;
734 IdentityChange::NewOrUnchanged
735 } else {
736 if !crate::protocol::storage::is_trusted_identity(
737 identity_store,
738 remote_address,
739 &their_identity_key,
740 Direction::Receiving,
741 )
742 .await?
743 {
744 log::warn!(
745 "Identity key {} is not trusted for remote address {}",
746 hex::encode(their_identity_key.public_key().public_key_bytes()),
747 remote_address,
748 );
749 return Err(SignalProtocolError::UntrustedIdentity(
750 remote_address.clone(),
751 ));
752 }
753
754 crate::protocol::storage::save_identity(identity_store, remote_address, &their_identity_key)
755 .await?
756 };
757
758 Ok((decrypt.commit(), identity_change))
759}
760
761fn take_key_agreement_failure(errs: &mut Vec<SignalProtocolError>) -> Option<SignalProtocolError> {
767 let at = errs
768 .iter()
769 .position(|e| matches!(e, SignalProtocolError::KeyAgreementFailed(_)))?;
770 Some(errs.remove(at))
771}
772
773fn create_decryption_failure_log(
774 remote_address: &ProtocolAddress,
775 mut errs: &[SignalProtocolError],
776 record: &SessionRecord,
777 ciphertext: &SignalMessage,
778) -> Result<String> {
779 fn append_session_summary(
780 lines: &mut Vec<String>,
781 idx: usize,
782 state: std::result::Result<&SessionState, crate::protocol::state::InvalidSessionError>,
783 err: Option<&SignalProtocolError>,
784 ) {
785 let chains = state.map(|state| state.all_receiver_chain_logging_info());
786 match (err, &chains) {
787 (Some(err), Ok(chains)) => {
788 lines.push(format!(
789 "Candidate session {} failed with '{}', had {} receiver chains",
790 idx,
791 err,
792 chains.len()
793 ));
794 }
795 (Some(err), Err(state_err)) => {
796 lines.push(format!(
797 "Candidate session {idx} failed with '{err}'; cannot get receiver chain info ({state_err})",
798 ));
799 }
800 (None, Ok(chains)) => {
801 lines.push(format!(
802 "Candidate session {} had {} receiver chains",
803 idx,
804 chains.len()
805 ));
806 }
807 (None, Err(state_err)) => {
808 lines.push(format!(
809 "Candidate session {idx}: cannot get receiver chain info ({state_err})",
810 ));
811 }
812 }
813
814 if let Ok(chains) = chains {
815 for chain in chains {
816 let chain_idx = match chain.1 {
817 Some(i) => i.to_string(),
818 None => "missing in protobuf".to_string(),
819 };
820
821 lines.push(format!(
822 "Receiver chain with sender ratchet public key {} chain key index {}",
823 hex::encode(chain.0),
824 chain_idx
825 ));
826 }
827 }
828 }
829
830 let mut lines = vec![];
831
832 lines.push(format!(
833 "Message from {} failed to decrypt; sender ratchet public key {} message counter {}",
834 remote_address,
835 hex::encode(ciphertext.sender_ratchet_key().public_key_bytes()),
836 ciphertext.counter()
837 ));
838
839 if let Some(current_session) = record.session_state() {
840 let err = errs.first();
841 if err.is_some() {
842 errs = &errs[1..];
843 }
844 append_session_summary(&mut lines, 0, Ok(current_session), err);
845 } else {
846 lines.push("No current session".to_string());
847 }
848
849 for (idx, (state, err)) in record
850 .previous_session_states()
851 .zip(errs.iter().map(Some).chain(std::iter::repeat(None)))
852 .enumerate()
853 {
854 let state = match state {
855 Ok(ref state) => Ok(state),
856 Err(err) => Err(err),
857 };
858 append_session_summary(&mut lines, idx + 1, state, err);
859 }
860
861 Ok(lines.join("\n"))
862}
863
864enum RecordDecryptState {
865 Current {
866 snapshot: DecryptSnapshot,
867 sender_chain_reset: bool,
868 },
869 Previous {
870 state: Box<SessionState>,
871 effect: StateDecryptEffect,
872 index: usize,
873 },
874}
875
876struct DeferredCurrentDecrypt<'a> {
877 record: &'a mut SessionRecord,
878 ratchet_key: PublicKey,
879 chain_key: ChainKey,
880 plaintext: Vec<u8>,
881}
882
883enum RecordDecrypt<'a> {
884 Deferred(DeferredCurrentDecrypt<'a>),
885 Transaction(RecordDecryptTransaction<'a>),
886}
887
888impl RecordDecrypt<'_> {
889 fn state(&self) -> &SessionState {
890 match self {
891 Self::Deferred(decrypt) => decrypt
892 .record
893 .session_state()
894 .expect("a deferred decrypt keeps the current state installed"),
895 Self::Transaction(decrypt) => decrypt.state(),
896 }
897 }
898
899 fn used_previous_session(&self) -> bool {
900 match self {
901 Self::Deferred(_) => false,
902 Self::Transaction(decrypt) => decrypt.used_previous_session(),
903 }
904 }
905
906 fn commit(self) -> Vec<u8> {
907 match self {
908 Self::Deferred(mut decrypt) => {
909 let state = decrypt
910 .record
911 .session_state_mut()
912 .expect("a deferred decrypt keeps the current state installed");
913 state
914 .set_receiver_chain_key(&decrypt.ratchet_key, &decrypt.chain_key)
915 .expect("the deferred in-order receiver chain remains installed");
916 state.clear_unacknowledged_pre_key_message();
917 std::mem::take(&mut decrypt.plaintext)
918 }
919 Self::Transaction(decrypt) => decrypt.commit(),
920 }
921 }
922}
923
924struct RecordDecryptTransaction<'a> {
925 record: &'a mut SessionRecord,
926 state: Option<RecordDecryptState>,
927 plaintext: Vec<u8>,
928}
929
930impl RecordDecryptTransaction<'_> {
931 fn state(&self) -> &SessionState {
932 match self
933 .state
934 .as_ref()
935 .expect("a live decrypt transaction owns its rollback")
936 {
937 RecordDecryptState::Current { .. } => self
938 .record
939 .session_state()
940 .expect("a current decrypt transaction keeps the current state installed"),
941 RecordDecryptState::Previous { state, .. } => state,
942 }
943 }
944
945 fn used_previous_session(&self) -> bool {
946 matches!(self.state, Some(RecordDecryptState::Previous { .. }))
947 }
948
949 fn commit(mut self) -> Vec<u8> {
950 match self
951 .state
952 .take()
953 .expect("a live decrypt transaction owns its rollback")
954 {
955 RecordDecryptState::Current {
956 sender_chain_reset, ..
957 } => {
958 let state = self
959 .record
960 .session_state_mut()
961 .expect("a current decrypt transaction keeps the current state installed");
962 state.clear_unacknowledged_pre_key_message();
963 if sender_chain_reset {
964 self.record.rebase_lease_after_sender_chain_reset();
965 }
966 }
967 RecordDecryptState::Previous {
968 mut state, effect, ..
969 } => {
970 let sender_chain_reset = effect.sender_chain_reset();
971 effect.commit(&mut state);
972 state.clear_unacknowledged_pre_key_message();
973 if sender_chain_reset {
974 self.record.promote_fresh_state(*state);
981 } else {
982 self.record.promote_state(*state);
983 }
984 }
985 }
986 std::mem::take(&mut self.plaintext)
987 }
988}
989
990impl Drop for RecordDecryptTransaction<'_> {
991 fn drop(&mut self) {
992 let Some(state) = self.state.take() else {
993 return;
994 };
995 match state {
996 RecordDecryptState::Current { snapshot, .. } => self
997 .record
998 .session_state_mut()
999 .expect("a current decrypt transaction keeps the current state installed")
1000 .restore_decrypt_snapshot(snapshot),
1001 RecordDecryptState::Previous {
1002 mut state,
1003 effect,
1004 index,
1005 } => {
1006 effect.rollback(&mut state);
1007 self.record.restore_previous_session(index, *state);
1008 }
1009 }
1010 }
1011}
1012
1013fn decrypt_message_with_record<'a, R: Rng + CryptoRng>(
1014 remote_address: &ProtocolAddress,
1015 record: &'a mut SessionRecord,
1016 ciphertext: &mut SignalDecryptInput<'_>,
1017 original_message_type: CiphertextMessageType,
1018 csprng: &mut R,
1019) -> Result<RecordDecrypt<'a>> {
1020 debug_assert!(matches!(
1021 original_message_type,
1022 CiphertextMessageType::Whisper | CiphertextMessageType::PreKey
1023 ));
1024 let log_decryption_failure =
1025 |message: &SignalMessage, state: &SessionState, error: &SignalProtocolError| {
1026 log::warn!(
1028 "Failed to decrypt {:?} message with ratchet key: {} and counter: {}. \
1029 Session loaded for {}. Local session has base key: {} and counter: {}. {}",
1030 original_message_type,
1031 hex::encode(message.sender_ratchet_key().public_key_bytes()),
1032 message.counter(),
1033 remote_address,
1034 state
1035 .sender_ratchet_key_for_logging()
1036 .unwrap_or_else(|e| format!("<error: {e}>")),
1037 state.previous_counter(),
1038 error
1039 );
1040 };
1041
1042 let mut errs = vec![];
1043
1044 if let Some(mut current_state) = record.take_session_state() {
1046 let result = decrypt_message_with_state(
1047 CurrentOrPrevious::Current,
1048 &mut current_state,
1049 ciphertext,
1050 original_message_type,
1051 remote_address,
1052 csprng,
1053 );
1054
1055 match result {
1056 Ok(result) => {
1057 log::debug!(
1058 "decrypted {:?} message from {} with current session state (base key {})",
1059 original_message_type,
1060 remote_address,
1061 current_state
1062 .sender_ratchet_key_for_logging()
1063 .expect("successful decrypt always has a valid base key"),
1064 );
1065 record.set_session_state(current_state);
1066 return match result.effect {
1067 StateDecryptEffect::DeferredChainKey {
1068 ratchet_key,
1069 chain_key,
1070 } => Ok(RecordDecrypt::Deferred(DeferredCurrentDecrypt {
1071 record,
1072 ratchet_key,
1073 chain_key,
1074 plaintext: result.plaintext,
1075 })),
1076 StateDecryptEffect::Applied {
1077 snapshot,
1078 sender_chain_reset,
1079 } => Ok(RecordDecrypt::Transaction(RecordDecryptTransaction {
1080 record,
1081 state: Some(RecordDecryptState::Current {
1082 snapshot,
1083 sender_chain_reset,
1084 }),
1085 plaintext: result.plaintext,
1086 })),
1087 };
1088 }
1089 Err(SignalProtocolError::DuplicatedMessage(chain, counter))
1090 if original_message_type == CiphertextMessageType::Whisper =>
1091 {
1092 let error = SignalProtocolError::DuplicatedMessage(chain, counter);
1096 log_decryption_failure(ciphertext.signal_message(), ¤t_state, &error);
1097 errs.push(error);
1098 record.set_session_state(current_state);
1099 }
1100 Err(SignalProtocolError::DuplicatedMessage(chain, counter)) => {
1101 let error = SignalProtocolError::DuplicatedMessage(chain, counter);
1102 log_decryption_failure(ciphertext.signal_message(), ¤t_state, &error);
1103 record.set_session_state(current_state);
1104 return Err(error);
1105 }
1106 Err(e) if !ciphertext.is_available() => {
1107 record.set_session_state(current_state);
1111 return Err(e);
1112 }
1113 Err(e) => {
1114 log_decryption_failure(ciphertext.signal_message(), ¤t_state, &e);
1115 errs.push(e);
1116 match original_message_type {
1117 CiphertextMessageType::PreKey => {
1118 record.set_session_state(current_state);
1124 log::warn!(
1125 "{}",
1126 create_decryption_failure_log(
1127 remote_address,
1128 &errs,
1129 record,
1130 ciphertext.signal_message()
1131 )?
1132 );
1133 if let Some(refused) = take_key_agreement_failure(&mut errs) {
1134 return Err(refused);
1135 }
1136 if errs
1138 .iter()
1139 .any(|e| matches!(e, SignalProtocolError::BadMac(_)))
1140 {
1141 return Err(SignalProtocolError::BadMac(original_message_type));
1142 }
1143 return Err(SignalProtocolError::InvalidMessage(
1144 original_message_type,
1145 "decryption failed",
1146 ));
1147 }
1148 CiphertextMessageType::Whisper => {
1149 record.set_session_state(current_state);
1151 }
1152 CiphertextMessageType::SenderKey | CiphertextMessageType::Plaintext => {
1153 unreachable!("should not be using Double Ratchet for these")
1154 }
1155 }
1156 }
1157 }
1158 }
1159
1160 let previous_count = record.previous_session_count();
1165 let mut idx = 0;
1166
1167 while idx < previous_count {
1168 let Some(mut previous) = record.take_previous_session(idx) else {
1170 break;
1172 };
1173
1174 let result = decrypt_message_with_state(
1175 CurrentOrPrevious::Previous,
1176 &mut previous,
1177 ciphertext,
1178 original_message_type,
1179 remote_address,
1180 csprng,
1181 );
1182
1183 match result {
1184 Ok(result) => {
1185 log::debug!(
1186 "decrypted {:?} message from {} with PREVIOUS session state (base key {})",
1187 original_message_type,
1188 remote_address,
1189 previous
1190 .sender_ratchet_key_for_logging()
1191 .expect("successful decrypt always has a valid base key"),
1192 );
1193 return Ok(RecordDecrypt::Transaction(RecordDecryptTransaction {
1194 record,
1195 state: Some(RecordDecryptState::Previous {
1196 state: Box::new(previous),
1197 effect: result.effect,
1198 index: idx,
1199 }),
1200 plaintext: result.plaintext,
1201 }));
1202 }
1203 Err(SignalProtocolError::DuplicatedMessage(chain, counter))
1204 if original_message_type == CiphertextMessageType::Whisper =>
1205 {
1206 let error = SignalProtocolError::DuplicatedMessage(chain, counter);
1207 log_decryption_failure(ciphertext.signal_message(), &previous, &error);
1208 errs.push(error);
1209 record.restore_previous_session(idx, previous);
1210 idx += 1;
1211 }
1212 Err(SignalProtocolError::DuplicatedMessage(chain, counter)) => {
1213 let error = SignalProtocolError::DuplicatedMessage(chain, counter);
1214 log_decryption_failure(ciphertext.signal_message(), &previous, &error);
1215 record.restore_previous_session(idx, previous);
1216 return Err(error);
1217 }
1218 Err(e) if !ciphertext.is_available() => {
1219 record.restore_previous_session(idx, previous);
1220 return Err(e);
1221 }
1222 Err(e) => {
1223 log_decryption_failure(ciphertext.signal_message(), &previous, &e);
1224 errs.push(e);
1225 record.restore_previous_session(idx, previous);
1227 idx += 1;
1228 }
1229 }
1230 }
1231
1232 let previous_state_count = record.previous_session_count();
1234
1235 if let Some(current_state) = record.session_state() {
1236 log::error!(
1237 "No valid session for recipient: {}, current session base key {}, number of previous states: {}",
1238 remote_address,
1239 current_state
1240 .sender_ratchet_key_for_logging()
1241 .unwrap_or_else(|e| format!("<error: {e}>")),
1242 previous_state_count,
1243 );
1244 } else {
1245 log::error!(
1246 "No valid session for recipient: {}, (no current session state), number of previous states: {}",
1247 remote_address,
1248 previous_state_count,
1249 );
1250 }
1251 log::error!(
1252 "{}",
1253 create_decryption_failure_log(remote_address, &errs, record, ciphertext.signal_message())?
1254 );
1255
1256 if let Some((chain, counter)) = errs.iter().find_map(|error| match error {
1262 SignalProtocolError::DuplicatedMessage(chain, counter) => Some((*chain, *counter)),
1263 _ => None,
1264 }) {
1265 return Err(SignalProtocolError::DuplicatedMessage(chain, counter));
1266 }
1267 if let Some(refused) = take_key_agreement_failure(&mut errs) {
1268 return Err(refused);
1269 }
1270 if errs
1275 .iter()
1276 .any(|e| matches!(e, SignalProtocolError::BadMac(_)))
1277 {
1278 return Err(SignalProtocolError::BadMac(original_message_type));
1279 }
1280
1281 Err(SignalProtocolError::InvalidMessage(
1282 original_message_type,
1283 "decryption failed",
1284 ))
1285}
1286
1287#[derive(Clone, Copy)]
1288enum CurrentOrPrevious {
1289 Current,
1290 Previous,
1291}
1292
1293impl std::fmt::Display for CurrentOrPrevious {
1294 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1295 match self {
1296 Self::Current => write!(f, "current"),
1297 Self::Previous => write!(f, "previous"),
1298 }
1299 }
1300}
1301
1302struct StateDecryptResult {
1303 plaintext: Vec<u8>,
1304 effect: StateDecryptEffect,
1305}
1306
1307enum StateDecryptEffect {
1308 DeferredChainKey {
1309 ratchet_key: PublicKey,
1310 chain_key: ChainKey,
1311 },
1312 Applied {
1313 snapshot: DecryptSnapshot,
1314 sender_chain_reset: bool,
1317 },
1318}
1319
1320impl StateDecryptEffect {
1321 fn sender_chain_reset(&self) -> bool {
1324 match self {
1325 Self::DeferredChainKey { .. } => false,
1326 Self::Applied {
1327 sender_chain_reset, ..
1328 } => *sender_chain_reset,
1329 }
1330 }
1331
1332 fn commit(self, state: &mut SessionState) {
1333 match self {
1334 Self::DeferredChainKey {
1335 ratchet_key,
1336 chain_key,
1337 } => {
1338 state
1339 .set_receiver_chain_key(&ratchet_key, &chain_key)
1340 .expect("the deferred in-order receiver chain remains installed");
1341 }
1342 Self::Applied { .. } => {}
1343 }
1344 }
1345
1346 fn rollback(self, state: &mut SessionState) {
1347 match self {
1348 Self::DeferredChainKey { .. } => {}
1349 Self::Applied { snapshot, .. } => state.restore_decrypt_snapshot(snapshot),
1350 }
1351 }
1352}
1353
1354fn decrypt_message_with_state<R: Rng + CryptoRng>(
1355 current_or_previous: CurrentOrPrevious,
1356 state: &mut SessionState,
1357 ciphertext: &mut SignalDecryptInput<'_>,
1358 original_message_type: CiphertextMessageType,
1359 remote_address: &ProtocolAddress,
1360 csprng: &mut R,
1361) -> Result<StateDecryptResult> {
1362 let _ = state.root_key().map_err(|_| {
1364 SignalProtocolError::InvalidMessage(
1365 original_message_type,
1366 "No session available to decrypt",
1367 )
1368 })?;
1369
1370 let message = ciphertext.signal_message();
1371 let ciphertext_version = message.message_version() as u32;
1372 if ciphertext_version != state.session_version()? {
1373 return Err(SignalProtocolError::UnrecognizedMessageVersion(
1374 ciphertext_version,
1375 ));
1376 }
1377
1378 let their_ephemeral = *message.sender_ratchet_key();
1379 let counter = message.counter();
1380
1381 let receiver_chain = state.receiver_chain_state(&their_ephemeral)?;
1383 if let Some(ReceiverChainState::Open(chain_key)) = receiver_chain
1384 && chain_key.index() == counter
1385 {
1386 let (message_key_gen, next_chain) = chain_key.step_with_message_keys()?;
1387 let plaintext = decrypt_with_message_keys(
1388 current_or_previous,
1389 state,
1390 ciphertext,
1391 original_message_type,
1392 remote_address,
1393 message_key_gen,
1394 )?;
1395 return Ok(StateDecryptResult {
1396 plaintext,
1397 effect: StateDecryptEffect::DeferredChainKey {
1398 ratchet_key: their_ephemeral,
1399 chain_key: next_chain,
1400 },
1401 });
1402 }
1403
1404 let snapshot = state.decrypt_snapshot();
1405
1406 let result = decrypt_with_pending_state(
1407 current_or_previous,
1408 state,
1409 ciphertext,
1410 original_message_type,
1411 remote_address,
1412 csprng,
1413 &their_ephemeral,
1414 receiver_chain,
1415 counter,
1416 );
1417 match result {
1418 Ok((plaintext, sender_chain_reset)) => Ok(StateDecryptResult {
1419 plaintext,
1420 effect: StateDecryptEffect::Applied {
1421 snapshot,
1422 sender_chain_reset,
1423 },
1424 }),
1425 Err(e) => {
1426 state.restore_decrypt_snapshot(snapshot);
1427 Err(e)
1428 }
1429 }
1430}
1431
1432#[allow(clippy::too_many_arguments)]
1433fn decrypt_with_pending_state<R: Rng + CryptoRng>(
1434 current_or_previous: CurrentOrPrevious,
1435 state: &mut SessionState,
1436 ciphertext: &mut SignalDecryptInput<'_>,
1437 original_message_type: CiphertextMessageType,
1438 remote_address: &ProtocolAddress,
1439 csprng: &mut R,
1440 their_ephemeral: &PublicKey,
1441 receiver_chain: Option<ReceiverChainState>,
1442 counter: u32,
1443) -> Result<(Vec<u8>, bool)> {
1444 if let Some(ReceiverChainState::Closed { next_index }) = receiver_chain {
1445 if counter >= next_index {
1446 return Err(SignalProtocolError::InvalidSessionStructure(
1447 "receiver chain is closed",
1448 ));
1449 }
1450 let Some(message_key_gen) = state.get_message_keys(their_ephemeral, counter)? else {
1451 return Err(SignalProtocolError::DuplicatedMessage(next_index, counter));
1452 };
1453 return decrypt_with_message_keys(
1454 current_or_previous,
1455 state,
1456 ciphertext,
1457 original_message_type,
1458 remote_address,
1459 message_key_gen,
1460 )
1461 .map(|plaintext| (plaintext, false));
1462 }
1463
1464 let (chain_key, deferred_ratchet) =
1465 get_or_create_chain_key(state, their_ephemeral, remote_address, receiver_chain)?;
1466
1467 let message_key_gen = get_or_create_message_key(
1468 state,
1469 their_ephemeral,
1470 remote_address,
1471 original_message_type,
1472 &chain_key,
1473 counter,
1474 )?;
1475
1476 let plaintext = decrypt_with_message_keys(
1477 current_or_previous,
1478 state,
1479 ciphertext,
1480 original_message_type,
1481 remote_address,
1482 message_key_gen,
1483 )?;
1484
1485 let sender_chain_reset = if let Some(deferred_ratchet) = deferred_ratchet {
1489 deferred_ratchet.apply(state, their_ephemeral, csprng)?;
1490 true
1491 } else {
1492 false
1493 };
1494
1495 Ok((plaintext, sender_chain_reset))
1496}
1497
1498fn decrypt_with_message_keys(
1499 current_or_previous: CurrentOrPrevious,
1500 state: &SessionState,
1501 ciphertext: &mut SignalDecryptInput<'_>,
1502 original_message_type: CiphertextMessageType,
1503 remote_address: &ProtocolAddress,
1504 message_key_gen: MessageKeyGenerator,
1505) -> Result<Vec<u8>> {
1506 let message_keys = message_key_gen.generate_keys();
1507
1508 let their_identity_key =
1509 state
1510 .remote_identity_key()?
1511 .ok_or(SignalProtocolError::InvalidSessionStructure(
1512 "cannot decrypt without remote identity key",
1513 ))?;
1514
1515 let local_identity_key = state.local_identity_key()?;
1516
1517 let mac_valid = ciphertext.signal_message().verify_mac(
1518 &their_identity_key,
1519 &local_identity_key,
1520 message_keys.mac_key(),
1521 )?;
1522
1523 if !mac_valid {
1524 let their_id_fingerprint = hex::encode(their_identity_key.public_key().public_key_bytes());
1525 let local_id_fingerprint = hex::encode(local_identity_key.public_key().public_key_bytes());
1526
1527 let mac_key_bytes = message_keys.mac_key();
1528 let mac_key_fingerprint: String = hex::encode(mac_key_bytes).chars().take(8).collect();
1529
1530 log::error!(
1531 "MAC verification failed for message from {}. \
1532 Remote Identity: {}, \
1533 Local Identity: {}, \
1534 MAC Key Fingerprint: {}...",
1535 remote_address,
1536 their_id_fingerprint,
1537 local_id_fingerprint,
1538 mac_key_fingerprint
1539 );
1540 return Err(SignalProtocolError::BadMac(original_message_type));
1541 }
1542
1543 match ciphertext.decrypt(message_keys.cipher_key(), message_keys.iv()) {
1544 Ok(plaintext) => Ok(plaintext),
1545 Err(DecryptionErrorCrypto::BadKeyOrIv) => {
1546 log::warn!("{current_or_previous} session state corrupt for {remote_address}",);
1547 Err(SignalProtocolError::InvalidSessionStructure(
1548 "invalid receiver chain message keys",
1549 ))
1550 }
1551 Err(DecryptionErrorCrypto::BadCiphertext(msg)) => {
1552 log::warn!("failed to decrypt 1:1 message: {msg}");
1553 Err(SignalProtocolError::InvalidMessage(
1554 original_message_type,
1555 "failed to decrypt",
1556 ))
1557 }
1558 }
1559}
1560
1561#[must_use = "the deferred sender ratchet must be applied after authenticated decryption succeeds"]
1562struct DeferredSenderRatchet {
1563 receiver_root_key: RootKey,
1564 previous_counter: u32,
1565}
1566
1567impl DeferredSenderRatchet {
1568 fn apply<R: Rng + CryptoRng>(
1569 self,
1570 state: &mut SessionState,
1571 their_ephemeral: &PublicKey,
1572 csprng: &mut R,
1573 ) -> Result<()> {
1574 let our_new_ephemeral = KeyPair::generate(csprng);
1575 let sender_chain = self
1576 .receiver_root_key
1577 .create_chain(their_ephemeral, &our_new_ephemeral.private_key)?;
1578
1579 state.set_root_key(&sender_chain.0);
1580 state.set_previous_counter(self.previous_counter);
1581 state.set_sender_chain(&our_new_ephemeral, &sender_chain.1);
1582 Ok(())
1583 }
1584}
1585
1586fn get_or_create_chain_key(
1587 state: &mut SessionState,
1588 their_ephemeral: &PublicKey,
1589 remote_address: &ProtocolAddress,
1590 receiver_chain: Option<ReceiverChainState>,
1591) -> Result<(ChainKey, Option<DeferredSenderRatchet>)> {
1592 match receiver_chain {
1593 Some(ReceiverChainState::Open(chain)) => return Ok((chain, None)),
1594 Some(ReceiverChainState::Closed { .. }) => {
1595 return Err(SignalProtocolError::InvalidSessionStructure(
1596 "receiver chain is closed",
1597 ));
1598 }
1599 None => {}
1600 }
1601
1602 log::debug!("{remote_address} creating new chains.");
1603
1604 let root_key = state.root_key()?;
1605 let our_ephemeral = state.sender_ratchet_private_key()?;
1606 let receiver_chain = root_key.create_chain(their_ephemeral, &our_ephemeral)?;
1607
1608 let current_index = state.get_sender_chain_key()?.index();
1609 let deferred_ratchet = DeferredSenderRatchet {
1610 receiver_root_key: receiver_chain.0,
1611 previous_counter: current_index.saturating_sub(1),
1612 };
1613 state.add_receiver_chain(their_ephemeral, &receiver_chain.1);
1614
1615 Ok((receiver_chain.1, Some(deferred_ratchet)))
1616}
1617
1618fn get_or_create_message_key(
1619 state: &mut SessionState,
1620 their_ephemeral: &PublicKey,
1621 remote_address: &ProtocolAddress,
1622 original_message_type: CiphertextMessageType,
1623 chain_key: &ChainKey,
1624 counter: u32,
1625) -> Result<MessageKeyGenerator> {
1626 let chain_index = chain_key.index();
1627
1628 if chain_index > counter {
1629 return match state.get_message_keys(their_ephemeral, counter)? {
1630 Some(keys) => Ok(keys),
1631 None => Err(SignalProtocolError::DuplicatedMessage(chain_index, counter)),
1632 };
1633 }
1634
1635 assert!(chain_index <= counter);
1636
1637 let jump = (counter - chain_index) as usize;
1638
1639 let limit = forward_jump_limit(state.session_with_self()?);
1642 if jump > limit {
1643 log::error!(
1644 "{remote_address} Exceeded future message limit: {limit}, index: {chain_index}, counter: {counter})"
1645 );
1646 return Err(SignalProtocolError::InvalidMessage(
1647 original_message_type,
1648 "message from too far into the future",
1649 ));
1650 }
1651
1652 let mut chain_key = *chain_key;
1653
1654 while chain_key.index() < counter {
1655 let (message_keys, next_chain) = chain_key.step_with_message_keys()?;
1656 state.set_message_keys(their_ephemeral, message_keys)?;
1657 chain_key = next_chain;
1658 }
1659
1660 let (result_message_keys, next_chain) = chain_key.step_with_message_keys()?;
1661 state.set_receiver_chain_key(their_ephemeral, &next_chain)?;
1662 Ok(result_message_keys)
1663}
1664
1665#[cfg(test)]
1666mod tests {
1667 use super::*;
1668 use crate::protocol::*;
1669 use async_trait::async_trait;
1670 use std::collections::HashMap;
1671 use std::future::Future;
1672 use std::num::NonZeroU64;
1673 use std::sync::Mutex as SyncMutex;
1674 use std::sync::atomic::{AtomicBool, Ordering};
1675 use std::task::{Context, Poll};
1676
1677 #[test]
1678 fn forward_jump_limit_matches_wa_web_signal_future_messages_max() {
1679 assert_eq!(forward_jump_limit(false), 2_000);
1682 assert_eq!(forward_jump_limit(false), MAX_FORWARD_JUMPS);
1683 assert_eq!(forward_jump_limit(true), 25_000);
1684 assert_eq!(forward_jump_limit(true), consts::MAX_FORWARD_JUMPS_SELF);
1685 assert!(forward_jump_limit(true) > forward_jump_limit(false));
1686 }
1687
1688 struct MemSessionStore(HashMap<String, SessionRecord>);
1691 impl MemSessionStore {
1692 fn new() -> Self {
1693 Self(HashMap::new())
1694 }
1695 }
1696
1697 struct DestructiveSessionStore(SyncMutex<Option<SessionRecord>>);
1698
1699 impl DestructiveSessionStore {
1700 const CHECKOUT: SessionCheckoutKey = SessionCheckoutKey::new(0, NonZeroU64::MIN);
1701
1702 fn new(record: Option<SessionRecord>) -> Self {
1703 Self(SyncMutex::new(record))
1704 }
1705
1706 fn take(&self) -> Option<SessionRecord> {
1707 self.0.lock().expect("test store lock poisoned").take()
1708 }
1709
1710 fn replace(&self, record: SessionRecord) {
1711 *self.0.lock().expect("test store lock poisoned") = Some(record);
1712 }
1713 }
1714
1715 #[cfg_attr(not(target_arch = "wasm32"), async_trait)]
1716 impl SessionStore for DestructiveSessionStore {
1717 async fn load_session(&self, _address: &ProtocolAddress) -> Result<Option<SessionRecord>> {
1718 Ok(self.0.lock().expect("test store lock poisoned").clone())
1719 }
1720
1721 fn try_load_session_for_update(
1722 &self,
1723 _address: &ProtocolAddress,
1724 ) -> Option<Result<(Option<SessionRecord>, Option<SessionCheckoutKey>)>> {
1725 Some(Ok((self.take(), Some(Self::CHECKOUT))))
1726 }
1727
1728 async fn has_session(&self, _address: &ProtocolAddress) -> Result<bool> {
1729 Ok(self.0.lock().expect("test store lock poisoned").is_some())
1730 }
1731
1732 async fn store_session(
1733 &mut self,
1734 _address: &ProtocolAddress,
1735 record: SessionRecord,
1736 ) -> Result<()> {
1737 self.replace(record);
1738 Ok(())
1739 }
1740
1741 fn try_store_session_from_checkout(
1742 &mut self,
1743 _address: &ProtocolAddress,
1744 record: SessionRecord,
1745 checkout: Option<SessionCheckoutKey>,
1746 _had_session: bool,
1747 ) -> SessionCheckoutStoreResult {
1748 if checkout != Some(Self::CHECKOUT)
1749 || self.0.lock().expect("test store lock poisoned").is_some()
1750 {
1751 return SessionCheckoutStoreResult::Rejected;
1752 }
1753 self.replace(record);
1754 SessionCheckoutStoreResult::Stored
1755 }
1756 }
1757 #[cfg_attr(not(target_arch = "wasm32"), async_trait)]
1758 impl SessionStore for MemSessionStore {
1759 async fn load_session(&self, address: &ProtocolAddress) -> Result<Option<SessionRecord>> {
1760 Ok(self.0.get(address.as_str()).cloned())
1761 }
1762 async fn has_session(&self, address: &ProtocolAddress) -> Result<bool> {
1763 Ok(self.0.contains_key(address.as_str()))
1764 }
1765 async fn store_session(
1766 &mut self,
1767 address: &ProtocolAddress,
1768 record: SessionRecord,
1769 ) -> Result<()> {
1770 self.0.insert(address.as_str().to_string(), record);
1771 Ok(())
1772 }
1773 }
1774
1775 struct MemIdentityStore {
1776 pair: IdentityKeyPair,
1777 reg_id: u32,
1778 known: HashMap<String, IdentityKey>,
1779 }
1780
1781 #[derive(Clone, Copy)]
1782 enum PendingIdentityCall {
1783 Trust,
1784 Save,
1785 }
1786
1787 struct PendingIdentityStore {
1788 inner: MemIdentityStore,
1789 call: PendingIdentityCall,
1790 entered: AtomicBool,
1791 }
1792
1793 impl PendingIdentityStore {
1794 fn new(inner: MemIdentityStore, call: PendingIdentityCall) -> Self {
1795 Self {
1796 inner,
1797 call,
1798 entered: AtomicBool::new(false),
1799 }
1800 }
1801
1802 fn wait_forever<T>(&self) -> impl Future<Output = T> {
1803 self.entered.store(true, Ordering::Release);
1804 futures::future::pending()
1805 }
1806 }
1807
1808 #[cfg_attr(not(target_arch = "wasm32"), async_trait)]
1809 impl IdentityKeyStore for PendingIdentityStore {
1810 async fn get_identity_key_pair(&self) -> Result<IdentityKeyPair> {
1811 self.inner.get_identity_key_pair().await
1812 }
1813
1814 async fn get_local_registration_id(&self) -> Result<u32> {
1815 self.inner.get_local_registration_id().await
1816 }
1817
1818 async fn save_identity(
1819 &mut self,
1820 address: &ProtocolAddress,
1821 identity: &IdentityKey,
1822 ) -> Result<IdentityChange> {
1823 if matches!(self.call, PendingIdentityCall::Save) {
1824 self.wait_forever().await
1825 } else {
1826 self.inner.save_identity(address, identity).await
1827 }
1828 }
1829
1830 async fn is_trusted_identity(
1831 &self,
1832 address: &ProtocolAddress,
1833 identity: &IdentityKey,
1834 direction: Direction,
1835 ) -> Result<bool> {
1836 if matches!(self.call, PendingIdentityCall::Trust) {
1837 self.wait_forever().await
1838 } else {
1839 self.inner
1840 .is_trusted_identity(address, identity, direction)
1841 .await
1842 }
1843 }
1844
1845 async fn get_identity(&self, address: &ProtocolAddress) -> Result<Option<IdentityKey>> {
1846 self.inner.get_identity(address).await
1847 }
1848 }
1849 impl MemIdentityStore {
1850 fn new(pair: IdentityKeyPair, reg_id: u32) -> Self {
1851 Self {
1852 pair,
1853 reg_id,
1854 known: HashMap::new(),
1855 }
1856 }
1857 }
1858 #[cfg_attr(not(target_arch = "wasm32"), async_trait)]
1859 impl IdentityKeyStore for MemIdentityStore {
1860 async fn get_identity_key_pair(&self) -> Result<IdentityKeyPair> {
1861 Ok(self.pair.clone())
1862 }
1863 async fn get_local_registration_id(&self) -> Result<u32> {
1864 Ok(self.reg_id)
1865 }
1866 async fn save_identity(
1867 &mut self,
1868 address: &ProtocolAddress,
1869 identity: &IdentityKey,
1870 ) -> Result<IdentityChange> {
1871 let changed = self
1872 .known
1873 .get(address.as_str())
1874 .is_some_and(|k| k != identity);
1875 self.known.insert(address.as_str().to_string(), *identity);
1876 Ok(IdentityChange::from_changed(changed))
1877 }
1878 async fn is_trusted_identity(
1879 &self,
1880 _address: &ProtocolAddress,
1881 _identity: &IdentityKey,
1882 _direction: Direction,
1883 ) -> Result<bool> {
1884 Ok(true)
1885 }
1886 async fn get_identity(&self, address: &ProtocolAddress) -> Result<Option<IdentityKey>> {
1887 Ok(self.known.get(address.as_str()).copied())
1888 }
1889 }
1890
1891 struct MemPreKeyStore(HashMap<PreKeyId, PreKeyRecord>);
1892 impl MemPreKeyStore {
1893 fn new() -> Self {
1894 Self(HashMap::new())
1895 }
1896 }
1897 #[cfg_attr(not(target_arch = "wasm32"), async_trait)]
1898 impl PreKeyStore for MemPreKeyStore {
1899 async fn get_pre_key(&self, id: PreKeyId) -> Result<PreKeyRecord> {
1900 self.0
1901 .get(&id)
1902 .cloned()
1903 .ok_or(SignalProtocolError::InvalidPreKeyId)
1904 }
1905 async fn save_pre_key(&mut self, id: PreKeyId, record: &PreKeyRecord) -> Result<()> {
1906 self.0.insert(id, record.clone());
1907 Ok(())
1908 }
1909 async fn remove_pre_key(&mut self, id: PreKeyId) -> Result<()> {
1910 self.0.remove(&id);
1911 Ok(())
1912 }
1913 }
1914
1915 struct MemSignedPreKeyStore(HashMap<SignedPreKeyId, SignedPreKeyRecord>);
1916 impl MemSignedPreKeyStore {
1917 fn new() -> Self {
1918 Self(HashMap::new())
1919 }
1920 }
1921 #[cfg_attr(not(target_arch = "wasm32"), async_trait)]
1922 impl SignedPreKeyStore for MemSignedPreKeyStore {
1923 async fn get_signed_pre_key(&self, id: SignedPreKeyId) -> Result<SignedPreKeyRecord> {
1924 self.0
1925 .get(&id)
1926 .cloned()
1927 .ok_or(SignalProtocolError::InvalidSignedPreKeyId)
1928 }
1929 async fn save_signed_pre_key(
1930 &mut self,
1931 id: SignedPreKeyId,
1932 record: &SignedPreKeyRecord,
1933 ) -> Result<()> {
1934 self.0.insert(id, record.clone());
1935 Ok(())
1936 }
1937 }
1938
1939 struct TestPair {
1940 alice_addr: ProtocolAddress,
1941 alice_sessions: MemSessionStore,
1942 alice_identity: MemIdentityStore,
1943 bob_addr: ProtocolAddress,
1944 bob_sessions: MemSessionStore,
1945 bob_identity: MemIdentityStore,
1946 bob_prekeys: MemPreKeyStore,
1947 bob_signed: MemSignedPreKeyStore,
1948 }
1949
1950 fn setup_established_session() -> TestPair {
1951 let mut rng = rand::make_rng::<rand::rngs::StdRng>();
1952
1953 let alice_addr = ProtocolAddress::new("alice", 1.into());
1954 let alice_id = IdentityKeyPair::generate(&mut rng);
1955 let mut alice_sessions = MemSessionStore::new();
1956 let mut alice_identity = MemIdentityStore::new(alice_id, 1);
1957
1958 let bob_addr = ProtocolAddress::new("bob", 1.into());
1959 let bob_id = IdentityKeyPair::generate(&mut rng);
1960 let bob_identity_key = *bob_id.identity_key();
1961
1962 let prekey_id: PreKeyId = 1.into();
1963 let prekey_pair = KeyPair::generate(&mut rng);
1964 let mut bob_prekeys = MemPreKeyStore::new();
1965
1966 let signed_id: SignedPreKeyId = 1.into();
1967 let signed_pair = KeyPair::generate(&mut rng);
1968 let signed_sig = bob_id
1969 .private_key()
1970 .calculate_signature(&signed_pair.public_key.serialize(), &mut rng)
1971 .expect("signature");
1972
1973 let mut bob_sessions = MemSessionStore::new();
1974 let mut bob_identity = MemIdentityStore::new(bob_id, 2);
1975 let mut bob_signed = MemSignedPreKeyStore::new();
1976
1977 futures::executor::block_on(async {
1978 bob_prekeys
1979 .save_pre_key(prekey_id, &PreKeyRecord::new(prekey_id, &prekey_pair))
1980 .await
1981 .expect("save prekey");
1982 bob_signed
1983 .save_signed_pre_key(
1984 signed_id,
1985 &SignedPreKeyRecord::new(
1986 signed_id,
1987 Timestamp::from_epoch_millis(0),
1988 &signed_pair,
1989 &signed_sig,
1990 ),
1991 )
1992 .await
1993 .expect("save signed prekey");
1994 });
1995
1996 let bundle = PreKeyBundle::new(
1997 2,
1998 1.into(),
1999 Some((prekey_id, prekey_pair.public_key)),
2000 signed_id,
2001 signed_pair.public_key,
2002 signed_sig.to_vec(),
2003 bob_identity_key,
2004 )
2005 .expect("valid bundle");
2006
2007 futures::executor::block_on(async {
2009 process_prekey_bundle(
2010 &bob_addr,
2011 &mut alice_sessions,
2012 &mut alice_identity,
2013 &bundle,
2014 &mut rng,
2015 UsePQRatchet::No,
2016 )
2017 .await
2018 .expect("process prekey bundle");
2019
2020 let ct = message_encrypt(
2022 b"hello",
2023 &bob_addr,
2024 &mut alice_sessions,
2025 &mut alice_identity,
2026 )
2027 .await
2028 .expect("encrypt first message");
2029
2030 let ct_msg = CiphertextMessage::PreKeySignalMessage(
2031 PreKeySignalMessage::try_from(ct.serialize())
2032 .expect("parse as PreKeySignalMessage"),
2033 );
2034 message_decrypt(
2035 &ct_msg,
2036 &alice_addr,
2037 &mut bob_sessions,
2038 &mut bob_identity,
2039 &mut bob_prekeys,
2040 &bob_signed,
2041 &mut rng,
2042 UsePQRatchet::No,
2043 )
2044 .await
2045 .expect("decrypt first message");
2046 });
2047
2048 TestPair {
2049 alice_addr,
2050 alice_sessions,
2051 alice_identity,
2052 bob_addr,
2053 bob_sessions,
2054 bob_identity,
2055 bob_prekeys,
2056 bob_signed,
2057 }
2058 }
2059
2060 fn setup_skipped_key_scenario() -> (TestPair, SignalMessage, SessionRecordComponents) {
2064 let mut tp = setup_established_session();
2065 let mut rng = rand::make_rng::<rand::rngs::StdRng>();
2066
2067 let first = futures::executor::block_on(async {
2068 let reply = message_encrypt(
2069 b"ratchet reply",
2070 &tp.alice_addr,
2071 &mut tp.bob_sessions,
2072 &mut tp.bob_identity,
2073 )
2074 .await
2075 .expect("encrypt reply");
2076 let CiphertextMessage::SignalMessage(reply) = reply else {
2077 panic!("established responder sends a signal message");
2078 };
2079 message_decrypt_signal(
2080 &reply,
2081 &tp.bob_addr,
2082 &mut tp.alice_sessions,
2083 &mut tp.alice_identity,
2084 &mut rng,
2085 )
2086 .await
2087 .expect("decrypt reply");
2088
2089 let first = message_encrypt(
2090 b"first",
2091 &tp.bob_addr,
2092 &mut tp.alice_sessions,
2093 &mut tp.alice_identity,
2094 )
2095 .await
2096 .expect("encrypt first");
2097 let second = message_encrypt(
2098 b"second",
2099 &tp.bob_addr,
2100 &mut tp.alice_sessions,
2101 &mut tp.alice_identity,
2102 )
2103 .await
2104 .expect("encrypt second");
2105 let CiphertextMessage::SignalMessage(first) = first else {
2106 panic!("pending pre-key was acknowledged");
2107 };
2108 let CiphertextMessage::SignalMessage(second) = second else {
2109 panic!("pending pre-key was acknowledged");
2110 };
2111
2112 message_decrypt_signal(
2113 &second,
2114 &tp.alice_addr,
2115 &mut tp.bob_sessions,
2116 &mut tp.bob_identity,
2117 &mut rng,
2118 )
2119 .await
2120 .expect("decrypt out of order");
2121 first
2122 });
2123
2124 let stored = tp
2125 .bob_sessions
2126 .0
2127 .remove(tp.alice_addr.as_str())
2128 .expect("stored session");
2129 let components = stored.into_components().expect("components");
2130 (tp, first, components)
2131 }
2132
2133 fn receiver_chain_for<'a>(
2134 session: &'a mut SessionComponents,
2135 message: &SignalMessage,
2136 ) -> &'a mut SessionChainComponents {
2137 session
2138 .receiver_chains
2139 .iter_mut()
2140 .find(|chain| {
2141 chain.sender_ratchet_key.as_deref()
2142 == Some(message.sender_ratchet_key().serialize().as_slice())
2143 })
2144 .expect("matching receiver chain")
2145 }
2146
2147 fn install_record(tp: &mut TestPair, components: SessionRecordComponents) {
2148 let rebuilt = SessionRecord::from_components(components).expect("rebuilt record");
2149 let reloaded = SessionRecord::deserialize(&rebuilt.serialize().expect("serialize"))
2152 .expect("reload persisted record");
2153 tp.bob_sessions
2154 .0
2155 .insert(tp.alice_addr.as_str().to_owned(), reloaded);
2156 }
2157
2158 #[test]
2159 fn closed_receiver_chain_searches_archives_for_a_persisted_skipped_key() {
2160 let (mut tp, first, mut components) = setup_skipped_key_scenario();
2161 let mut rng = rand::make_rng::<rand::rngs::StdRng>();
2162
2163 let current = components.current_session.as_mut().expect("current");
2164 let mut previous = current.clone();
2165 let receiver = receiver_chain_for(current, &first);
2166 receiver.chain_key.as_mut().expect("chain key").key = None;
2167 receiver.message_keys.clear();
2168 let previous_receiver = receiver_chain_for(&mut previous, &first);
2169 previous_receiver.chain_key.as_mut().expect("chain key").key = None;
2170 components.previous_sessions.push(previous);
2171 install_record(&mut tp, components);
2172
2173 futures::executor::block_on(async {
2174 let decrypted = message_decrypt_signal(
2175 &first,
2176 &tp.alice_addr,
2177 &mut tp.bob_sessions,
2178 &mut tp.bob_identity,
2179 &mut rng,
2180 )
2181 .await
2182 .expect("consume skipped key from archived closed chain");
2183 assert_eq!(decrypted.plaintext, b"first");
2184
2185 let replay = message_decrypt_signal(
2186 &first,
2187 &tp.alice_addr,
2188 &mut tp.bob_sessions,
2189 &mut tp.bob_identity,
2190 &mut rng,
2191 )
2192 .await
2193 .expect_err("consumed key remains a duplicate after every session is searched");
2194 assert!(matches!(
2195 replay,
2196 SignalProtocolError::DuplicatedMessage(_, _)
2197 ));
2198 });
2199 }
2200
2201 #[test]
2202 fn open_receiver_chain_searches_archives_for_a_persisted_skipped_key() {
2203 let (mut tp, first, mut components) = setup_skipped_key_scenario();
2204 let mut rng = rand::make_rng::<rand::rngs::StdRng>();
2205
2206 let current = components.current_session.as_mut().expect("current");
2207 let previous = current.clone();
2208 receiver_chain_for(current, &first).message_keys.clear();
2210 components.previous_sessions.push(previous);
2211 install_record(&mut tp, components);
2212
2213 futures::executor::block_on(async {
2214 let decrypted = message_decrypt_signal(
2215 &first,
2216 &tp.alice_addr,
2217 &mut tp.bob_sessions,
2218 &mut tp.bob_identity,
2219 &mut rng,
2220 )
2221 .await
2222 .expect("consume skipped key from archived open chain");
2223 assert_eq!(decrypted.plaintext, b"first");
2224
2225 let replay = message_decrypt_signal(
2226 &first,
2227 &tp.alice_addr,
2228 &mut tp.bob_sessions,
2229 &mut tp.bob_identity,
2230 &mut rng,
2231 )
2232 .await
2233 .expect_err("consumed key remains a duplicate after every session is searched");
2234 assert!(matches!(
2235 replay,
2236 SignalProtocolError::DuplicatedMessage(_, _)
2237 ));
2238 });
2239 }
2240
2241 #[test]
2242 fn a_recognized_duplicate_outranks_candidate_session_mac_failures() {
2243 let (mut tp, first, mut components) = setup_skipped_key_scenario();
2244 let mut rng = rand::make_rng::<rand::rngs::StdRng>();
2245
2246 let current = components.current_session.as_mut().expect("current");
2247 let mut tampered = current.clone();
2248 let receiver = receiver_chain_for(current, &first);
2249 receiver.chain_key.as_mut().expect("chain key").key = None;
2250 receiver.message_keys.clear();
2251 let tampered_receiver = receiver_chain_for(&mut tampered, &first);
2256 let tampered_chain_key = tampered_receiver.chain_key.as_mut().expect("chain key");
2257 tampered_chain_key.index = Some(0);
2258 tampered_chain_key.key = Some(vec![0x42; 32]);
2259 tampered_receiver.message_keys.clear();
2260 components.previous_sessions.push(tampered);
2261 install_record(&mut tp, components);
2262
2263 futures::executor::block_on(async {
2264 let outcome = message_decrypt_signal(
2265 &first,
2266 &tp.alice_addr,
2267 &mut tp.bob_sessions,
2268 &mut tp.bob_identity,
2269 &mut rng,
2270 )
2271 .await
2272 .expect_err("closed chain without its key classifies as a duplicate");
2273 assert!(matches!(
2274 outcome,
2275 SignalProtocolError::DuplicatedMessage(_, _)
2276 ));
2277 });
2278 }
2279
2280 #[test]
2281 fn owned_prekey_decrypt_consumes_the_envelope_on_success() {
2282 const PLAINTEXT_BYTES: usize = 64 * 1024;
2283
2284 let mut tp = setup_established_session();
2285 let mut rng = rand::make_rng::<rand::rngs::StdRng>();
2286 let plaintext = vec![0x7a; PLAINTEXT_BYTES];
2287 let ciphertext = futures::executor::block_on(message_encrypt(
2288 &plaintext,
2289 &tp.bob_addr,
2290 &mut tp.alice_sessions,
2291 &mut tp.alice_identity,
2292 ))
2293 .expect("encrypt owned prekey test message");
2294 assert!(matches!(
2295 &ciphertext,
2296 CiphertextMessage::PreKeySignalMessage(_)
2297 ));
2298 let mut ciphertext = OwnedCiphertextMessage::from(ciphertext);
2299
2300 let decrypted = futures::executor::block_on(message_decrypt_owned(
2301 &mut ciphertext,
2302 &tp.alice_addr,
2303 &mut tp.bob_sessions,
2304 &mut tp.bob_identity,
2305 &mut tp.bob_prekeys,
2306 &tp.bob_signed,
2307 &mut rng,
2308 UsePQRatchet::No,
2309 ))
2310 .expect("decrypt caller-owned prekey message");
2311
2312 assert_eq!(decrypted.plaintext, plaintext);
2313 assert!(!ciphertext.is_available());
2314 }
2315
2316 #[test]
2317 fn owned_prekey_bad_mac_preserves_the_envelope_for_retry() {
2318 let mut tp = setup_established_session();
2319 let mut rng = rand::make_rng::<rand::rngs::StdRng>();
2320 let ciphertext = futures::executor::block_on(message_encrypt(
2321 b"authenticated retry",
2322 &tp.bob_addr,
2323 &mut tp.alice_sessions,
2324 &mut tp.alice_identity,
2325 ))
2326 .expect("encrypt owned retry test message");
2327 let CiphertextMessage::PreKeySignalMessage(pre_key) = ciphertext else {
2328 panic!("pending session must emit a PreKey envelope");
2329 };
2330 let mut nested_wire = pre_key.message().serialized().to_vec();
2331 let mac_byte = nested_wire
2332 .len()
2333 .checked_sub(4)
2334 .expect("test Signal envelope contains a MAC");
2335 nested_wire[mac_byte] ^= 0xff;
2336 let nested = SignalMessage::try_from(nested_wire.as_slice())
2337 .expect("corrupted MAC keeps the nested envelope parseable");
2338 let corrupted = PreKeySignalMessage::new(
2339 pre_key.message_version(),
2340 pre_key.registration_id(),
2341 pre_key.pre_key_id(),
2342 pre_key.signed_pre_key_id(),
2343 *pre_key.base_key(),
2344 *pre_key.identity_key(),
2345 nested,
2346 )
2347 .expect("rebuild test PreKey envelope");
2348 let mut corrupted =
2349 OwnedCiphertextMessage::from(CiphertextMessage::PreKeySignalMessage(corrupted));
2350
2351 let error = futures::executor::block_on(message_decrypt_owned(
2352 &mut corrupted,
2353 &tp.alice_addr,
2354 &mut tp.bob_sessions,
2355 &mut tp.bob_identity,
2356 &mut tp.bob_prekeys,
2357 &tp.bob_signed,
2358 &mut rng,
2359 UsePQRatchet::No,
2360 ))
2361 .expect_err("a corrupted nested MAC must fail authentication");
2362
2363 assert!(matches!(error, SignalProtocolError::BadMac(_)));
2364 assert!(corrupted.is_available());
2365 }
2366
2367 #[test]
2368 fn cancelled_signal_decrypt_restores_the_ratchet_for_retry() {
2369 let mut tp = setup_established_session();
2370 let mut rng = rand::make_rng::<rand::rngs::StdRng>();
2371 let ciphertext = futures::executor::block_on(async {
2372 let reply = message_encrypt(
2373 b"ack",
2374 &tp.alice_addr,
2375 &mut tp.bob_sessions,
2376 &mut tp.bob_identity,
2377 )
2378 .await
2379 .expect("encrypt reply");
2380 let CiphertextMessage::SignalMessage(reply) = reply else {
2381 panic!("established reply must be a SignalMessage");
2382 };
2383 message_decrypt_signal(
2384 &reply,
2385 &tp.bob_addr,
2386 &mut tp.alice_sessions,
2387 &mut tp.alice_identity,
2388 &mut rng,
2389 )
2390 .await
2391 .expect("decrypt reply");
2392
2393 let message = message_encrypt(
2394 b"retry-safe",
2395 &tp.bob_addr,
2396 &mut tp.alice_sessions,
2397 &mut tp.alice_identity,
2398 )
2399 .await
2400 .expect("encrypt test message");
2401 let CiphertextMessage::SignalMessage(message) = message else {
2402 panic!("acknowledged session must emit a SignalMessage");
2403 };
2404 message
2405 });
2406
2407 let record = tp
2408 .bob_sessions
2409 .0
2410 .remove(tp.alice_addr.as_str())
2411 .expect("Bob session");
2412 let before = record.serialize().expect("serialize baseline");
2413 let mut sessions = DestructiveSessionStore::new(Some(record));
2414 let mut identity = PendingIdentityStore::new(tp.bob_identity, PendingIdentityCall::Trust);
2415
2416 let mut decrypt = Box::pin(message_decrypt_signal(
2417 &ciphertext,
2418 &tp.alice_addr,
2419 &mut sessions,
2420 &mut identity,
2421 &mut rng,
2422 ));
2423 let waker = futures::task::noop_waker();
2424 let mut context = Context::from_waker(&waker);
2425 assert!(matches!(decrypt.as_mut().poll(&mut context), Poll::Pending));
2426 drop(decrypt);
2427 assert!(identity.entered.load(Ordering::Acquire));
2428
2429 let restored = sessions.take().expect("cancelled checkout restored");
2430 assert_eq!(restored.serialize().expect("serialize restored"), before);
2431 sessions.replace(restored);
2432 let retried = futures::executor::block_on(message_decrypt_signal(
2433 &ciphertext,
2434 &tp.alice_addr,
2435 &mut sessions,
2436 &mut identity.inner,
2437 &mut rng,
2438 ))
2439 .expect("redelivery must decrypt");
2440 assert_eq!(retried.plaintext, b"retry-safe");
2441 }
2442
2443 #[test]
2444 fn cancelled_fresh_prekey_decrypt_discards_the_promoted_session() {
2445 let mut tp = setup_established_session();
2446 let mut rng = rand::make_rng::<rand::rngs::StdRng>();
2447 let ciphertext = futures::executor::block_on(message_encrypt(
2448 b"fresh-retry",
2449 &tp.bob_addr,
2450 &mut tp.alice_sessions,
2451 &mut tp.alice_identity,
2452 ))
2453 .expect("encrypt prekey test message");
2454 let CiphertextMessage::PreKeySignalMessage(ciphertext) = ciphertext else {
2455 panic!("unacknowledged session must emit a PreKeySignalMessage");
2456 };
2457
2458 let mut sessions = DestructiveSessionStore::new(None);
2459 let mut identity = PendingIdentityStore::new(tp.bob_identity, PendingIdentityCall::Save);
2460 let mut decrypt = Box::pin(message_decrypt_prekey(
2461 &ciphertext,
2462 &tp.alice_addr,
2463 &mut sessions,
2464 &mut identity,
2465 &mut tp.bob_prekeys,
2466 &tp.bob_signed,
2467 &mut rng,
2468 UsePQRatchet::No,
2469 ));
2470 let waker = futures::task::noop_waker();
2471 let mut context = Context::from_waker(&waker);
2472 assert!(matches!(decrypt.as_mut().poll(&mut context), Poll::Pending));
2473 drop(decrypt);
2474 assert!(identity.entered.load(Ordering::Acquire));
2475 assert!(sessions.take().is_none());
2476
2477 let retried = futures::executor::block_on(message_decrypt_prekey(
2478 &ciphertext,
2479 &tp.alice_addr,
2480 &mut sessions,
2481 &mut identity.inner,
2482 &mut tp.bob_prekeys,
2483 &tp.bob_signed,
2484 &mut rng,
2485 UsePQRatchet::No,
2486 ))
2487 .expect("redelivery must establish the session");
2488 assert_eq!(retried.plaintext, b"fresh-retry");
2489 }
2490
2491 #[allow(clippy::type_complexity)]
2495 fn fresh_bob(
2496 rng: &mut rand::rngs::StdRng,
2497 ) -> (
2498 ProtocolAddress,
2499 MemSessionStore,
2500 MemIdentityStore,
2501 MemPreKeyStore,
2502 MemSignedPreKeyStore,
2503 PreKeyBundle,
2504 ) {
2505 let bob_addr = ProtocolAddress::new("bob", 1.into());
2506 let bob_id = IdentityKeyPair::generate(rng);
2507 let bob_identity_key = *bob_id.identity_key();
2508
2509 let prekey_id: PreKeyId = 1.into();
2510 let prekey_pair = KeyPair::generate(rng);
2511 let signed_id: SignedPreKeyId = 1.into();
2512 let signed_pair = KeyPair::generate(rng);
2513 let signed_sig = bob_id
2514 .private_key()
2515 .calculate_signature(&signed_pair.public_key.serialize(), rng)
2516 .expect("signature");
2517
2518 let mut bob_prekeys = MemPreKeyStore::new();
2519 let mut bob_signed = MemSignedPreKeyStore::new();
2520 futures::executor::block_on(async {
2521 bob_prekeys
2522 .save_pre_key(prekey_id, &PreKeyRecord::new(prekey_id, &prekey_pair))
2523 .await
2524 .expect("save prekey");
2525 bob_signed
2526 .save_signed_pre_key(
2527 signed_id,
2528 &SignedPreKeyRecord::new(
2529 signed_id,
2530 Timestamp::from_epoch_millis(0),
2531 &signed_pair,
2532 &signed_sig,
2533 ),
2534 )
2535 .await
2536 .expect("save signed prekey");
2537 });
2538
2539 let bundle = PreKeyBundle::new(
2540 2,
2541 1.into(),
2542 Some((prekey_id, prekey_pair.public_key)),
2543 signed_id,
2544 signed_pair.public_key,
2545 signed_sig.to_vec(),
2546 bob_identity_key,
2547 )
2548 .expect("valid bundle");
2549
2550 (
2551 bob_addr,
2552 MemSessionStore::new(),
2553 MemIdentityStore::new(bob_id, 2),
2554 bob_prekeys,
2555 bob_signed,
2556 bundle,
2557 )
2558 }
2559
2560 #[test]
2565 fn process_prekey_bundle_reports_identity_change() {
2566 let mut rng = rand::make_rng::<rand::rngs::StdRng>();
2567 let (bob_addr, _bs, _bi, _bp, _bsp, bundle1) = fresh_bob(&mut rng);
2568 let (_bob_addr2, _bs2, _bi2, _bp2, _bsp2, bundle2) = fresh_bob(&mut rng);
2569
2570 let alice_id = IdentityKeyPair::generate(&mut rng);
2571 let mut alice_sessions = MemSessionStore::new();
2572 let mut alice_identity = MemIdentityStore::new(alice_id, 1);
2573
2574 futures::executor::block_on(async {
2575 let first = process_prekey_bundle(
2576 &bob_addr,
2577 &mut alice_sessions,
2578 &mut alice_identity,
2579 &bundle1,
2580 &mut rng,
2581 UsePQRatchet::No,
2582 )
2583 .await
2584 .expect("first bundle");
2585 assert_eq!(first, IdentityChange::NewOrUnchanged, "first contact");
2586
2587 let second = process_prekey_bundle(
2588 &bob_addr,
2589 &mut alice_sessions,
2590 &mut alice_identity,
2591 &bundle2,
2592 &mut rng,
2593 UsePQRatchet::No,
2594 )
2595 .await
2596 .expect("second bundle");
2597 assert_eq!(
2598 second,
2599 IdentityChange::ReplacedExisting,
2600 "different identity for same address"
2601 );
2602 });
2603 }
2604
2605 #[test]
2609 fn decrypt_prekey_reports_identity_change() {
2610 let mut rng = rand::make_rng::<rand::rngs::StdRng>();
2611
2612 for (preseed_stale, expected) in [
2613 (false, IdentityChange::NewOrUnchanged),
2614 (true, IdentityChange::ReplacedExisting),
2615 ] {
2616 let alice_addr = ProtocolAddress::new("alice", 1.into());
2617 let alice_id = IdentityKeyPair::generate(&mut rng);
2618 let mut alice_sessions = MemSessionStore::new();
2619 let mut alice_identity = MemIdentityStore::new(alice_id, 1);
2620
2621 let (bob_addr, mut bob_sessions, mut bob_identity, mut bob_prekeys, bob_signed, bundle) =
2622 fresh_bob(&mut rng);
2623
2624 futures::executor::block_on(async {
2625 if preseed_stale {
2626 let stale = *IdentityKeyPair::generate(&mut rng).identity_key();
2628 bob_identity
2629 .save_identity(&alice_addr, &stale)
2630 .await
2631 .expect("seed stale identity");
2632 }
2633
2634 process_prekey_bundle(
2635 &bob_addr,
2636 &mut alice_sessions,
2637 &mut alice_identity,
2638 &bundle,
2639 &mut rng,
2640 UsePQRatchet::No,
2641 )
2642 .await
2643 .expect("process bundle");
2644
2645 let ct =
2646 message_encrypt(b"hi", &bob_addr, &mut alice_sessions, &mut alice_identity)
2647 .await
2648 .expect("encrypt");
2649 let pkmsg = CiphertextMessage::PreKeySignalMessage(
2650 PreKeySignalMessage::try_from(ct.serialize()).expect("parse pkmsg"),
2651 );
2652
2653 let res = message_decrypt(
2654 &pkmsg,
2655 &alice_addr,
2656 &mut bob_sessions,
2657 &mut bob_identity,
2658 &mut bob_prekeys,
2659 &bob_signed,
2660 &mut rng,
2661 UsePQRatchet::No,
2662 )
2663 .await
2664 .expect("decrypt pkmsg");
2665
2666 assert_eq!(res.plaintext, b"hi".to_vec());
2667 assert_eq!(res.identity_change, expected);
2668 });
2669 }
2670 }
2671
2672 #[test]
2677 fn process_prekey_signals_reuse_for_established_session() {
2678 let mut rng = rand::make_rng::<rand::rngs::StdRng>();
2679 let alice_addr = ProtocolAddress::new("alice", 1.into());
2680 let alice_id = IdentityKeyPair::generate(&mut rng);
2681 let mut alice_sessions = MemSessionStore::new();
2682 let mut alice_identity = MemIdentityStore::new(alice_id, 1);
2683
2684 let (bob_addr, _bs, bob_identity, bob_prekeys, bob_signed, bundle) = fresh_bob(&mut rng);
2685
2686 futures::executor::block_on(async {
2687 process_prekey_bundle(
2688 &bob_addr,
2689 &mut alice_sessions,
2690 &mut alice_identity,
2691 &bundle,
2692 &mut rng,
2693 UsePQRatchet::No,
2694 )
2695 .await
2696 .expect("process bundle");
2697 let ct = message_encrypt(b"hi", &bob_addr, &mut alice_sessions, &mut alice_identity)
2698 .await
2699 .expect("encrypt");
2700 let pkmsg = PreKeySignalMessage::try_from(ct.serialize()).expect("parse pkmsg");
2701
2702 let mut record = SessionRecord::new_fresh();
2703 let (_used, _save, reused_first) = process_prekey(
2704 &pkmsg,
2705 &alice_addr,
2706 &mut record,
2707 &bob_identity,
2708 &bob_prekeys,
2709 &bob_signed,
2710 UsePQRatchet::No,
2711 )
2712 .await
2713 .expect("first process_prekey");
2714 assert!(!reused_first, "first pkmsg establishes a new session");
2715
2716 let (_used2, _save2, reused_again) = process_prekey(
2717 &pkmsg,
2718 &alice_addr,
2719 &mut record,
2720 &bob_identity,
2721 &bob_prekeys,
2722 &bob_signed,
2723 UsePQRatchet::No,
2724 )
2725 .await
2726 .expect("second process_prekey");
2727 assert!(
2728 reused_again,
2729 "re-processing the same pkmsg must signal session reuse"
2730 );
2731 });
2732 }
2733
2734 #[test]
2740 fn decrypt_corrupted_mac_returns_bad_mac() {
2741 let mut tp = setup_established_session();
2742 let mut rng = rand::make_rng::<rand::rngs::StdRng>();
2743
2744 futures::executor::block_on(async {
2745 let bob_reply = message_encrypt(
2747 b"ack",
2748 &tp.alice_addr,
2749 &mut tp.bob_sessions,
2750 &mut tp.bob_identity,
2751 )
2752 .await
2753 .expect("Bob encrypt reply");
2754
2755 let bob_msg = SignalMessage::try_from(bob_reply.serialize())
2758 .expect("Bob's reply should be a SignalMessage");
2759 message_decrypt_signal(
2760 &bob_msg,
2761 &tp.bob_addr,
2762 &mut tp.alice_sessions,
2763 &mut tp.alice_identity,
2764 &mut rng,
2765 )
2766 .await
2767 .expect("Alice should decrypt Bob's reply");
2768
2769 let ct = message_encrypt(
2772 b"secret payload",
2773 &tp.bob_addr,
2774 &mut tp.alice_sessions,
2775 &mut tp.alice_identity,
2776 )
2777 .await
2778 .expect("Alice encrypt second message");
2779
2780 assert!(
2782 matches!(ct, CiphertextMessage::SignalMessage(_)),
2783 "Expected SignalMessage after full round-trip, got {:?}",
2784 ct.message_type()
2785 );
2786
2787 let raw = ct.serialize();
2789 let mut corrupted_bytes = raw.to_vec();
2790 let mac_offset = corrupted_bytes.len() - 4;
2791 corrupted_bytes[mac_offset] ^= 0xFF;
2792
2793 let corrupted = SignalMessage::try_from(corrupted_bytes.as_slice())
2794 .expect("protobuf is intact, only MAC region is modified");
2795
2796 const DECRYPT_RNG_SEED: u64 = 0xD3FE_22ED;
2800 let mut decrypt_rng =
2801 <rand::rngs::StdRng as rand::SeedableRng>::seed_from_u64(DECRYPT_RNG_SEED);
2802 let mut untouched_rng =
2803 <rand::rngs::StdRng as rand::SeedableRng>::seed_from_u64(DECRYPT_RNG_SEED);
2804
2805 let err = message_decrypt_signal(
2807 &corrupted,
2808 &tp.alice_addr,
2809 &mut tp.bob_sessions,
2810 &mut tp.bob_identity,
2811 &mut decrypt_rng,
2812 )
2813 .await
2814 .expect_err("decryption should fail due to corrupted MAC");
2815
2816 assert!(
2818 matches!(err, SignalProtocolError::BadMac(_)),
2819 "expected BadMac, got: {err}"
2820 );
2821 use rand::RngExt as _;
2822 assert_eq!(decrypt_rng.random::<u64>(), untouched_rng.random::<u64>());
2823 });
2824 }
2825
2826 #[test]
2832 fn inorder_macfail_rolls_back_chain_and_recovers() {
2833 let mut tp = setup_established_session();
2834 let mut rng = rand::make_rng::<rand::rngs::StdRng>();
2835
2836 futures::executor::block_on(async {
2837 let bob_reply = message_encrypt(
2840 b"ack",
2841 &tp.alice_addr,
2842 &mut tp.bob_sessions,
2843 &mut tp.bob_identity,
2844 )
2845 .await
2846 .expect("Bob encrypt reply");
2847 let bob_msg =
2848 SignalMessage::try_from(bob_reply.serialize()).expect("reply is a SignalMessage");
2849 message_decrypt_signal(
2850 &bob_msg,
2851 &tp.bob_addr,
2852 &mut tp.alice_sessions,
2853 &mut tp.alice_identity,
2854 &mut rng,
2855 )
2856 .await
2857 .expect("Alice decrypts reply");
2858
2859 let m1 = message_encrypt(
2861 b"first",
2862 &tp.bob_addr,
2863 &mut tp.alice_sessions,
2864 &mut tp.alice_identity,
2865 )
2866 .await
2867 .expect("encrypt m1");
2868 let m2 = message_encrypt(
2869 b"second",
2870 &tp.bob_addr,
2871 &mut tp.alice_sessions,
2872 &mut tp.alice_identity,
2873 )
2874 .await
2875 .expect("encrypt m2");
2876 assert!(
2877 matches!(m2, CiphertextMessage::SignalMessage(_)),
2878 "m2 should be a SignalMessage"
2879 );
2880
2881 let m1_sig = SignalMessage::try_from(m1.serialize()).expect("m1 SignalMessage");
2884 let p1 = message_decrypt_signal(
2885 &m1_sig,
2886 &tp.alice_addr,
2887 &mut tp.bob_sessions,
2888 &mut tp.bob_identity,
2889 &mut rng,
2890 )
2891 .await
2892 .expect("Bob decrypts m1");
2893 assert_eq!(p1.plaintext, b"first");
2894
2895 let raw = m2.serialize();
2897 let mut corrupted = raw.to_vec();
2898 let off = corrupted.len() - 4;
2899 corrupted[off] ^= 0xFF;
2900 let corrupted_msg =
2901 SignalMessage::try_from(corrupted.as_slice()).expect("protobuf intact");
2902 let err = message_decrypt_signal(
2903 &corrupted_msg,
2904 &tp.alice_addr,
2905 &mut tp.bob_sessions,
2906 &mut tp.bob_identity,
2907 &mut rng,
2908 )
2909 .await
2910 .expect_err("corrupted m2 must fail");
2911 assert!(
2912 matches!(err, SignalProtocolError::BadMac(_)),
2913 "expected BadMac, got: {err}"
2914 );
2915
2916 let m2_sig = SignalMessage::try_from(m2.serialize()).expect("m2 SignalMessage");
2918 let p2 = message_decrypt_signal(
2919 &m2_sig,
2920 &tp.alice_addr,
2921 &mut tp.bob_sessions,
2922 &mut tp.bob_identity,
2923 &mut rng,
2924 )
2925 .await
2926 .expect("Bob decrypts legit m2 after rollback");
2927 assert_eq!(p2.plaintext, b"second");
2928 });
2929 }
2930
2931 #[test]
2936 fn encrypt_releases_oversized_buffer() {
2937 let mut tp = setup_established_session();
2938 futures::executor::block_on(async {
2939 let big = vec![7u8; 32 * 1024]; message_encrypt(
2941 &big,
2942 &tp.bob_addr,
2943 &mut tp.alice_sessions,
2944 &mut tp.alice_identity,
2945 )
2946 .await
2947 .expect("encrypt large message");
2948
2949 let cap = encryption_buffer_capacity();
2955 assert!(
2956 cap <= EncryptionBuffer::MAX_CAPACITY,
2957 "encrypt buffer should be released after an oversized send, got capacity {cap}"
2958 );
2959 });
2960 }
2961
2962 #[test]
2965 fn decrypt_with_empty_session_returns_session_not_found() {
2966 let mut rng = rand::make_rng::<rand::rngs::StdRng>();
2967
2968 let alice_addr = ProtocolAddress::new("alice", 1.into());
2969 let alice_id = IdentityKeyPair::generate(&mut rng);
2970 let bob_id = IdentityKeyPair::generate(&mut rng);
2971 let alice_identity_key = *alice_id.identity_key();
2972 let bob_identity_key = *bob_id.identity_key();
2973
2974 let mut bob_sessions = MemSessionStore::new();
2976 let mut bob_identity = MemIdentityStore::new(bob_id, 2);
2977
2978 futures::executor::block_on(async {
2979 bob_sessions
2981 .store_session(&alice_addr, SessionRecord::new_fresh())
2982 .await
2983 .expect("store empty session");
2984
2985 let loaded = bob_sessions
2987 .load_session(&alice_addr)
2988 .await
2989 .expect("load session");
2990 assert!(loaded.is_some(), "record should exist");
2991
2992 let ratchet_key = KeyPair::generate(&mut rng).public_key;
2995 let msg = SignalMessage::new(
2996 4,
2997 &[0u8; 32],
2998 ratchet_key,
2999 0,
3000 0,
3001 b"dummy",
3002 &alice_identity_key,
3003 &bob_identity_key,
3004 )
3005 .expect("craft SignalMessage");
3006
3007 let err = message_decrypt_signal(
3008 &msg,
3009 &alice_addr,
3010 &mut bob_sessions,
3011 &mut bob_identity,
3012 &mut rng,
3013 )
3014 .await
3015 .expect_err("should fail on empty session");
3016
3017 assert!(
3018 matches!(err, SignalProtocolError::SessionNotFound(_)),
3019 "expected SessionNotFound for degenerate session, got: {err}"
3020 );
3021 });
3022 }
3023}