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