Skip to main content

libsignal_service/
cipher.rs

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