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