1use std::{collections::BTreeMap, fmt, ops::Not, sync::Arc};
16
17use ruma::{
18 DeviceKeyAlgorithm, EventId, MilliSecondsSinceUnixEpoch, OwnedDeviceId, OwnedEventId,
19 OwnedUserId,
20 events::{
21 AnySyncMessageLikeEvent, AnySyncTimelineEvent, AnyTimelineEvent, AnyToDeviceEvent,
22 MessageLikeEventType, room::encrypted::EncryptedEventScheme,
23 },
24 push::Action,
25 serde::{
26 AsRefStr, AsStrAsRefStr, DebugAsRefStr, DeserializeFromCowStr, FromString, JsonObject, Raw,
27 SerializeAsRefStr,
28 },
29};
30use serde::{Deserialize, Serialize};
31use tracing::warn;
32#[cfg(target_family = "wasm")]
33use wasm_bindgen::prelude::*;
34
35use crate::{
36 debug::{DebugRawEvent, DebugStructExt},
37 serde_helpers::{extract_bundled_thread, extract_is_thread_root, extract_timestamp},
38};
39
40const AUTHENTICITY_NOT_GUARANTEED: &str =
41 "The authenticity of this encrypted message can't be guaranteed on this device.";
42const UNVERIFIED_IDENTITY: &str = "Encrypted by an unverified user.";
43const VERIFICATION_VIOLATION: &str =
44 "Encrypted by a previously-verified user who is no longer verified.";
45const UNSIGNED_DEVICE: &str = "Encrypted by a device not verified by its owner.";
46const UNKNOWN_DEVICE: &str = "Encrypted by an unknown or deleted device.";
47const MISMATCHED_SENDER: &str = "\
48 The sender of the event does not match the owner of the device \
49 that created the Megolm session.";
50
51#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
54#[serde(from = "OldVerificationStateHelper")]
55pub enum VerificationState {
56 Verified,
61
62 Unverified(VerificationLevel),
67}
68
69#[derive(Clone, Debug, Deserialize)]
72enum OldVerificationStateHelper {
73 Untrusted,
74 UnknownDevice,
75 #[serde(alias = "Trusted")]
76 Verified,
77 Unverified(VerificationLevel),
78}
79
80impl From<OldVerificationStateHelper> for VerificationState {
81 fn from(value: OldVerificationStateHelper) -> Self {
82 match value {
83 OldVerificationStateHelper::Untrusted => {
86 VerificationState::Unverified(VerificationLevel::UnsignedDevice)
87 }
88 OldVerificationStateHelper::UnknownDevice => {
89 Self::Unverified(VerificationLevel::None(DeviceLinkProblem::MissingDevice))
90 }
91 OldVerificationStateHelper::Verified => Self::Verified,
92 OldVerificationStateHelper::Unverified(l) => Self::Unverified(l),
93 }
94 }
95}
96
97impl VerificationState {
98 pub fn to_shield_state_strict(&self) -> ShieldState {
105 match self {
106 VerificationState::Verified => ShieldState::None,
107 VerificationState::Unverified(level) => match level {
108 VerificationLevel::UnverifiedIdentity
109 | VerificationLevel::VerificationViolation
110 | VerificationLevel::UnsignedDevice => ShieldState::Red {
111 code: ShieldStateCode::UnverifiedIdentity,
112 message: UNVERIFIED_IDENTITY,
113 },
114 VerificationLevel::None(link) => match link {
115 DeviceLinkProblem::MissingDevice => ShieldState::Red {
116 code: ShieldStateCode::UnknownDevice,
117 message: UNKNOWN_DEVICE,
118 },
119 DeviceLinkProblem::InsecureSource => ShieldState::Red {
120 code: ShieldStateCode::AuthenticityNotGuaranteed,
121 message: AUTHENTICITY_NOT_GUARANTEED,
122 },
123 },
124 VerificationLevel::MismatchedSender => ShieldState::Red {
125 code: ShieldStateCode::MismatchedSender,
126 message: MISMATCHED_SENDER,
127 },
128 },
129 }
130 }
131
132 pub fn to_shield_state_lax(&self) -> ShieldState {
140 match self {
141 VerificationState::Verified => ShieldState::None,
142 VerificationState::Unverified(level) => match level {
143 VerificationLevel::UnverifiedIdentity => {
144 ShieldState::None
147 }
148 VerificationLevel::VerificationViolation => {
149 ShieldState::Red {
152 code: ShieldStateCode::VerificationViolation,
153 message: VERIFICATION_VIOLATION,
154 }
155 }
156 VerificationLevel::UnsignedDevice => {
157 ShieldState::Red {
160 code: ShieldStateCode::UnsignedDevice,
161 message: UNSIGNED_DEVICE,
162 }
163 }
164 VerificationLevel::None(link) => match link {
165 DeviceLinkProblem::MissingDevice => {
166 ShieldState::Red {
171 code: ShieldStateCode::UnknownDevice,
172 message: UNKNOWN_DEVICE,
173 }
174 }
175 DeviceLinkProblem::InsecureSource => {
176 ShieldState::Grey {
180 code: ShieldStateCode::AuthenticityNotGuaranteed,
181 message: AUTHENTICITY_NOT_GUARANTEED,
182 }
183 }
184 },
185 VerificationLevel::MismatchedSender => ShieldState::Red {
186 code: ShieldStateCode::MismatchedSender,
187 message: MISMATCHED_SENDER,
188 },
189 },
190 }
191 }
192}
193
194#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
197pub enum VerificationLevel {
198 UnverifiedIdentity,
200
201 #[serde(alias = "PreviouslyVerified")]
204 VerificationViolation,
205
206 UnsignedDevice,
209
210 None(DeviceLinkProblem),
216
217 MismatchedSender,
220}
221
222impl fmt::Display for VerificationLevel {
223 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
224 let display = match self {
225 VerificationLevel::UnverifiedIdentity => "The sender's identity was not verified",
226 VerificationLevel::VerificationViolation => {
227 "The sender's identity was previously verified but has changed"
228 }
229 VerificationLevel::UnsignedDevice => {
230 "The sending device was not signed by the user's identity"
231 }
232 VerificationLevel::None(..) => "The sending device is not known",
233 VerificationLevel::MismatchedSender => MISMATCHED_SENDER,
234 };
235 write!(f, "{display}")
236 }
237}
238
239#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
242pub enum DeviceLinkProblem {
243 MissingDevice,
246 InsecureSource,
249}
250
251#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq)]
254pub enum ShieldState {
255 Red {
258 code: ShieldStateCode,
260 message: &'static str,
262 },
263 Grey {
266 code: ShieldStateCode,
268 message: &'static str,
270 },
271 None,
273}
274
275#[derive(Clone, Copy, Debug, Deserialize, Serialize, Eq, PartialEq)]
277#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
278#[cfg_attr(target_family = "wasm", wasm_bindgen)]
279pub enum ShieldStateCode {
280 AuthenticityNotGuaranteed,
282 UnknownDevice,
284 UnsignedDevice,
286 UnverifiedIdentity,
288 #[serde(alias = "PreviouslyVerified")]
290 VerificationViolation,
291 MismatchedSender,
294}
295
296#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
298pub enum AlgorithmInfo {
299 MegolmV1AesSha2 {
301 curve25519_key: String,
304 sender_claimed_keys: BTreeMap<DeviceKeyAlgorithm, String>,
308
309 #[serde(default, skip_serializing_if = "Option::is_none")]
312 session_id: Option<String>,
313 },
314
315 OlmV1Curve25519AesSha2 {
317 curve25519_public_key_base64: String,
319 },
320}
321
322#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
325pub struct ForwarderInfo {
326 pub user_id: OwnedUserId,
328 pub device_id: OwnedDeviceId,
330}
331
332#[derive(Clone, Debug, PartialEq, Serialize)]
334pub struct EncryptionInfo {
335 pub sender: OwnedUserId,
338 pub sender_device: Option<OwnedDeviceId>,
341 pub forwarder: Option<ForwarderInfo>,
346 pub algorithm_info: AlgorithmInfo,
348 pub verification_state: VerificationState,
355}
356
357impl EncryptionInfo {
358 pub fn session_id(&self) -> Option<&str> {
360 if let AlgorithmInfo::MegolmV1AesSha2 { session_id, .. } = &self.algorithm_info {
361 session_id.as_deref()
362 } else {
363 None
364 }
365 }
366}
367
368impl<'de> Deserialize<'de> for EncryptionInfo {
369 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
370 where
371 D: serde::Deserializer<'de>,
372 {
373 #[derive(Deserialize)]
376 struct Helper {
377 pub sender: OwnedUserId,
378 pub sender_device: Option<OwnedDeviceId>,
379 pub forwarder: Option<ForwarderInfo>,
380 pub algorithm_info: AlgorithmInfo,
381 pub verification_state: VerificationState,
382 #[serde(rename = "session_id")]
383 pub old_session_id: Option<String>,
384 }
385
386 let Helper {
387 sender,
388 sender_device,
389 forwarder,
390 algorithm_info,
391 verification_state,
392 old_session_id,
393 } = Helper::deserialize(deserializer)?;
394
395 let algorithm_info = match algorithm_info {
396 AlgorithmInfo::MegolmV1AesSha2 { curve25519_key, sender_claimed_keys, session_id } => {
397 AlgorithmInfo::MegolmV1AesSha2 {
398 session_id: session_id.or(old_session_id),
400 curve25519_key,
401 sender_claimed_keys,
402 }
403 }
404 other => other,
405 };
406
407 Ok(EncryptionInfo { sender, sender_device, forwarder, algorithm_info, verification_state })
408 }
409}
410
411#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
422pub struct ThreadSummary {
423 #[serde(skip_serializing_if = "Option::is_none")]
425 pub latest_reply: Option<OwnedEventId>,
426
427 pub num_replies: u32,
433}
434
435impl ThreadSummary {
436 pub fn new<N>(latest_reply: Option<OwnedEventId>, num_replies: N) -> Self
440 where
441 N: TryInto<u32>,
442 {
443 Self { latest_reply, num_replies: num_replies.try_into().unwrap_or(u32::MAX) }
444 }
445}
446
447#[derive(Clone, Debug, Serialize)]
470pub struct TimelineEvent {
471 #[serde(skip)]
479 event_id: Option<OwnedEventId>,
480
481 pub kind: TimelineEventKind,
483
484 pub timestamp: Option<MilliSecondsSinceUnixEpoch>,
490
491 #[serde(skip_serializing_if = "skip_serialize_push_actions")]
496 push_actions: Option<Vec<Action>>,
497}
498
499fn skip_serialize_push_actions(push_actions: &Option<Vec<Action>>) -> bool {
501 push_actions.as_ref().is_none_or(|v| v.is_empty())
502}
503
504#[cfg(not(feature = "test-send-sync"))]
506unsafe impl Send for TimelineEvent {}
507
508#[cfg(not(feature = "test-send-sync"))]
510unsafe impl Sync for TimelineEvent {}
511
512#[cfg(feature = "test-send-sync")]
513#[test]
514fn test_send_sync_for_sync_timeline_event() {
516 fn assert_send_sync<T: crate::SendOutsideWasm + crate::SyncOutsideWasm>() {}
517
518 assert_send_sync::<TimelineEvent>();
519}
520
521impl TimelineEvent {
522 pub fn from_plaintext(event: Raw<AnySyncTimelineEvent>) -> Self {
527 Self::from_plaintext_with_max_timestamp(event, MilliSecondsSinceUnixEpoch::now())
528 }
529
530 pub fn from_plaintext_with_max_timestamp(
532 event: Raw<AnySyncTimelineEvent>,
533 max_timestamp: MilliSecondsSinceUnixEpoch,
534 ) -> Self {
535 Self::new(TimelineEventKind::PlainText { event }, None, max_timestamp)
536 }
537
538 pub fn from_decrypted(
540 decrypted: DecryptedRoomEvent,
541 push_actions: Option<Vec<Action>>,
542 ) -> Self {
543 Self::from_decrypted_with_max_timestamp(
544 decrypted,
545 push_actions,
546 MilliSecondsSinceUnixEpoch::now(),
547 )
548 }
549
550 pub fn from_decrypted_with_max_timestamp(
552 decrypted: DecryptedRoomEvent,
553 push_actions: Option<Vec<Action>>,
554 max_timestamp: MilliSecondsSinceUnixEpoch,
555 ) -> Self {
556 Self::new(TimelineEventKind::Decrypted(decrypted), push_actions, max_timestamp)
557 }
558
559 pub fn from_utd(event: Raw<AnySyncTimelineEvent>, utd_info: UnableToDecryptInfo) -> Self {
562 Self::from_utd_with_max_timestamp(event, utd_info, MilliSecondsSinceUnixEpoch::now())
563 }
564
565 pub fn from_utd_with_max_timestamp(
567 event: Raw<AnySyncTimelineEvent>,
568 utd_info: UnableToDecryptInfo,
569 max_timestamp: MilliSecondsSinceUnixEpoch,
570 ) -> Self {
571 Self::new(TimelineEventKind::UnableToDecrypt { event, utd_info }, None, max_timestamp)
572 }
573
574 fn new(
579 kind: TimelineEventKind,
580 push_actions: Option<Vec<Action>>,
581 max_timestamp: MilliSecondsSinceUnixEpoch,
582 ) -> Self {
583 let raw = kind.raw();
584
585 let timestamp = extract_timestamp(raw, max_timestamp);
586
587 Self { event_id: kind.parse_event_id(), kind, push_actions, timestamp }
588 }
589
590 pub fn to_decrypted(
598 &self,
599 decrypted: DecryptedRoomEvent,
600 push_actions: Option<Vec<Action>>,
601 ) -> Self {
602 debug_assert!(
603 matches!(self.kind, TimelineEventKind::Decrypted(_)).not(),
604 "`TimelineEvent::to_decrypted` has been called on an already decrypted `TimelineEvent`."
605 );
606
607 let kind = TimelineEventKind::Decrypted(decrypted);
608
609 Self {
610 event_id: kind.parse_event_id(),
614 kind,
615 timestamp: self.timestamp,
616 push_actions,
617 }
618 }
619
620 pub fn to_utd(&self, utd_info: UnableToDecryptInfo) -> Self {
628 debug_assert!(
629 matches!(self.kind, TimelineEventKind::UnableToDecrypt { .. }).not(),
630 "`TimelineEvent::to_utd` has been called on an already UTD `TimelineEvent`."
631 );
632
633 Self {
634 event_id: self.event_id.clone(),
635 kind: TimelineEventKind::UnableToDecrypt { event: self.raw().clone(), utd_info },
636 timestamp: self.timestamp,
637 push_actions: None,
638 }
639 }
640
641 fn from_bundled_latest_event(
644 kind: &TimelineEventKind,
645 latest_event: Raw<AnySyncMessageLikeEvent>,
646 max_timestamp: MilliSecondsSinceUnixEpoch,
647 ) -> Option<Self> {
648 match kind {
649 TimelineEventKind::Decrypted(decrypted) => {
650 if let Some(unsigned_decryption_result) =
651 decrypted.unsigned_encryption_info.as_ref().and_then(|unsigned_map| {
652 unsigned_map.get(&UnsignedEventLocation::RelationsThreadLatestEvent)
653 })
654 {
655 match unsigned_decryption_result {
656 UnsignedDecryptionResult::Decrypted(encryption_info) => {
657 return Some(TimelineEvent::from_decrypted_with_max_timestamp(
660 DecryptedRoomEvent {
661 event: latest_event.cast_unchecked(),
664 encryption_info: encryption_info.clone(),
665 unsigned_encryption_info: None,
670 },
671 None,
672 max_timestamp,
673 ));
674 }
675
676 UnsignedDecryptionResult::UnableToDecrypt(utd_info) => {
677 return Some(TimelineEvent::from_utd_with_max_timestamp(
680 latest_event.cast(),
681 utd_info.clone(),
682 max_timestamp,
683 ));
684 }
685 }
686 }
687 }
688
689 TimelineEventKind::UnableToDecrypt { .. } | TimelineEventKind::PlainText { .. } => {
690 }
692 }
693
694 match latest_event.get_field::<MessageLikeEventType>("type") {
695 Ok(None) => {
696 let event_id = latest_event.get_field::<OwnedEventId>("event_id").ok().flatten();
697 warn!(
698 ?event_id,
699 "couldn't deserialize bundled latest thread event: missing `type` field \
700 in bundled latest thread event"
701 );
702 None
703 }
704
705 Ok(Some(MessageLikeEventType::RoomEncrypted)) => {
706 let session_id = if let Some(content) =
710 latest_event.get_field::<EncryptedEventScheme>("content").ok().flatten()
711 {
712 match content {
713 EncryptedEventScheme::MegolmV1AesSha2(content) => Some(content.session_id),
714 _ => None,
715 }
716 } else {
717 None
718 };
719
720 Some(TimelineEvent::from_utd_with_max_timestamp(
721 latest_event.cast(),
722 UnableToDecryptInfo { session_id, reason: UnableToDecryptReason::Unknown },
723 max_timestamp,
724 ))
725 }
726
727 Ok(_) => Some(TimelineEvent::from_plaintext_with_max_timestamp(
728 latest_event.cast(),
729 max_timestamp,
730 )),
731
732 Err(err) => {
733 let event_id = latest_event.get_field::<OwnedEventId>("event_id").ok().flatten();
734 warn!(?event_id, "couldn't deserialize bundled latest thread event's type: {err}");
735 None
736 }
737 }
738 }
739
740 pub fn push_actions(&self) -> Option<&[Action]> {
745 self.push_actions.as_deref()
746 }
747
748 pub fn set_push_actions(&mut self, push_actions: Vec<Action>) {
750 self.push_actions = Some(push_actions);
751 }
752
753 pub fn event_id(&self) -> Option<&EventId> {
756 self.event_id.as_deref()
757 }
758
759 pub fn sender(&self) -> Option<OwnedUserId> {
761 self.kind.parse_sender()
762 }
763
764 pub fn raw(&self) -> &Raw<AnySyncTimelineEvent> {
767 self.kind.raw()
768 }
769
770 pub fn replace_raw(&mut self, replacement: Raw<AnyTimelineEvent>) {
772 match &mut self.kind {
773 TimelineEventKind::Decrypted(decrypted) => decrypted.event = replacement,
774 TimelineEventKind::UnableToDecrypt { event, .. }
775 | TimelineEventKind::PlainText { event } => {
776 *event = replacement.cast();
780 }
781 }
782
783 self.event_id = self.kind.parse_event_id();
784 }
785
786 pub fn timestamp(&self) -> Option<MilliSecondsSinceUnixEpoch> {
795 self.timestamp.or_else(|| {
796 warn!("`TimelineEvent::timestamp` is parsing the raw event to extract the `timestamp`");
797
798 extract_timestamp(self.raw(), MilliSecondsSinceUnixEpoch::now())
799 })
800 }
801
802 pub fn timestamp_raw(&self) -> Option<MilliSecondsSinceUnixEpoch> {
804 self.timestamp
805 }
806
807 pub fn encryption_info(&self) -> Option<&Arc<EncryptionInfo>> {
810 self.kind.encryption_info()
811 }
812
813 pub fn into_raw(self) -> Raw<AnySyncTimelineEvent> {
816 self.kind.into_raw()
817 }
818
819 pub fn is_thread_root(&self) -> bool {
822 extract_is_thread_root(self.raw())
823 }
824
825 pub fn thread_summary(&self) -> Option<ThreadSummary> {
832 extract_bundled_thread(self.raw()).map(|bundled_thread| {
833 ThreadSummary::new(
834 bundled_thread.latest_event.get_field::<OwnedEventId>("event_id").ok().flatten(),
835 bundled_thread.count,
836 )
837 })
838 }
839
840 pub fn bundled_latest_thread_event(&self) -> Option<Self> {
850 let bundled_thread = extract_bundled_thread(self.raw())?;
851
852 Self::from_bundled_latest_event(
853 &self.kind,
854 bundled_thread.latest_event,
855 self.timestamp_raw().unwrap_or_else(MilliSecondsSinceUnixEpoch::now),
856 )
857 }
858
859 pub fn thread_summary_with_latest_event(&self) -> Option<(ThreadSummary, Self)> {
868 extract_bundled_thread(self.raw()).and_then(|bundled_thread| {
869 Some((
870 ThreadSummary::new(
871 bundled_thread
872 .latest_event
873 .get_field::<OwnedEventId>("event_id")
874 .ok()
875 .flatten(),
876 bundled_thread.count,
877 ),
878 Self::from_bundled_latest_event(
879 &self.kind,
880 bundled_thread.latest_event,
881 self.timestamp_raw().unwrap_or_else(MilliSecondsSinceUnixEpoch::now),
882 )?,
883 ))
884 })
885 }
886}
887
888impl<'de> Deserialize<'de> for TimelineEvent {
889 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
898 where
899 D: serde::Deserializer<'de>,
900 {
901 use serde_json::{Map, Value};
902
903 let value = Map::<String, Value>::deserialize(deserializer)?;
905
906 if value.contains_key("event") {
908 let v0: SyncTimelineEventDeserializationHelperV0 =
909 serde_json::from_value(Value::Object(value)).map_err(|e| {
910 serde::de::Error::custom(format!(
911 "Unable to deserialize V0-format TimelineEvent: {e}",
912 ))
913 })?;
914 Ok(v0.into())
915 }
916 else {
918 let v1: SyncTimelineEventDeserializationHelperV1 =
919 serde_json::from_value(Value::Object(value)).map_err(|e| {
920 serde::de::Error::custom(format!(
921 "Unable to deserialize V1-format TimelineEvent: {e}",
922 ))
923 })?;
924 Ok(v1.into())
925 }
926 }
927}
928
929#[derive(Clone, Serialize, Deserialize)]
931pub enum TimelineEventKind {
932 Decrypted(DecryptedRoomEvent),
934
935 UnableToDecrypt {
937 event: Raw<AnySyncTimelineEvent>,
941
942 utd_info: UnableToDecryptInfo,
944 },
945
946 PlainText {
948 event: Raw<AnySyncTimelineEvent>,
952 },
953}
954
955impl TimelineEventKind {
956 pub fn raw(&self) -> &Raw<AnySyncTimelineEvent> {
959 match self {
960 TimelineEventKind::Decrypted(d) => d.event.cast_ref(),
967 TimelineEventKind::UnableToDecrypt { event, .. } => event,
968 TimelineEventKind::PlainText { event } => event,
969 }
970 }
971
972 pub fn parse_event_id(&self) -> Option<OwnedEventId> {
975 self.raw().get_field::<OwnedEventId>("event_id").ok().flatten()
976 }
977
978 pub fn parse_sender(&self) -> Option<OwnedUserId> {
980 self.raw().get_field::<OwnedUserId>("sender").ok().flatten()
981 }
982
983 pub fn is_utd(&self) -> bool {
985 matches!(self, TimelineEventKind::UnableToDecrypt { .. })
986 }
987
988 pub fn encryption_info(&self) -> Option<&Arc<EncryptionInfo>> {
991 match self {
992 TimelineEventKind::Decrypted(d) => Some(&d.encryption_info),
993 TimelineEventKind::UnableToDecrypt { .. } | TimelineEventKind::PlainText { .. } => None,
994 }
995 }
996
997 pub fn unsigned_encryption_map(
1000 &self,
1001 ) -> Option<&BTreeMap<UnsignedEventLocation, UnsignedDecryptionResult>> {
1002 match self {
1003 TimelineEventKind::Decrypted(d) => d.unsigned_encryption_info.as_ref(),
1004 TimelineEventKind::UnableToDecrypt { .. } | TimelineEventKind::PlainText { .. } => None,
1005 }
1006 }
1007
1008 pub fn into_raw(self) -> Raw<AnySyncTimelineEvent> {
1011 match self {
1012 TimelineEventKind::Decrypted(d) => d.event.cast(),
1019 TimelineEventKind::UnableToDecrypt { event, .. } => event,
1020 TimelineEventKind::PlainText { event } => event,
1021 }
1022 }
1023
1024 pub fn session_id(&self) -> Option<&str> {
1027 match self {
1028 TimelineEventKind::Decrypted(decrypted_room_event) => {
1029 decrypted_room_event.encryption_info.session_id()
1030 }
1031 TimelineEventKind::UnableToDecrypt { utd_info, .. } => utd_info.session_id.as_deref(),
1032 TimelineEventKind::PlainText { .. } => None,
1033 }
1034 }
1035
1036 pub fn event_type(&self) -> Option<String> {
1041 self.raw().get_field("type").ok().flatten()
1042 }
1043}
1044
1045#[cfg(not(tarpaulin_include))]
1046impl fmt::Debug for TimelineEventKind {
1047 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1048 match &self {
1049 Self::PlainText { event } => f
1050 .debug_struct("TimelineEventKind::PlainText")
1051 .field("event", &DebugRawEvent(event))
1052 .finish(),
1053
1054 Self::UnableToDecrypt { event, utd_info } => f
1055 .debug_struct("TimelineEventKind::UnableToDecrypt")
1056 .field("event", &DebugRawEvent(event))
1057 .field("utd_info", &utd_info)
1058 .finish(),
1059
1060 Self::Decrypted(decrypted) => {
1061 f.debug_tuple("TimelineEventKind::Decrypted").field(decrypted).finish()
1062 }
1063 }
1064 }
1065}
1066
1067#[derive(Clone, Serialize, Deserialize)]
1069pub struct DecryptedRoomEvent {
1070 pub event: Raw<AnyTimelineEvent>,
1078
1079 pub encryption_info: Arc<EncryptionInfo>,
1081
1082 #[serde(skip_serializing_if = "Option::is_none")]
1086 pub unsigned_encryption_info: Option<BTreeMap<UnsignedEventLocation, UnsignedDecryptionResult>>,
1087}
1088
1089#[cfg(not(tarpaulin_include))]
1090impl fmt::Debug for DecryptedRoomEvent {
1091 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1092 let DecryptedRoomEvent { event, encryption_info, unsigned_encryption_info } = self;
1093
1094 f.debug_struct("DecryptedRoomEvent")
1095 .field("event", &DebugRawEvent(event))
1096 .field("encryption_info", encryption_info)
1097 .maybe_field("unsigned_encryption_info", unsigned_encryption_info)
1098 .finish()
1099 }
1100}
1101
1102#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
1104pub enum UnsignedEventLocation {
1105 RelationsReplace,
1108 RelationsThreadLatestEvent,
1111}
1112
1113impl UnsignedEventLocation {
1114 pub fn find_mut<'a>(&self, unsigned: &'a mut JsonObject) -> Option<&'a mut serde_json::Value> {
1121 let relations = unsigned.get_mut("m.relations")?.as_object_mut()?;
1122
1123 match self {
1124 Self::RelationsReplace => relations.get_mut("m.replace"),
1125 Self::RelationsThreadLatestEvent => {
1126 relations.get_mut("m.thread")?.as_object_mut()?.get_mut("latest_event")
1127 }
1128 }
1129 }
1130}
1131
1132#[derive(Debug, Clone, Serialize, Deserialize)]
1134pub enum UnsignedDecryptionResult {
1135 Decrypted(Arc<EncryptionInfo>),
1137 UnableToDecrypt(UnableToDecryptInfo),
1139}
1140
1141impl UnsignedDecryptionResult {
1142 pub fn encryption_info(&self) -> Option<&Arc<EncryptionInfo>> {
1145 match self {
1146 Self::Decrypted(info) => Some(info),
1147 Self::UnableToDecrypt(_) => None,
1148 }
1149 }
1150}
1151
1152#[derive(Debug, Clone, Serialize, Deserialize)]
1154pub struct UnableToDecryptInfo {
1155 #[serde(skip_serializing_if = "Option::is_none")]
1158 pub session_id: Option<String>,
1159
1160 #[serde(default = "unknown_utd_reason", deserialize_with = "deserialize_utd_reason")]
1162 pub reason: UnableToDecryptReason,
1163}
1164
1165fn unknown_utd_reason() -> UnableToDecryptReason {
1166 UnableToDecryptReason::Unknown
1167}
1168
1169pub fn deserialize_utd_reason<'de, D>(d: D) -> Result<UnableToDecryptReason, D::Error>
1172where
1173 D: serde::Deserializer<'de>,
1174{
1175 let v: serde_json::Value = Deserialize::deserialize(d)?;
1177 if v.as_str().is_some_and(|s| s == "MissingMegolmSession") {
1180 return Ok(UnableToDecryptReason::MissingMegolmSession { withheld_code: None });
1181 }
1182 serde_json::from_value::<UnableToDecryptReason>(v).map_err(serde::de::Error::custom)
1185}
1186
1187#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1189pub enum UnableToDecryptReason {
1190 #[doc(hidden)]
1193 Unknown,
1194
1195 MalformedEncryptedEvent,
1199
1200 MissingMegolmSession {
1203 withheld_code: Option<WithheldCode>,
1206 },
1207
1208 UnknownMegolmMessageIndex,
1211
1212 MegolmDecryptionFailure,
1219
1220 PayloadDeserializationFailure,
1222
1223 MismatchedIdentityKeys,
1227
1228 SenderIdentityNotTrusted(VerificationLevel),
1231
1232 #[cfg(feature = "experimental-encrypted-state-events")]
1235 StateKeyVerificationFailed,
1236}
1237
1238impl UnableToDecryptReason {
1239 pub fn is_missing_room_key(&self) -> bool {
1242 matches!(
1246 self,
1247 Self::MissingMegolmSession { withheld_code: None } | Self::UnknownMegolmMessageIndex
1248 )
1249 }
1250}
1251
1252#[derive(
1256 Clone,
1257 PartialEq,
1258 Eq,
1259 Hash,
1260 AsStrAsRefStr,
1261 AsRefStr,
1262 FromString,
1263 DebugAsRefStr,
1264 SerializeAsRefStr,
1265 DeserializeFromCowStr,
1266)]
1267pub enum WithheldCode {
1268 #[ruma_enum(rename = "m.blacklisted")]
1270 Blacklisted,
1271
1272 #[ruma_enum(rename = "m.unverified")]
1274 Unverified,
1275
1276 #[ruma_enum(rename = "m.unauthorised")]
1280 Unauthorised,
1281
1282 #[ruma_enum(rename = "m.unavailable")]
1285 Unavailable,
1286
1287 #[ruma_enum(rename = "m.no_olm")]
1290 NoOlm,
1291
1292 #[ruma_enum(rename = "m.history_not_shared", alias = "io.element.msc4268.history_not_shared")]
1297 HistoryNotShared,
1298
1299 #[doc(hidden)]
1300 _Custom(PrivOwnedStr),
1301}
1302
1303impl fmt::Display for WithheldCode {
1304 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
1305 let string = match self {
1306 WithheldCode::Blacklisted => "The sender has blocked you.",
1307 WithheldCode::Unverified => "The sender has disabled encrypting to unverified devices.",
1308 WithheldCode::Unauthorised => "You are not authorised to read the message.",
1309 WithheldCode::Unavailable => "The requested key was not found.",
1310 WithheldCode::NoOlm => "Unable to establish a secure channel.",
1311 WithheldCode::HistoryNotShared => "The sender disabled sharing encrypted history.",
1312 _ => self.as_str(),
1313 };
1314
1315 f.write_str(string)
1316 }
1317}
1318
1319#[doc(hidden)]
1323#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
1324pub struct PrivOwnedStr(pub Box<str>);
1325
1326#[cfg(not(tarpaulin_include))]
1327impl fmt::Debug for PrivOwnedStr {
1328 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1329 self.0.fmt(f)
1330 }
1331}
1332
1333#[derive(Debug, Deserialize)]
1338struct SyncTimelineEventDeserializationHelperV1 {
1339 kind: TimelineEventKind,
1341
1342 #[serde(default)]
1345 timestamp: Option<MilliSecondsSinceUnixEpoch>,
1346
1347 #[serde(default)]
1349 push_actions: Vec<Action>,
1350}
1351
1352impl From<SyncTimelineEventDeserializationHelperV1> for TimelineEvent {
1353 fn from(value: SyncTimelineEventDeserializationHelperV1) -> Self {
1354 let SyncTimelineEventDeserializationHelperV1 { kind, timestamp, push_actions } = value;
1355
1356 TimelineEvent {
1366 event_id: kind.parse_event_id(),
1367 kind,
1368 timestamp,
1369 push_actions: Some(push_actions),
1370 }
1371 }
1372}
1373
1374#[derive(Deserialize)]
1376struct SyncTimelineEventDeserializationHelperV0 {
1377 event: Raw<AnySyncTimelineEvent>,
1379
1380 encryption_info: Option<Arc<EncryptionInfo>>,
1384
1385 #[serde(default)]
1387 push_actions: Vec<Action>,
1388
1389 unsigned_encryption_info: Option<BTreeMap<UnsignedEventLocation, UnsignedDecryptionResult>>,
1393}
1394
1395impl From<SyncTimelineEventDeserializationHelperV0> for TimelineEvent {
1396 fn from(value: SyncTimelineEventDeserializationHelperV0) -> Self {
1397 let SyncTimelineEventDeserializationHelperV0 {
1398 event,
1399 encryption_info,
1400 push_actions,
1401 unsigned_encryption_info,
1402 } = value;
1403
1404 let timestamp = None;
1411
1412 let kind = match encryption_info {
1413 Some(encryption_info) => {
1414 TimelineEventKind::Decrypted(DecryptedRoomEvent {
1415 event: event.cast_unchecked(),
1421 encryption_info,
1422 unsigned_encryption_info,
1423 })
1424 }
1425
1426 None => TimelineEventKind::PlainText { event },
1427 };
1428
1429 TimelineEvent {
1430 event_id: kind.parse_event_id(),
1431 kind,
1432 timestamp,
1433 push_actions: Some(push_actions),
1434 }
1435 }
1436}
1437
1438#[derive(Debug, Clone, PartialEq)]
1440pub enum ToDeviceUnableToDecryptReason {
1441 DecryptionFailure,
1444
1445 UnverifiedSenderDevice,
1449
1450 NoOlmMachine,
1453
1454 EncryptionIsDisabled,
1456}
1457
1458#[derive(Clone, Debug)]
1460pub struct ToDeviceUnableToDecryptInfo {
1461 pub reason: ToDeviceUnableToDecryptReason,
1463}
1464
1465#[derive(Clone, Debug)]
1467pub enum ProcessedToDeviceEvent {
1468 Decrypted {
1471 raw: Raw<AnyToDeviceEvent>,
1473 encryption_info: EncryptionInfo,
1475 },
1476
1477 UnableToDecrypt {
1479 encrypted_event: Raw<AnyToDeviceEvent>,
1480 utd_info: ToDeviceUnableToDecryptInfo,
1481 },
1482
1483 PlainText(Raw<AnyToDeviceEvent>),
1485
1486 Invalid(Raw<AnyToDeviceEvent>),
1489}
1490
1491impl ProcessedToDeviceEvent {
1492 pub fn to_raw(&self) -> Raw<AnyToDeviceEvent> {
1495 match self {
1496 ProcessedToDeviceEvent::Decrypted { raw, .. } => raw.clone(),
1497 ProcessedToDeviceEvent::UnableToDecrypt { encrypted_event, .. } => {
1498 encrypted_event.clone()
1499 }
1500 ProcessedToDeviceEvent::PlainText(event) => event.clone(),
1501 ProcessedToDeviceEvent::Invalid(event) => event.clone(),
1502 }
1503 }
1504
1505 pub fn as_raw(&self) -> &Raw<AnyToDeviceEvent> {
1507 match self {
1508 ProcessedToDeviceEvent::Decrypted { raw, .. } => raw,
1509 ProcessedToDeviceEvent::UnableToDecrypt { encrypted_event, .. } => encrypted_event,
1510 ProcessedToDeviceEvent::PlainText(event) => event,
1511 ProcessedToDeviceEvent::Invalid(event) => event,
1512 }
1513 }
1514}
1515
1516#[cfg(test)]
1517mod tests {
1518 use std::{collections::BTreeMap, sync::Arc};
1519
1520 use assert_matches::assert_matches;
1521 use insta::{assert_json_snapshot, with_settings};
1522 use ruma::{
1523 DeviceKeyAlgorithm, MilliSecondsSinceUnixEpoch, UInt, event_id,
1524 events::{AnySyncTimelineEvent, room::message::RoomMessageEventContent},
1525 owned_device_id, owned_user_id,
1526 serde::Raw,
1527 };
1528 use serde::Deserialize;
1529 use serde_json::json;
1530 use strass::assert_let;
1531
1532 use super::{
1533 AlgorithmInfo, DecryptedRoomEvent, DeviceLinkProblem, EncryptionInfo, ShieldState,
1534 ShieldStateCode, TimelineEvent, TimelineEventKind, UnableToDecryptInfo,
1535 UnableToDecryptReason, UnsignedDecryptionResult, UnsignedEventLocation, VerificationLevel,
1536 VerificationState, WithheldCode,
1537 };
1538
1539 fn example_event() -> serde_json::Value {
1540 json!({
1541 "content": RoomMessageEventContent::text_plain("secret"),
1542 "type": "m.room.message",
1543 "event_id": "$xxxxx:example.org",
1544 "room_id": "!someroom:example.com",
1545 "origin_server_ts": 2189,
1546 "sender": "@carl:example.com",
1547 })
1548 }
1549
1550 #[test]
1551 fn sync_timeline_debug_content() {
1552 let room_event =
1553 TimelineEvent::from_plaintext(Raw::new(&example_event()).unwrap().cast_unchecked());
1554 let debug_s = format!("{room_event:?}");
1555 assert!(
1556 !debug_s.contains("secret"),
1557 "Debug representation contains event content!\n{debug_s}"
1558 );
1559 }
1560
1561 #[test]
1562 fn old_verification_state_to_new_migration() {
1563 #[derive(Deserialize)]
1564 struct State {
1565 state: VerificationState,
1566 }
1567
1568 let state = json!({
1569 "state": "Trusted",
1570 });
1571 let deserialized: State =
1572 serde_json::from_value(state).expect("We can deserialize the old trusted value");
1573 assert_eq!(deserialized.state, VerificationState::Verified);
1574
1575 let state = json!({
1576 "state": "UnknownDevice",
1577 });
1578
1579 let deserialized: State =
1580 serde_json::from_value(state).expect("We can deserialize the old unknown device value");
1581
1582 assert_eq!(
1583 deserialized.state,
1584 VerificationState::Unverified(VerificationLevel::None(
1585 DeviceLinkProblem::MissingDevice
1586 ))
1587 );
1588
1589 let state = json!({
1590 "state": "Untrusted",
1591 });
1592 let deserialized: State =
1593 serde_json::from_value(state).expect("We can deserialize the old trusted value");
1594
1595 assert_eq!(
1596 deserialized.state,
1597 VerificationState::Unverified(VerificationLevel::UnsignedDevice)
1598 );
1599 }
1600
1601 #[test]
1602 fn test_verification_level_deserializes() {
1603 #[derive(Deserialize)]
1605 struct Container {
1606 verification_level: VerificationLevel,
1607 }
1608 let container = json!({ "verification_level": "VerificationViolation" });
1609
1610 let deserialized: Container = serde_json::from_value(container)
1612 .expect("We can deserialize the old PreviouslyVerified value");
1613
1614 assert_eq!(deserialized.verification_level, VerificationLevel::VerificationViolation);
1616 }
1617
1618 #[test]
1619 fn test_verification_level_deserializes_from_old_previously_verified_value() {
1620 #[derive(Deserialize)]
1622 struct Container {
1623 verification_level: VerificationLevel,
1624 }
1625 let container = json!({ "verification_level": "PreviouslyVerified" });
1626
1627 let deserialized: Container = serde_json::from_value(container)
1629 .expect("We can deserialize the old PreviouslyVerified value");
1630
1631 assert_eq!(deserialized.verification_level, VerificationLevel::VerificationViolation);
1633 }
1634
1635 #[test]
1636 fn test_shield_state_code_deserializes() {
1637 #[derive(Deserialize)]
1639 struct Container {
1640 shield_state_code: ShieldStateCode,
1641 }
1642 let container = json!({ "shield_state_code": "VerificationViolation" });
1643
1644 let deserialized: Container = serde_json::from_value(container)
1646 .expect("We can deserialize the old PreviouslyVerified value");
1647
1648 assert_eq!(deserialized.shield_state_code, ShieldStateCode::VerificationViolation);
1650 }
1651
1652 #[test]
1653 fn test_shield_state_code_deserializes_from_old_previously_verified_value() {
1654 #[derive(Deserialize)]
1656 struct Container {
1657 shield_state_code: ShieldStateCode,
1658 }
1659 let container = json!({ "shield_state_code": "PreviouslyVerified" });
1660
1661 let deserialized: Container = serde_json::from_value(container)
1663 .expect("We can deserialize the old PreviouslyVerified value");
1664
1665 assert_eq!(deserialized.shield_state_code, ShieldStateCode::VerificationViolation);
1667 }
1668
1669 #[test]
1670 fn sync_timeline_event_serialisation() {
1671 let kind = TimelineEventKind::Decrypted(DecryptedRoomEvent {
1672 event: Raw::new(&example_event()).unwrap().cast_unchecked(),
1673 encryption_info: Arc::new(EncryptionInfo {
1674 sender: owned_user_id!("@sender:example.com"),
1675 sender_device: None,
1676 forwarder: None,
1677 algorithm_info: AlgorithmInfo::MegolmV1AesSha2 {
1678 curve25519_key: "xxx".to_owned(),
1679 sender_claimed_keys: Default::default(),
1680 session_id: Some("xyz".to_owned()),
1681 },
1682 verification_state: VerificationState::Verified,
1683 }),
1684 unsigned_encryption_info: Some(BTreeMap::from([(
1685 UnsignedEventLocation::RelationsReplace,
1686 UnsignedDecryptionResult::UnableToDecrypt(UnableToDecryptInfo {
1687 session_id: Some("xyz".to_owned()),
1688 reason: UnableToDecryptReason::MalformedEncryptedEvent,
1689 }),
1690 )])),
1691 });
1692 let room_event = TimelineEvent {
1693 event_id: kind.parse_event_id(),
1694 kind,
1695 timestamp: Some(MilliSecondsSinceUnixEpoch(UInt::new_saturating(2189))),
1696 push_actions: Default::default(),
1697 };
1698
1699 let serialized = serde_json::to_value(&room_event).unwrap();
1700
1701 assert_eq!(
1703 serialized,
1704 json!({
1705 "kind": {
1706 "Decrypted": {
1707 "event": {
1708 "content": {"body": "secret", "msgtype": "m.text"},
1709 "event_id": "$xxxxx:example.org",
1710 "origin_server_ts": 2189,
1711 "room_id": "!someroom:example.com",
1712 "sender": "@carl:example.com",
1713 "type": "m.room.message",
1714 },
1715 "encryption_info": {
1716 "sender": "@sender:example.com",
1717 "sender_device": null,
1718 "forwarder": null,
1719 "algorithm_info": {
1720 "MegolmV1AesSha2": {
1721 "curve25519_key": "xxx",
1722 "sender_claimed_keys": {},
1723 "session_id": "xyz",
1724 }
1725 },
1726 "verification_state": "Verified",
1727 },
1728 "unsigned_encryption_info": {
1729 "RelationsReplace": {"UnableToDecrypt": {
1730 "session_id": "xyz",
1731 "reason": "MalformedEncryptedEvent",
1732 }}
1733 }
1734 }
1735 },
1736 "timestamp": 2189,
1737 })
1738 );
1739
1740 let event: TimelineEvent = serde_json::from_value(serialized).unwrap();
1742 assert_eq!(event.event_id.as_deref(), Some(event_id!("$xxxxx:example.org")));
1743 assert_eq!(event.event_id.as_deref(), event.event_id());
1744 assert_matches!(
1745 event.encryption_info().unwrap().algorithm_info,
1746 AlgorithmInfo::MegolmV1AesSha2 { .. }
1747 );
1748 assert_eq!(event.timestamp(), Some(MilliSecondsSinceUnixEpoch(UInt::new_saturating(2189))));
1749 assert_eq!(event.timestamp(), event.timestamp_raw());
1750
1751 let serialized = json!({
1753 "event": {
1754 "content": {"body": "secret", "msgtype": "m.text"},
1755 "event_id": "$xxxxx:example.org",
1756 "origin_server_ts": 2189,
1757 "room_id": "!someroom:example.com",
1758 "sender": "@carl:example.com",
1759 "type": "m.room.message",
1760 },
1761 "encryption_info": {
1762 "sender": "@sender:example.com",
1763 "sender_device": null,
1764 "algorithm_info": {
1765 "MegolmV1AesSha2": {
1766 "curve25519_key": "xxx",
1767 "sender_claimed_keys": {}
1768 }
1769 },
1770 "verification_state": "Verified",
1771 },
1772 });
1773 let event: TimelineEvent = serde_json::from_value(serialized).unwrap();
1774 assert_eq!(event.event_id(), Some(event_id!("$xxxxx:example.org")));
1775 assert_matches!(
1776 event.encryption_info().unwrap().algorithm_info,
1777 AlgorithmInfo::MegolmV1AesSha2 { session_id: None, .. }
1778 );
1779 assert_eq!(event.timestamp(), Some(MilliSecondsSinceUnixEpoch(UInt::new_saturating(2189))));
1780 assert!(event.timestamp_raw().is_none());
1781
1782 let serialized = json!({
1785 "event": {
1786 "content": {"body": "secret", "msgtype": "m.text"},
1787 "event_id": "$xxxxx:example.org",
1788 "origin_server_ts": 2189,
1789 "room_id": "!someroom:example.com",
1790 "sender": "@carl:example.com",
1791 "type": "m.room.message",
1792 },
1793 "encryption_info": {
1794 "sender": "@sender:example.com",
1795 "sender_device": null,
1796 "algorithm_info": {
1797 "MegolmV1AesSha2": {
1798 "curve25519_key": "xxx",
1799 "sender_claimed_keys": {}
1800 }
1801 },
1802 "verification_state": "Verified",
1803 },
1804 "unsigned_encryption_info": {
1805 "RelationsReplace": {"UnableToDecrypt": {"session_id": "xyz"}}
1806 }
1807 });
1808 let event: TimelineEvent = serde_json::from_value(serialized).unwrap();
1809 assert_eq!(event.event_id.as_deref(), event.event_id());
1810 assert_eq!(event.event_id.as_deref(), Some(event_id!("$xxxxx:example.org")));
1811 assert_matches!(
1812 event.encryption_info().unwrap().algorithm_info,
1813 AlgorithmInfo::MegolmV1AesSha2 { .. }
1814 );
1815 assert_eq!(event.timestamp(), Some(MilliSecondsSinceUnixEpoch(UInt::new_saturating(2189))));
1816 assert!(event.timestamp_raw().is_none());
1817 assert_matches!(event.kind, TimelineEventKind::Decrypted(decrypted) => {
1818 assert_matches!(decrypted.unsigned_encryption_info, Some(map) => {
1819 assert_eq!(map.len(), 1);
1820 let (location, result) = map.into_iter().next().unwrap();
1821 assert_eq!(location, UnsignedEventLocation::RelationsReplace);
1822 assert_matches!(result, UnsignedDecryptionResult::UnableToDecrypt(utd_info) => {
1823 assert_eq!(utd_info.session_id, Some("xyz".to_owned()));
1824 assert_eq!(utd_info.reason, UnableToDecryptReason::Unknown);
1825 })
1826 });
1827 });
1828 }
1829
1830 #[test]
1831 fn sync_timeline_event_deserialisation_migration_for_withheld() {
1832 let serialized = json!({
1840 "kind": {
1841 "UnableToDecrypt": {
1842 "event": {
1843 "content": {
1844 "algorithm": "m.megolm.v1.aes-sha2",
1845 "ciphertext": "AwgAEoABzL1JYhqhjW9jXrlT3M6H8mJ4qffYtOQOnPuAPNxsuG20oiD/Fnpv6jnQGhU6YbV9pNM+1mRnTvxW3CbWOPjLKqCWTJTc7Q0vDEVtYePg38ncXNcwMmfhgnNAoW9S7vNs8C003x3yUl6NeZ8bH+ci870BZL+kWM/lMl10tn6U7snNmSjnE3ckvRdO+11/R4//5VzFQpZdf4j036lNSls/WIiI67Fk9iFpinz9xdRVWJFVdrAiPFwb8L5xRZ8aX+e2JDMlc1eW8gk",
1846 "device_id": "SKCGPNUWAU",
1847 "sender_key": "Gim/c7uQdSXyrrUbmUOrBT6sMC0gO7QSLmOK6B7NOm0",
1848 "session_id": "hgLyeSqXfb8vc5AjQLsg6TSHVu0HJ7HZ4B6jgMvxkrs"
1849 },
1850 "event_id": "$xxxxx:example.org",
1851 "origin_server_ts": 2189,
1852 "room_id": "!someroom:example.com",
1853 "sender": "@carl:example.com",
1854 "type": "m.room.message"
1855 },
1856 "utd_info": {
1857 "reason": "MissingMegolmSession",
1858 "session_id": "session000"
1859 }
1860 }
1861 }
1862 });
1863
1864 let result = serde_json::from_value(serialized);
1865 assert!(result.is_ok());
1866
1867 let event: TimelineEvent = result.unwrap();
1869 assert_matches!(
1870 event.kind,
1871 TimelineEventKind::UnableToDecrypt { utd_info, .. }=> {
1872 assert_matches!(
1873 utd_info.reason,
1874 UnableToDecryptReason::MissingMegolmSession { withheld_code: None }
1875 );
1876 }
1877 )
1878 }
1879
1880 #[test]
1881 fn unable_to_decrypt_info_migration_for_withheld() {
1882 let old_format = json!({
1883 "reason": "MissingMegolmSession",
1884 "session_id": "session000"
1885 });
1886
1887 let deserialized = serde_json::from_value::<UnableToDecryptInfo>(old_format).unwrap();
1888 let session_id = Some("session000".to_owned());
1889
1890 assert_eq!(deserialized.session_id, session_id);
1891 assert_eq!(
1892 deserialized.reason,
1893 UnableToDecryptReason::MissingMegolmSession { withheld_code: None },
1894 );
1895
1896 let new_format = json!({
1897 "session_id": "session000",
1898 "reason": {
1899 "MissingMegolmSession": {
1900 "withheld_code": null
1901 }
1902 }
1903 });
1904
1905 let deserialized = serde_json::from_value::<UnableToDecryptInfo>(new_format).unwrap();
1906
1907 assert_eq!(
1908 deserialized.reason,
1909 UnableToDecryptReason::MissingMegolmSession { withheld_code: None },
1910 );
1911 assert_eq!(deserialized.session_id, session_id);
1912 }
1913
1914 #[test]
1915 fn unable_to_decrypt_reason_is_missing_room_key() {
1916 let reason = UnableToDecryptReason::MissingMegolmSession { withheld_code: None };
1917 assert!(reason.is_missing_room_key());
1918
1919 let reason = UnableToDecryptReason::MissingMegolmSession {
1920 withheld_code: Some(WithheldCode::Blacklisted),
1921 };
1922 assert!(!reason.is_missing_room_key());
1923
1924 let reason = UnableToDecryptReason::UnknownMegolmMessageIndex;
1925 assert!(reason.is_missing_room_key());
1926 }
1927
1928 #[test]
1929 fn snapshot_test_verification_level() {
1930 with_settings!({ prepend_module_to_snapshot => false }, {
1931 assert_json_snapshot!(VerificationLevel::VerificationViolation);
1932 assert_json_snapshot!(VerificationLevel::UnsignedDevice);
1933 assert_json_snapshot!(VerificationLevel::None(DeviceLinkProblem::InsecureSource));
1934 assert_json_snapshot!(VerificationLevel::None(DeviceLinkProblem::MissingDevice));
1935 assert_json_snapshot!(VerificationLevel::UnverifiedIdentity);
1936 });
1937 }
1938
1939 #[test]
1940 fn snapshot_test_verification_states() {
1941 with_settings!({ prepend_module_to_snapshot => false }, {
1942 assert_json_snapshot!(VerificationState::Unverified(VerificationLevel::UnsignedDevice));
1943 assert_json_snapshot!(VerificationState::Unverified(
1944 VerificationLevel::VerificationViolation
1945 ));
1946 assert_json_snapshot!(VerificationState::Unverified(VerificationLevel::None(
1947 DeviceLinkProblem::InsecureSource,
1948 )));
1949 assert_json_snapshot!(VerificationState::Unverified(VerificationLevel::None(
1950 DeviceLinkProblem::MissingDevice,
1951 )));
1952 assert_json_snapshot!(VerificationState::Verified);
1953 });
1954 }
1955
1956 #[test]
1957 fn snapshot_test_shield_states() {
1958 with_settings!({ prepend_module_to_snapshot => false }, {
1959 assert_json_snapshot!(ShieldState::None);
1960 assert_json_snapshot!(ShieldState::Red {
1961 code: ShieldStateCode::UnverifiedIdentity,
1962 message: "a message"
1963 });
1964 assert_json_snapshot!(ShieldState::Grey {
1965 code: ShieldStateCode::AuthenticityNotGuaranteed,
1966 message: "authenticity of this message cannot be guaranteed",
1967 });
1968 });
1969 }
1970
1971 #[test]
1972 fn snapshot_test_shield_codes() {
1973 with_settings!({ prepend_module_to_snapshot => false }, {
1974 assert_json_snapshot!(ShieldStateCode::AuthenticityNotGuaranteed);
1975 assert_json_snapshot!(ShieldStateCode::UnknownDevice);
1976 assert_json_snapshot!(ShieldStateCode::UnsignedDevice);
1977 assert_json_snapshot!(ShieldStateCode::UnverifiedIdentity);
1978 assert_json_snapshot!(ShieldStateCode::VerificationViolation);
1979 });
1980 }
1981
1982 #[test]
1983 fn snapshot_test_algorithm_info() {
1984 let mut map = BTreeMap::new();
1985 map.insert(DeviceKeyAlgorithm::Curve25519, "claimedclaimedcurve25519".to_owned());
1986 map.insert(DeviceKeyAlgorithm::Ed25519, "claimedclaimeded25519".to_owned());
1987 let info = AlgorithmInfo::MegolmV1AesSha2 {
1988 curve25519_key: "curvecurvecurve".into(),
1989 sender_claimed_keys: BTreeMap::from([
1990 (DeviceKeyAlgorithm::Curve25519, "claimedclaimedcurve25519".to_owned()),
1991 (DeviceKeyAlgorithm::Ed25519, "claimedclaimeded25519".to_owned()),
1992 ]),
1993 session_id: None,
1994 };
1995
1996 with_settings!({ prepend_module_to_snapshot => false }, {
1997 assert_json_snapshot!(info);
1998 });
1999 }
2000
2001 #[test]
2002 fn test_encryption_info_migration() {
2003 let old_format = json!({
2006 "sender": "@alice:localhost",
2007 "sender_device": "ABCDEFGH",
2008 "algorithm_info": {
2009 "MegolmV1AesSha2": {
2010 "curve25519_key": "curvecurvecurve",
2011 "sender_claimed_keys": {}
2012 }
2013 },
2014 "verification_state": "Verified",
2015 "session_id": "mysessionid76"
2016 });
2017
2018 let deserialized = serde_json::from_value::<EncryptionInfo>(old_format).unwrap();
2019 let expected_session_id = Some("mysessionid76".to_owned());
2020
2021 assert_let!(
2022 AlgorithmInfo::MegolmV1AesSha2 { session_id, .. } = deserialized.algorithm_info.clone()
2023 );
2024 assert_eq!(session_id, expected_session_id);
2025
2026 assert_json_snapshot!(deserialized);
2027 }
2028
2029 #[test]
2030 fn snapshot_test_encryption_info() {
2031 let info = EncryptionInfo {
2032 sender: owned_user_id!("@alice:localhost"),
2033 sender_device: Some(owned_device_id!("ABCDEFGH")),
2034 forwarder: None,
2035 algorithm_info: AlgorithmInfo::MegolmV1AesSha2 {
2036 curve25519_key: "curvecurvecurve".into(),
2037 sender_claimed_keys: Default::default(),
2038 session_id: Some("mysessionid76".to_owned()),
2039 },
2040 verification_state: VerificationState::Verified,
2041 };
2042
2043 with_settings!({ sort_maps => true, prepend_module_to_snapshot => false }, {
2044 assert_json_snapshot!(info);
2045 })
2046 }
2047
2048 #[test]
2049 fn snapshot_test_sync_timeline_event() {
2050 let kind = TimelineEventKind::Decrypted(DecryptedRoomEvent {
2051 event: Raw::new(&example_event()).unwrap().cast_unchecked(),
2052 encryption_info: Arc::new(EncryptionInfo {
2053 sender: owned_user_id!("@sender:example.com"),
2054 sender_device: Some(owned_device_id!("ABCDEFGHIJ")),
2055 forwarder: None,
2056 algorithm_info: AlgorithmInfo::MegolmV1AesSha2 {
2057 curve25519_key: "xxx".to_owned(),
2058 sender_claimed_keys: BTreeMap::from([
2059 (
2060 DeviceKeyAlgorithm::Ed25519,
2061 "I3YsPwqMZQXHkSQbjFNEs7b529uac2xBpI83eN3LUXo".to_owned(),
2062 ),
2063 (
2064 DeviceKeyAlgorithm::Curve25519,
2065 "qzdW3F5IMPFl0HQgz5w/L5Oi/npKUFn8Um84acIHfPY".to_owned(),
2066 ),
2067 ]),
2068 session_id: Some("mysessionid112".to_owned()),
2069 },
2070 verification_state: VerificationState::Verified,
2071 }),
2072 unsigned_encryption_info: Some(BTreeMap::from([(
2073 UnsignedEventLocation::RelationsThreadLatestEvent,
2074 UnsignedDecryptionResult::UnableToDecrypt(UnableToDecryptInfo {
2075 session_id: Some("xyz".to_owned()),
2076 reason: UnableToDecryptReason::MissingMegolmSession {
2077 withheld_code: Some(WithheldCode::Unverified),
2078 },
2079 }),
2080 )])),
2081 });
2082 let room_event = TimelineEvent {
2083 event_id: kind.parse_event_id(),
2084 kind,
2085 timestamp: Some(MilliSecondsSinceUnixEpoch(UInt::new_saturating(2189))),
2086 push_actions: Default::default(),
2087 };
2088
2089 with_settings!({ sort_maps => true, prepend_module_to_snapshot => false }, {
2090 assert_json_snapshot! {
2094 serde_json::to_value(&room_event).unwrap(),
2095 }
2096 });
2097 }
2098
2099 #[test]
2100 fn test_from_bundled_latest_event_keeps_session_id() {
2101 let session_id = "hgLyeSqXfb8vc5AjQLsg6TSHVu0HJ7HZ4B6jgMvxkrs";
2102 let serialized = json!({
2103 "content": {
2104 "algorithm": "m.megolm.v1.aes-sha2",
2105 "ciphertext": "AwgAEoABzL1JYhqhjW9jXrlT3M6H8mJ4qffYtOQOnPuAPNxsuG20oiD/Fnpv6jnQGhU6YbV9pNM+1mRnTvxW3CbWOPjLKqCWTJTc7Q0vDEVtYePg38ncXNcwMmfhgnNAoW9S7vNs8C003x3yUl6NeZ8bH+ci870BZL+kWM/lMl10tn6U7snNmSjnE3ckvRdO+11/R4//5VzFQpZdf4j036lNSls/WIiI67Fk9iFpinz9xdRVWJFVdrAiPFwb8L5xRZ8aX+e2JDMlc1eW8gk",
2106 "device_id": "SKCGPNUWAU",
2107 "sender_key": "Gim/c7uQdSXyrrUbmUOrBT6sMC0gO7QSLmOK6B7NOm0",
2108 "session_id": session_id,
2109 },
2110 "event_id": "$xxxxx:example.org",
2111 "origin_server_ts": 2189,
2112 "room_id": "!someroom:example.com",
2113 "sender": "@carl:example.com",
2114 "type": "m.room.encrypted"
2115 });
2116 let json = serialized.to_string();
2117 let value = Raw::<AnySyncTimelineEvent>::from_json_string(json).unwrap();
2118
2119 let kind = TimelineEventKind::UnableToDecrypt {
2120 event: value.clone(),
2121 utd_info: UnableToDecryptInfo {
2122 session_id: None,
2123 reason: UnableToDecryptReason::Unknown,
2124 },
2125 };
2126 let result = TimelineEvent::from_bundled_latest_event(
2127 &kind,
2128 value.cast_unchecked(),
2129 MilliSecondsSinceUnixEpoch::now(),
2130 )
2131 .expect("Could not get bundled latest event");
2132
2133 assert_let!(TimelineEventKind::UnableToDecrypt { utd_info, .. } = result.kind);
2134 assert!(utd_info.session_id.is_some());
2135 assert_eq!(utd_info.session_id.unwrap(), session_id);
2136 }
2137
2138 #[test]
2139 fn test_timeline_event_replace_raw_update_the_event_id() {
2140 let mut timeline_event = TimelineEvent::from_plaintext(
2141 Raw::new(&json!({
2142 "event_id": "$ev0",
2143 "type": "m.room.message",
2144 "sender": "@alice",
2145 "origin_server_ts": 42,
2146 "content": {
2147 "body": "Hello, World!",
2148 },
2149 "unsigned": {},
2150 }))
2151 .unwrap()
2152 .cast_unchecked(),
2153 );
2154
2155 assert_eq!(timeline_event.event_id(), Some(event_id!("$ev0")));
2156
2157 timeline_event.replace_raw(
2158 Raw::new(&json!({
2159 "event_id": "$ev1",
2160 "type": "m.room.message",
2161 "sender": "@bob",
2162 "origin_server_ts": 153,
2163 "content": {
2164 "body": "Bonjour !",
2165 },
2166 "unsigned": {},
2167 }))
2168 .unwrap()
2169 .cast_unchecked(),
2170 );
2171
2172 assert_eq!(timeline_event.event_id(), Some(event_id!("$ev1")));
2173 }
2174}