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 SignalMessage, SignalProtocolError, SignedPreKeyStore, Timestamp,
15 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 Plaintext {
331 metadata,
332 data: ciphertext.clone(),
333 }
334 },
335 Type::Ciphertext => {
336 let sender = get_preferred_protocol_address(
337 &self.protocol_store,
338 &envelope
339 .parse_source_service_id()
340 .expect("ciphertext envelope format"),
341 envelope.source_device().try_into()?,
342 )
343 .await?;
344 let metadata = Metadata {
345 destination: envelope
346 .parse_destination_service_id()
347 .expect("ciphertext envelope format"),
348 sender: envelope
349 .parse_source_service_id()
350 .expect("ciphertext envelope format"),
351 sender_device: envelope.source_device().try_into()?,
352 timestamp: timestamp?,
353 server_timestamp: server_timestamp?,
354 needs_receipt: false,
355 unidentified_sender: false,
356 was_plaintext: false,
357
358 server_guid,
359 };
360
361 let mut data = message_decrypt_signal(
362 &SignalMessage::try_from(&ciphertext[..])?,
363 &sender,
364 &mut self.protocol_store.clone(),
365 &mut self.protocol_store.clone(),
366 csprng,
367 )
368 .await?
369 .as_slice()
370 .to_vec();
371
372 let session_record = self
373 .protocol_store
374 .load_session(&sender)
375 .await?
376 .ok_or(SignalProtocolError::SessionNotFound(sender))?;
377
378 strip_padding_version(
379 session_record.session_version()?,
380 &mut data,
381 )?;
382 Plaintext { metadata, data }
383 },
384 Type::UnidentifiedSender => {
385 let SealedSenderDecryptionResult {
386 sender_uuid,
387 sender_e164: _,
388 device_id,
389 mut message,
390 } = sealed_sender_decrypt(
391 ciphertext,
392 &self.trust_roots,
393 Timestamp::from_epoch_millis(envelope.timestamp()),
394 None,
395 self.local_address.clone(),
396 &mut self.protocol_store.clone(),
397 &mut self.protocol_store.clone(),
398 &mut self.protocol_store.clone(),
399 &mut self.protocol_store.clone(),
400 &mut self.protocol_store.clone(),
401 &mut self.protocol_store,
402 )
403 .await?;
404
405 let Some(sender) =
406 ServiceId::parse_from_service_id_string(&sender_uuid)
407 else {
408 return Err(
409 SignalProtocolError::InvalidSealedSenderMessage(
410 "invalid sender UUID".to_string(),
411 )
412 .into(),
413 );
414 };
415
416 let needs_receipt = if envelope.source_service_id.is_some() {
417 tracing::warn!(?envelope, "Received an unidentified delivery over an identified channel. Marking needs_receipt=false");
418 false
419 } else {
420 true
421 };
422
423 if sender.kind() == ServiceIdKind::Pni {
424 tracing::warn!(
425 "sealed sender used for PNI; ignoring invalid message"
426 );
427 return Err(ServiceError::InvalidFrame {
428 reason: "sealed sender used for PNI",
429 });
430 }
431
432 let metadata = Metadata {
433 destination: envelope
434 .parse_destination_service_id()
435 .expect("unidentified sender envelope format"),
436 sender,
437 sender_device: device_id,
438 timestamp: timestamp?,
439 server_timestamp: server_timestamp?,
440 unidentified_sender: true,
441 needs_receipt,
442 was_plaintext: false,
443
444 server_guid,
445 };
446
447 strip_padding(&mut message)?;
448
449 Plaintext {
450 metadata,
451 data: message,
452 }
453 },
454 _ => {
455 return Err(ServiceError::InvalidFrame {
457 reason: "envelope has unknown type",
458 });
459 },
460 };
461 Ok(plaintext)
462 }
463
464 #[tracing::instrument(
465 skip(address, unidentified_access, content, csprng),
466 fields(
467 address = %address,
468 with_unidentified_access = unidentified_access.is_some(),
469 content_length = content.len(),
470 )
471 )]
472 pub(crate) async fn encrypt<R: Rng + CryptoRng>(
473 &mut self,
474 address: &ProtocolAddress,
475 unidentified_access: Option<&SenderCertificate>,
476 content: &[u8],
477 csprng: &mut R,
478 ) -> Result<OutgoingPushMessage, ServiceError> {
479 let mut rng = rng();
480
481 let session_record = self
482 .protocol_store
483 .load_session(address)
484 .await?
485 .ok_or_else(|| {
486 SignalProtocolError::SessionNotFound(address.clone())
487 })?;
488
489 let padded_content =
490 add_padding(session_record.session_version()?, content)?;
491
492 if let Some(unindentified_access) = unidentified_access {
493 let destination_registration_id =
494 session_record.remote_registration_id()?;
495
496 let message = sealed_sender_encrypt(
497 address,
498 unindentified_access,
499 &padded_content,
500 &mut self.protocol_store.clone(),
501 &mut self.protocol_store,
502 SystemTime::now(),
503 csprng,
504 )
505 .await?;
506
507 use crate::proto::envelope::Type;
508 Ok(OutgoingPushMessage {
509 r#type: Type::UnidentifiedSender as u32,
510 destination_device_id: address.device_id(),
511 destination_registration_id,
512 content: BASE64_RELAXED.encode(message),
513 })
514 } else {
515 let message = message_encrypt(
516 &padded_content,
517 address,
518 &self.local_address,
519 &mut self.protocol_store.clone(),
520 &mut self.protocol_store.clone(),
521 SystemTime::now(),
522 &mut rng,
523 )
524 .await?;
525
526 let destination_registration_id =
527 session_record.remote_registration_id()?;
528
529 let body = BASE64_RELAXED.encode(message.serialize());
530
531 use crate::proto::envelope::Type;
532 let message_type = match message.message_type() {
533 CiphertextMessageType::PreKey => Type::PrekeyBundle,
534 CiphertextMessageType::Whisper => Type::Ciphertext,
535 t => panic!("Bad type: {:?}", t),
536 } as u32;
537 Ok(OutgoingPushMessage {
538 r#type: message_type,
539 destination_device_id: address.device_id(),
540 destination_registration_id,
541 content: body,
542 })
543 }
544 }
545}
546
547struct Plaintext {
548 metadata: Metadata,
549 data: Vec<u8>,
550}
551
552#[expect(clippy::comparison_chain)]
553fn add_padding(version: u32, contents: &[u8]) -> Result<Vec<u8>, ServiceError> {
554 if version < 2 {
555 Err(ServiceError::PaddingVersion(version))
556 } else if version == 2 {
557 Ok(contents.to_vec())
558 } else {
559 let message_length = contents.len();
560 let message_length_with_terminator = contents.len() + 1;
561 let mut message_part_count = message_length_with_terminator / 160;
562 if !message_length_with_terminator.is_multiple_of(160) {
563 message_part_count += 1;
564 }
565
566 let message_length_with_padding = message_part_count * 160;
567
568 let mut buffer = vec![0u8; message_length_with_padding];
569 buffer[..message_length].copy_from_slice(contents);
570 Iso7816::raw_pad(&mut buffer, message_length);
571 Ok(buffer)
572 }
573}
574
575#[expect(clippy::comparison_chain)]
576fn strip_padding_version(
577 version: u32,
578 contents: &mut Vec<u8>,
579) -> Result<(), ServiceError> {
580 if version < 2 {
581 Err(ServiceError::InvalidFrame {
582 reason: "unknown version",
583 })
584 } else if version == 2 {
585 Ok(())
586 } else {
587 strip_padding(contents)?;
588 Ok(())
589 }
590}
591
592fn strip_padding(contents: &mut Vec<u8>) -> Result<(), ServiceError> {
593 let new_length = Iso7816::raw_unpad(contents)?.len();
594 contents.resize(new_length, 0);
595 Ok(())
596}
597
598pub async fn get_preferred_protocol_address<S: SessionStore>(
600 session_store: &S,
601 address: &ServiceId,
602 device_id: DeviceId,
603) -> Result<ProtocolAddress, libsignal_protocol::error::SignalProtocolError> {
604 let address = address.to_protocol_address(device_id);
605 if session_store.load_session(&address).await?.is_some() {
606 return Ok(address);
607 }
608
609 Ok(address)
610}
611
612#[derive(thiserror::Error)]
618#[error("error: {inner}, usmc: {}", sender.is_some())]
619pub struct SealedSenderDecryptionError {
620 pub inner: SignalProtocolError,
621 pub sender: Option<ProtocolAddress>,
622}
623
624impl fmt::Debug for SealedSenderDecryptionError {
625 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
626 f.debug_struct("SealedSenderDecryptionError")
627 .field("inner", &self.inner)
628 .field("sender", &self.sender)
629 .finish()
630 }
631}
632
633impl From<SignalProtocolError> for SealedSenderDecryptionError {
634 fn from(e: SignalProtocolError) -> Self {
635 SealedSenderDecryptionError {
636 inner: e,
637 sender: None,
638 }
639 }
640}
641
642#[allow(clippy::too_many_arguments)]
650#[tracing::instrument(
651 skip(
652 ciphertext,
653 trust_roots,
654 identity_store,
655 session_store,
656 pre_key_store,
657 signed_pre_key_store,
658 sender_key_store,
659 kyber_pre_key_store
660 ),
661 fields(
662 ciphertext = ciphertext.len(),
663 )
664)]
665async fn sealed_sender_decrypt(
666 ciphertext: &[u8],
667 trust_roots: &[PublicKey],
668 timestamp: Timestamp,
669 local_e164: Option<String>,
670 local_address: ProtocolAddress,
671 identity_store: &mut dyn IdentityKeyStore,
672 session_store: &mut dyn SessionStore,
673 pre_key_store: &mut dyn PreKeyStore,
674 signed_pre_key_store: &mut dyn SignedPreKeyStore,
675 sender_key_store: &mut dyn SenderKeyStore,
676 kyber_pre_key_store: &mut dyn KyberPreKeyStore,
677) -> Result<SealedSenderDecryptionResult, SealedSenderDecryptionError> {
678 let usmc =
679 sealed_sender_decrypt_to_usmc(ciphertext, identity_store).await?;
680
681 if !usmc
682 .sender()?
683 .validate_with_trust_roots(trust_roots, timestamp)?
684 {
685 return Err(SignalProtocolError::InvalidSealedSenderMessage(
686 "trust root validation failed".to_string(),
687 )
688 .into());
689 }
690
691 let local_service_id =
692 ServiceId::parse_from_service_id_string(local_address.name())
693 .expect("valid protocol address name");
694 let is_local_uuid = local_service_id.raw_uuid()
695 == usmc
696 .sender()?
697 .sender_uuid()?
698 .parse::<Uuid>()
699 .expect("valid uuid");
701
702 let is_local_e164 = match (local_e164, usmc.sender()?.sender_e164()?) {
703 (Some(l), Some(s)) => l == s,
704 (_, _) => false,
705 };
706
707 if (is_local_e164 || is_local_uuid)
708 && usmc.sender()?.sender_device_id()? == local_address.device_id()
709 {
710 return Err(SignalProtocolError::SealedSenderSelfSend.into());
711 }
712
713 let remote_address = ProtocolAddress::new(
714 usmc.sender()?.sender_uuid()?.to_string(),
715 usmc.sender()?.sender_device_id()?,
716 );
717
718 sealed_sender_decrypt_with_validated_usmc(
719 &usmc,
720 &remote_address,
721 &local_address,
722 identity_store,
723 session_store,
724 pre_key_store,
725 signed_pre_key_store,
726 sender_key_store,
727 kyber_pre_key_store,
728 )
729 .await
730 .map_err(|inner| SealedSenderDecryptionError {
731 inner,
732 sender: Some(remote_address),
733 })
734}
735
736#[allow(clippy::too_many_arguments)]
737async fn sealed_sender_decrypt_with_validated_usmc(
738 usmc: &UnidentifiedSenderMessageContent,
739 remote_address: &ProtocolAddress,
740 local_address: &ProtocolAddress,
741 identity_store: &mut dyn IdentityKeyStore,
742 session_store: &mut dyn SessionStore,
743 pre_key_store: &mut dyn PreKeyStore,
744 signed_pre_key_store: &mut dyn SignedPreKeyStore,
745 sender_key_store: &mut dyn SenderKeyStore,
746 kyber_pre_key_store: &mut dyn KyberPreKeyStore,
747) -> Result<SealedSenderDecryptionResult, SignalProtocolError> {
748 let mut rng = rng();
749
750 let message = match usmc.msg_type()? {
751 CiphertextMessageType::Whisper => {
752 let ctext = SignalMessage::try_from(usmc.contents()?)?;
753 message_decrypt_signal(
754 &ctext,
755 remote_address,
756 session_store,
757 identity_store,
758 &mut rng,
759 )
760 .await?
761 },
762 CiphertextMessageType::PreKey => {
763 let ctext = PreKeySignalMessage::try_from(usmc.contents()?)?;
764 message_decrypt_prekey(
765 &ctext,
766 remote_address,
767 local_address,
768 session_store,
769 identity_store,
770 pre_key_store,
771 signed_pre_key_store,
772 kyber_pre_key_store,
773 &mut rng,
774 )
775 .await?
776 },
777 CiphertextMessageType::SenderKey => {
778 group_decrypt(usmc.contents()?, sender_key_store, remote_address)
779 .await?
780 },
781 CiphertextMessageType::Plaintext => {
782 let plaintext_content =
785 PlaintextContent::try_from(usmc.contents()?)?;
786 plaintext_content.body().to_vec()
787 },
788 };
789
790 Ok(SealedSenderDecryptionResult {
791 sender_uuid: usmc.sender()?.sender_uuid()?.to_string(),
792 sender_e164: usmc.sender()?.sender_e164()?.map(|s| s.to_string()),
793 device_id: usmc.sender()?.sender_device_id()?,
794 message,
795 })
796}