Skip to main content

libsignal_service/
cipher.rs

1use std::{convert::TryFrom, fmt, time::SystemTime};
2
3use aes::cipher::block_padding::{Iso7816, Padding};
4use base64::prelude::*;
5use libsignal_core::ServiceIdKind;
6use libsignal_protocol::{
7    group_decrypt, message_decrypt_prekey, message_decrypt_signal,
8    message_encrypt, process_sender_key_distribution_message,
9    sealed_sender_decrypt_to_usmc, sealed_sender_encrypt,
10    CiphertextMessageType, DeviceId, IdentityKeyStore, KyberPreKeyStore,
11    PlaintextContent, PreKeySignalMessage, PreKeyStore, ProtocolAddress,
12    ProtocolStore, PublicKey, SealedSenderDecryptionResult, SenderCertificate,
13    SenderKeyDistributionMessage, SenderKeyStore, ServiceId, SessionNotFound,
14    SessionStore, SessionUsabilityRequirements, SignalMessage,
15    SignalProtocolError, SignedPreKeyStore, Timestamp,
16    UnidentifiedSenderMessageContent,
17};
18use prost::Message;
19use rand::{rng, CryptoRng, Rng};
20use uuid::Uuid;
21
22use crate::{
23    content::{Content, Metadata},
24    envelope::Envelope,
25    push_service::ServiceError,
26    sender::OutgoingPushMessage,
27    session_store::SessionStoreExt,
28    utils::BASE64_RELAXED,
29    ServiceIdExt,
30};
31
32/// Decrypts incoming messages and encrypts outgoing messages.
33///
34/// Equivalent of SignalServiceCipher in Java.
35#[derive(Clone)]
36pub struct ServiceCipher<S> {
37    protocol_store: S,
38    trust_roots: Vec<PublicKey>,
39    local_address: ProtocolAddress,
40}
41
42impl<S> fmt::Debug for ServiceCipher<S> {
43    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
44        f.debug_struct("ServiceCipher")
45            .field("protocol_store", &"...")
46            .field("trust_root", &"...")
47            .field("local_address", &self.local_address)
48            .finish()
49    }
50}
51
52fn debug_envelope(envelope: &Envelope) -> String {
53    if envelope.content.is_none() {
54        "Envelope { empty }".to_string()
55    } else {
56        format!(
57            "Envelope {{ \
58                 source_address: {:?}, \
59                 source_device: {:?}, \
60                 server_guid: {:?}, \
61                 timestamp: {:?}, \
62                 content: {} bytes, \
63             }}",
64            envelope.parse_source_service_id(),
65            envelope.source_device(),
66            envelope.server_guid(),
67            envelope.timestamp(),
68            envelope.content().len(),
69        )
70    }
71}
72
73impl<S> ServiceCipher<S>
74where
75    S: ProtocolStore + SenderKeyStore + SessionStoreExt + Clone,
76{
77    pub fn new(
78        protocol_store: S,
79        trust_roots: Vec<PublicKey>,
80        local_address: ProtocolAddress,
81    ) -> Self {
82        Self {
83            protocol_store,
84            trust_roots,
85            local_address,
86        }
87    }
88
89    /// Opens ("decrypts") an envelope.
90    ///
91    /// Envelopes may be empty, in which case this method returns `Ok(None)`
92    #[tracing::instrument(skip(envelope, csprng), fields(envelope = debug_envelope(&envelope)))]
93    pub async fn open_envelope<R: Rng + CryptoRng>(
94        &mut self,
95        envelope: Envelope,
96        csprng: &mut R,
97    ) -> Result<Option<Content>, ServiceError> {
98        let local_service: ServiceId =
99            ServiceId::parse_from_service_id_string(self.local_address.name())
100                .expect("valid protocol address name");
101
102        if envelope.content.is_some() {
103            let plaintext = self.decrypt(&envelope, csprng).await?;
104            let was_plaintext = plaintext.metadata.was_plaintext;
105            let message =
106                crate::proto::Content::decode(plaintext.data.as_slice())?;
107
108            tracing::Span::current()
109                .record("envelope_metadata", plaintext.metadata.to_string());
110
111            // Sanity test: if the envelope was plaintext, the message should *only* be a
112            // decryption failure error
113            if was_plaintext {
114                if let crate::proto::Content {
115                    data_message: None,
116                    sync_message: None,
117                    call_message: None,
118                    null_message: None,
119                    receipt_message: None,
120                    typing_message: None,
121                    sender_key_distribution_message: None,
122                    decryption_error_message: Some(decryption_error_message),
123                    story_message: None,
124                    pni_signature_message: None,
125                    edit_message: None,
126                } = &message
127                {
128                    tracing::warn!(
129                        ?envelope,
130                        "Received a decryption error message: {}.",
131                        String::from_utf8_lossy(decryption_error_message)
132                    );
133                } else {
134                    tracing::error!(
135                        ?envelope,
136                        "Received a plaintext envelope with a non-decryption error message."
137                    );
138                    return Ok(None);
139                }
140            }
141
142            if message.sync_message.is_some()
143                && plaintext.metadata.sender.aci().map(Uuid::from)
144                    != Some(local_service.raw_uuid())
145                && local_service.kind() == ServiceIdKind::Aci
146            {
147                tracing::warn!("Source is not ourself.");
148                return Ok(None);
149            }
150
151            if let Some(bytes) = &message.sender_key_distribution_message {
152                let skdm = SenderKeyDistributionMessage::try_from(&bytes[..])?;
153                process_sender_key_distribution_message(
154                    &plaintext.metadata.protocol_address()?,
155                    &skdm,
156                    &mut self.protocol_store,
157                )
158                .await?;
159
160                match Content::from_proto(message, plaintext.metadata) {
161                    Err(ServiceError::UnsupportedContent) => {
162                        tracing::trace!("Sender key distribution message without additional content");
163                        return Ok(None);
164                    },
165                    content => return Ok(Some(content?)),
166                }
167            }
168            let content = Content::from_proto(message, plaintext.metadata);
169            Ok(Some(content?))
170        } else {
171            Ok(None)
172        }
173    }
174
175    /// Equivalent of decrypt(Envelope, ciphertext)
176    ///
177    /// Triage of legacy messages happens inside this method, as opposed to the
178    /// Java implementation, because it makes the borrow checker and the
179    /// author happier.
180    #[tracing::instrument(skip(envelope, csprng), fields(envelope = debug_envelope(envelope)))]
181    async fn decrypt<R: Rng + CryptoRng>(
182        &mut self,
183        envelope: &Envelope,
184        csprng: &mut R,
185    ) -> Result<Plaintext, ServiceError> {
186        let local_service: ServiceId =
187            ServiceId::parse_from_service_id_string(self.local_address.name())
188                .expect("valid protocol address name");
189
190        let ciphertext = if let Some(msg) = envelope.content.as_ref() {
191            msg
192        } else {
193            return Err(ServiceError::InvalidFrame {
194                reason:
195                    "envelope should have either a legacy message or content.",
196            });
197        };
198
199        let server_guid = envelope.parse_server_guid();
200
201        let Some(destination_service_id) =
202            envelope.parse_destination_service_id()
203        else {
204            tracing::warn!(
205                "missing destination service id; ignoring invalid message."
206            );
207            return Err(ServiceError::InvalidFrame {
208                reason: "missing destination service id",
209            });
210        };
211
212        if destination_service_id != local_service {
213            tracing::warn!(
214                "mismatching destination service id; ignoring invalid message."
215            );
216            return Err(ServiceError::InvalidFrame {
217                reason: "mismatch destination service id",
218            });
219        }
220
221        let source_service_id = envelope.parse_source_service_id();
222
223        if destination_service_id.kind() == ServiceIdKind::Pni
224            && source_service_id.is_none()
225        {
226            tracing::warn!("received sealed sender message to our PNI; ignoring invalid message");
227            return Err(ServiceError::InvalidFrame {
228                reason: "sealed sender received on our PNI",
229            });
230        }
231
232        // TODO: let chain in edition 2024
233        if let Some(source_service_id) = source_service_id {
234            if source_service_id.kind() == ServiceIdKind::Pni
235                && envelope.r#type() != Type::ServerDeliveryReceipt
236            {
237                tracing::warn!("got a message from a PNI that was not a ServerDeliveryReceipt; ignoring invalid message");
238                return Err(ServiceError::InvalidFrame {
239                    reason: "PNI received a non-ServerDeliveryReceipt",
240                });
241            }
242        }
243
244        // Extract both kinds of timestamps.
245        // Note that we do not `?` here, but rather only later, in case we ever have a branch which
246        // is not concerned with envelope metadata.
247        let timestamp = chrono::DateTime::from_timestamp_millis(
248            envelope.timestamp() as i64,
249        )
250        .ok_or(ServiceError::InvalidFrame {
251            reason: "unparseable timestamp",
252        });
253        let server_timestamp = chrono::DateTime::from_timestamp_millis(
254            envelope.server_timestamp() as i64,
255        )
256        .ok_or(ServiceError::InvalidFrame {
257            reason: "unparseable server timestamp",
258        });
259
260        use crate::proto::envelope::Type;
261        let plaintext = match envelope.r#type() {
262            Type::PrekeyBundle => {
263                let source_service_id = source_service_id
264                    .expect("prekey bundle format contains source_service_id");
265                let sender = get_preferred_protocol_address(
266                    &self.protocol_store,
267                    &source_service_id,
268                    envelope.source_device().try_into()?,
269                )
270                .await?;
271                let metadata = Metadata {
272                    destination: envelope
273                        .parse_destination_service_id()
274                        .expect("prekey bundle format"),
275                    sender: source_service_id,
276                    sender_device: envelope.source_device().try_into()?,
277                    timestamp: timestamp?,
278                    server_timestamp: server_timestamp?,
279                    needs_receipt: false,
280                    unidentified_sender: false,
281                    was_plaintext: false,
282
283                    server_guid,
284                };
285
286                let mut data = message_decrypt_prekey(
287                    &PreKeySignalMessage::try_from(&ciphertext[..])?,
288                    &sender,
289                    &self.local_address,
290                    &mut self.protocol_store.clone(),
291                    &mut self.protocol_store.clone(),
292                    &mut self.protocol_store.clone(),
293                    &self.protocol_store.clone(),
294                    &mut self.protocol_store.clone(),
295                    csprng,
296                )
297                .await?
298                .as_slice()
299                .to_vec();
300
301                let session_record = self
302                    .protocol_store
303                    .load_session(&sender)
304                    .await?
305                    .ok_or_else(|| {
306                        SignalProtocolError::SessionNotFound(
307                            SessionNotFound::new(sender, "decrypt"),
308                        )
309                    })?;
310
311                strip_padding_version(
312                    session_record.session_version()?,
313                    &mut data,
314                )?;
315                Plaintext { metadata, data }
316            },
317            Type::PlaintextContent => {
318                tracing::warn!(?envelope, "Envelope with plaintext content.  This usually indicates a decryption retry.");
319                let source_service_id = source_service_id
320                    .expect("prekey bundle format contains source_service_id");
321                let metadata = Metadata {
322                    destination: envelope
323                        .parse_destination_service_id()
324                        .expect("plaintext content format"),
325                    sender: source_service_id,
326                    sender_device: envelope.source_device().try_into()?,
327                    timestamp: timestamp?,
328                    server_timestamp: server_timestamp?,
329                    needs_receipt: false,
330                    unidentified_sender: false,
331                    was_plaintext: true,
332
333                    server_guid,
334                };
335                // Unsealed envelope wrapping a PlaintextContent.
336                // Should contain a DecryptionErrorMessage.
337                let plaintext_content =
338                    PlaintextContent::try_from(&ciphertext[..])?;
339                let mut data = plaintext_content.body().to_vec();
340                strip_padding(&mut data)?;
341                Plaintext { metadata, data }
342            },
343            Type::Ciphertext => {
344                let source_service_id = source_service_id
345                    .expect("prekey bundle format contains source_service_id");
346                let sender = get_preferred_protocol_address(
347                    &self.protocol_store,
348                    &source_service_id,
349                    envelope.source_device().try_into()?,
350                )
351                .await?;
352                let metadata = Metadata {
353                    destination: envelope
354                        .parse_destination_service_id()
355                        .expect("ciphertext envelope format"),
356                    sender: envelope
357                        .parse_source_service_id()
358                        .expect("ciphertext envelope format"),
359                    sender_device: envelope.source_device().try_into()?,
360                    timestamp: timestamp?,
361                    server_timestamp: server_timestamp?,
362                    needs_receipt: false,
363                    unidentified_sender: false,
364                    was_plaintext: false,
365
366                    server_guid,
367                };
368
369                let mut data = message_decrypt_signal(
370                    &SignalMessage::try_from(&ciphertext[..])?,
371                    &sender,
372                    &self.local_address,
373                    &mut self.protocol_store.clone(),
374                    &mut self.protocol_store.clone(),
375                    csprng,
376                )
377                .await?
378                .as_slice()
379                .to_vec();
380
381                let session_record = self
382                    .protocol_store
383                    .load_session(&sender)
384                    .await?
385                    .ok_or_else(|| {
386                        SignalProtocolError::SessionNotFound(
387                            SessionNotFound::new(sender, "decrypt"),
388                        )
389                    })?;
390
391                strip_padding_version(
392                    session_record.session_version()?,
393                    &mut data,
394                )?;
395                Plaintext { metadata, data }
396            },
397            Type::UnidentifiedSender => {
398                let SealedSenderDecryptionResult {
399                    sender_uuid,
400                    sender_e164: _,
401                    device_id,
402                    mut message,
403                } = sealed_sender_decrypt(
404                    ciphertext,
405                    &self.trust_roots,
406                    Timestamp::from_epoch_millis(envelope.timestamp()),
407                    None,
408                    self.local_address.clone(),
409                    &mut self.protocol_store.clone(),
410                    &mut self.protocol_store.clone(),
411                    &mut self.protocol_store.clone(),
412                    &mut self.protocol_store.clone(),
413                    &mut self.protocol_store.clone(),
414                    &mut self.protocol_store,
415                )
416                .await?;
417
418                let Some(sender) =
419                    ServiceId::parse_from_service_id_string(&sender_uuid)
420                else {
421                    return Err(
422                        SignalProtocolError::InvalidSealedSenderMessage(
423                            "invalid sender UUID".to_string(),
424                        )
425                        .into(),
426                    );
427                };
428
429                let needs_receipt = if source_service_id.is_some() {
430                    tracing::warn!(?envelope, "Received an unidentified delivery over an identified channel.  Marking needs_receipt=false");
431                    false
432                } else {
433                    true
434                };
435
436                if sender.kind() == ServiceIdKind::Pni {
437                    tracing::warn!(
438                        "sealed sender used for PNI; ignoring invalid message"
439                    );
440                    return Err(ServiceError::InvalidFrame {
441                        reason: "sealed sender used for PNI",
442                    });
443                }
444
445                let metadata = Metadata {
446                    destination: envelope
447                        .parse_destination_service_id()
448                        .expect("unidentified sender envelope format"),
449                    sender,
450                    sender_device: device_id,
451                    timestamp: timestamp?,
452                    server_timestamp: server_timestamp?,
453                    unidentified_sender: true,
454                    needs_receipt,
455                    was_plaintext: false,
456
457                    server_guid,
458                };
459
460                strip_padding(&mut message)?;
461
462                Plaintext {
463                    metadata,
464                    data: message,
465                }
466            },
467            _ => {
468                // else
469                return Err(ServiceError::InvalidFrame {
470                    reason: "envelope has unknown type",
471                });
472            },
473        };
474        Ok(plaintext)
475    }
476
477    #[tracing::instrument(
478        skip(address, unidentified_access, content, csprng),
479        fields(
480            address = %address,
481            with_unidentified_access = unidentified_access.is_some(),
482            content_length = content.len(),
483        )
484    )]
485    pub(crate) async fn encrypt<R: Rng + CryptoRng>(
486        &mut self,
487        address: &ProtocolAddress,
488        unidentified_access: Option<&SenderCertificate>,
489        content: &[u8],
490        csprng: &mut R,
491    ) -> Result<OutgoingPushMessage, ServiceError> {
492        let mut rng = rng();
493
494        let session_record = self
495            .protocol_store
496            .load_session(address)
497            .await?
498            .ok_or_else(|| {
499            SignalProtocolError::SessionNotFound(SessionNotFound::new(
500                address.clone(),
501                "encrypt",
502            ))
503        })?;
504
505        let record_usable = session_record
506            .has_usable_sender_chain(
507                SystemTime::now(),
508                SessionUsabilityRequirements::NotStale,
509            )
510            .unwrap_or(false);
511        if !record_usable {
512            Err(SignalProtocolError::SessionNotFound(SessionNotFound::new(
513                address.clone(),
514                "encrypt",
515            )))?;
516        }
517
518        let padded_content =
519            add_padding(session_record.session_version()?, content)?;
520
521        if let Some(unindentified_access) = unidentified_access {
522            let destination_registration_id =
523                session_record.remote_registration_id()?;
524
525            let message = sealed_sender_encrypt(
526                address,
527                unindentified_access,
528                &padded_content,
529                &mut self.protocol_store.clone(),
530                &mut self.protocol_store,
531                SystemTime::now(),
532                csprng,
533            )
534            .await?;
535
536            use crate::proto::envelope::Type;
537            Ok(OutgoingPushMessage {
538                r#type: Type::UnidentifiedSender as u32,
539                destination_device_id: address.device_id(),
540                destination_registration_id,
541                content: BASE64_RELAXED.encode(message),
542            })
543        } else {
544            let message = message_encrypt(
545                &padded_content,
546                address,
547                &self.local_address,
548                &mut self.protocol_store.clone(),
549                &mut self.protocol_store.clone(),
550                SystemTime::now(),
551                &mut rng,
552            )
553            .await?;
554
555            let destination_registration_id =
556                session_record.remote_registration_id()?;
557
558            let body = BASE64_RELAXED.encode(message.serialize());
559
560            use crate::proto::envelope::Type;
561            let message_type = match message.message_type() {
562                CiphertextMessageType::PreKey => Type::PrekeyBundle,
563                CiphertextMessageType::Whisper => Type::Ciphertext,
564                t => panic!("Bad type: {:?}", t),
565            } as u32;
566            Ok(OutgoingPushMessage {
567                r#type: message_type,
568                destination_device_id: address.device_id(),
569                destination_registration_id,
570                content: body,
571            })
572        }
573    }
574}
575
576struct Plaintext {
577    metadata: Metadata,
578    data: Vec<u8>,
579}
580
581#[expect(clippy::comparison_chain)]
582fn add_padding(version: u32, contents: &[u8]) -> Result<Vec<u8>, ServiceError> {
583    if version < 2 {
584        Err(ServiceError::PaddingVersion(version))
585    } else if version == 2 {
586        Ok(contents.to_vec())
587    } else {
588        let message_length = contents.len();
589        let message_length_with_terminator = contents.len() + 1;
590        let mut message_part_count = message_length_with_terminator / 160;
591        if !message_length_with_terminator.is_multiple_of(160) {
592            message_part_count += 1;
593        }
594
595        let message_length_with_padding = message_part_count * 160;
596
597        let mut buffer = vec![0u8; message_length_with_padding];
598        buffer[..message_length].copy_from_slice(contents);
599        Iso7816::raw_pad(&mut buffer, message_length);
600        Ok(buffer)
601    }
602}
603
604#[expect(clippy::comparison_chain)]
605fn strip_padding_version(
606    version: u32,
607    contents: &mut Vec<u8>,
608) -> Result<(), ServiceError> {
609    if version < 2 {
610        Err(ServiceError::InvalidFrame {
611            reason: "unknown version",
612        })
613    } else if version == 2 {
614        Ok(())
615    } else {
616        strip_padding(contents)?;
617        Ok(())
618    }
619}
620
621fn strip_padding(contents: &mut Vec<u8>) -> Result<(), ServiceError> {
622    let new_length = Iso7816::raw_unpad(contents)?.len();
623    contents.resize(new_length, 0);
624    Ok(())
625}
626
627/// Equivalent of `SignalServiceCipher::getPreferredProtocolAddress`
628pub async fn get_preferred_protocol_address<S: SessionStore>(
629    session_store: &S,
630    address: &ServiceId,
631    device_id: DeviceId,
632) -> Result<ProtocolAddress, libsignal_protocol::error::SignalProtocolError> {
633    let address = address.to_protocol_address(device_id);
634    if session_store.load_session(&address).await?.is_some() {
635        return Ok(address);
636    }
637
638    Ok(address)
639}
640
641/// Error thrown when the sealed sending decryption fails.
642///
643/// The USMC sender field is only populated when the USMC could be validated against the trust roots;
644/// hence the sender information can be trusted, give or take an active attacker on the Signal
645/// side.
646#[derive(thiserror::Error)]
647#[error("error: {inner}, usmc: {}", sender.is_some())]
648pub struct SealedSenderDecryptionError {
649    pub inner: SignalProtocolError,
650    pub sender: Option<ProtocolAddress>,
651}
652
653impl fmt::Debug for SealedSenderDecryptionError {
654    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
655        f.debug_struct("SealedSenderDecryptionError")
656            .field("inner", &self.inner)
657            .field("sender", &self.sender)
658            .finish()
659    }
660}
661
662impl From<SignalProtocolError> for SealedSenderDecryptionError {
663    fn from(e: SignalProtocolError) -> Self {
664        SealedSenderDecryptionError {
665            inner: e,
666            sender: None,
667        }
668    }
669}
670
671/// Decrypt a Sealed Sender message `ciphertext` in either the v1 or v2 format, validate its sender
672/// certificate, and then decrypt the inner message payload.
673///
674/// This method calls [`sealed_sender_decrypt_to_usmc`] to extract the sender information, including
675/// the embedded [`SenderCertificate`]. The sender certificate (signed by the [`ServerCertificate`])
676/// is then validated against the `trust_root` baked into the client to ensure that the sender's
677/// identity was not forged.
678#[allow(clippy::too_many_arguments)]
679#[tracing::instrument(
680    skip(
681        ciphertext,
682        trust_roots,
683        identity_store,
684        session_store,
685        pre_key_store,
686        signed_pre_key_store,
687        sender_key_store,
688        kyber_pre_key_store
689    ),
690    fields(
691        ciphertext = ciphertext.len(),
692    )
693)]
694async fn sealed_sender_decrypt(
695    ciphertext: &[u8],
696    trust_roots: &[PublicKey],
697    timestamp: Timestamp,
698    local_e164: Option<String>,
699    local_address: ProtocolAddress,
700    identity_store: &mut dyn IdentityKeyStore,
701    session_store: &mut dyn SessionStore,
702    pre_key_store: &mut dyn PreKeyStore,
703    signed_pre_key_store: &mut dyn SignedPreKeyStore,
704    sender_key_store: &mut dyn SenderKeyStore,
705    kyber_pre_key_store: &mut dyn KyberPreKeyStore,
706) -> Result<SealedSenderDecryptionResult, SealedSenderDecryptionError> {
707    let usmc =
708        sealed_sender_decrypt_to_usmc(ciphertext, identity_store).await?;
709
710    if !usmc
711        .sender()?
712        .validate_with_trust_roots(trust_roots, timestamp)?
713    {
714        return Err(SignalProtocolError::InvalidSealedSenderMessage(
715            "trust root validation failed".to_string(),
716        )
717        .into());
718    }
719
720    let local_service_id =
721        ServiceId::parse_from_service_id_string(local_address.name())
722            .expect("valid protocol address name");
723    let is_local_uuid = local_service_id.raw_uuid()
724        == usmc
725            .sender()?
726            .sender_uuid()?
727            .parse::<Uuid>()
728            // Validity checked inside certificate checker
729            .expect("valid uuid");
730
731    let is_local_e164 = match (local_e164, usmc.sender()?.sender_e164()?) {
732        (Some(l), Some(s)) => l == s,
733        (_, _) => false,
734    };
735
736    if (is_local_e164 || is_local_uuid)
737        && usmc.sender()?.sender_device_id()? == local_address.device_id()
738    {
739        return Err(SignalProtocolError::SealedSenderSelfSend.into());
740    }
741
742    let remote_address = ProtocolAddress::new(
743        usmc.sender()?.sender_uuid()?.to_string(),
744        usmc.sender()?.sender_device_id()?,
745    );
746
747    sealed_sender_decrypt_with_validated_usmc(
748        &usmc,
749        &remote_address,
750        &local_address,
751        identity_store,
752        session_store,
753        pre_key_store,
754        signed_pre_key_store,
755        sender_key_store,
756        kyber_pre_key_store,
757    )
758    .await
759    .map_err(|inner| SealedSenderDecryptionError {
760        inner,
761        sender: Some(remote_address),
762    })
763}
764
765#[allow(clippy::too_many_arguments)]
766async fn sealed_sender_decrypt_with_validated_usmc(
767    usmc: &UnidentifiedSenderMessageContent,
768    remote_address: &ProtocolAddress,
769    local_address: &ProtocolAddress,
770    identity_store: &mut dyn IdentityKeyStore,
771    session_store: &mut dyn SessionStore,
772    pre_key_store: &mut dyn PreKeyStore,
773    signed_pre_key_store: &mut dyn SignedPreKeyStore,
774    sender_key_store: &mut dyn SenderKeyStore,
775    kyber_pre_key_store: &mut dyn KyberPreKeyStore,
776) -> Result<SealedSenderDecryptionResult, SignalProtocolError> {
777    let mut rng = rng();
778
779    let message = match usmc.msg_type()? {
780        CiphertextMessageType::Whisper => {
781            let ctext = SignalMessage::try_from(usmc.contents()?)?;
782            message_decrypt_signal(
783                &ctext,
784                remote_address,
785                local_address,
786                session_store,
787                identity_store,
788                &mut rng,
789            )
790            .await?
791        },
792        CiphertextMessageType::PreKey => {
793            let ctext = PreKeySignalMessage::try_from(usmc.contents()?)?;
794            message_decrypt_prekey(
795                &ctext,
796                remote_address,
797                local_address,
798                session_store,
799                identity_store,
800                pre_key_store,
801                signed_pre_key_store,
802                kyber_pre_key_store,
803                &mut rng,
804            )
805            .await?
806        },
807        CiphertextMessageType::SenderKey => {
808            group_decrypt(usmc.contents()?, sender_key_store, remote_address)
809                .await?
810        },
811        CiphertextMessageType::Plaintext => {
812            // Sealed sender envelope wrapping a PlaintextContent.
813            // Should contain a DecryptionErrorMessage.
814            let plaintext_content =
815                PlaintextContent::try_from(usmc.contents()?)?;
816            plaintext_content.body().to_vec()
817        },
818    };
819
820    Ok(SealedSenderDecryptionResult {
821        sender_uuid: usmc.sender()?.sender_uuid()?.to_string(),
822        sender_e164: usmc.sender()?.sender_e164()?.map(|s| s.to_string()),
823        device_id: usmc.sender()?.sender_device_id()?,
824        message,
825    })
826}