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#[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 #[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 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 #[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 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 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 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 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
612pub 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#[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#[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 .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 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}