1use std::{collections::BTreeMap, fmt, ops::Not, sync::Arc};
16
17use ruma::{
18 DeviceKeyAlgorithm, MilliSecondsSinceUnixEpoch, OwnedDeviceId, OwnedEventId, OwnedUserId,
19 events::{
20 AnySyncMessageLikeEvent, AnySyncTimelineEvent, AnyTimelineEvent, AnyToDeviceEvent,
21 MessageLikeEventType,
22 },
23 push::Action,
24 serde::{
25 AsRefStr, AsStrAsRefStr, DebugAsRefStr, DeserializeFromCowStr, FromString, JsonObject, Raw,
26 SerializeAsRefStr,
27 },
28};
29use serde::{Deserialize, Serialize};
30use tracing::warn;
31#[cfg(target_family = "wasm")]
32use wasm_bindgen::prelude::*;
33
34use crate::{
35 debug::{DebugRawEvent, DebugStructExt},
36 serde_helpers::{extract_bundled_thread_summary, extract_timestamp},
37};
38
39const AUTHENTICITY_NOT_GUARANTEED: &str =
40 "The authenticity of this encrypted message can't be guaranteed on this device.";
41const UNVERIFIED_IDENTITY: &str = "Encrypted by an unverified user.";
42const VERIFICATION_VIOLATION: &str =
43 "Encrypted by a previously-verified user who is no longer verified.";
44const UNSIGNED_DEVICE: &str = "Encrypted by a device not verified by its owner.";
45const UNKNOWN_DEVICE: &str = "Encrypted by an unknown or deleted device.";
46const MISMATCHED_SENDER: &str = "\
47 The sender of the event does not match the owner of the device \
48 that created the Megolm session.";
49pub const SENT_IN_CLEAR: &str = "Not encrypted.";
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 {
159 code: ShieldStateCode::UnsignedDevice,
160 message: UNSIGNED_DEVICE,
161 }
162 }
163 VerificationLevel::None(link) => match link {
164 DeviceLinkProblem::MissingDevice => {
165 ShieldState::Red {
169 code: ShieldStateCode::UnknownDevice,
170 message: UNKNOWN_DEVICE,
171 }
172 }
173 DeviceLinkProblem::InsecureSource => {
174 ShieldState::Grey {
177 code: ShieldStateCode::AuthenticityNotGuaranteed,
178 message: AUTHENTICITY_NOT_GUARANTEED,
179 }
180 }
181 },
182 VerificationLevel::MismatchedSender => ShieldState::Red {
183 code: ShieldStateCode::MismatchedSender,
184 message: MISMATCHED_SENDER,
185 },
186 },
187 }
188 }
189}
190
191#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
194pub enum VerificationLevel {
195 UnverifiedIdentity,
197
198 #[serde(alias = "PreviouslyVerified")]
201 VerificationViolation,
202
203 UnsignedDevice,
206
207 None(DeviceLinkProblem),
213
214 MismatchedSender,
217}
218
219impl fmt::Display for VerificationLevel {
220 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
221 let display = match self {
222 VerificationLevel::UnverifiedIdentity => "The sender's identity was not verified",
223 VerificationLevel::VerificationViolation => {
224 "The sender's identity was previously verified but has changed"
225 }
226 VerificationLevel::UnsignedDevice => {
227 "The sending device was not signed by the user's identity"
228 }
229 VerificationLevel::None(..) => "The sending device is not known",
230 VerificationLevel::MismatchedSender => MISMATCHED_SENDER,
231 };
232 write!(f, "{display}")
233 }
234}
235
236#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
239pub enum DeviceLinkProblem {
240 MissingDevice,
244 InsecureSource,
247}
248
249#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq)]
252pub enum ShieldState {
253 Red {
256 code: ShieldStateCode,
258 message: &'static str,
260 },
261 Grey {
264 code: ShieldStateCode,
266 message: &'static str,
268 },
269 None,
271}
272
273#[derive(Clone, Copy, Debug, Deserialize, Serialize, Eq, PartialEq)]
275#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
276#[cfg_attr(target_family = "wasm", wasm_bindgen)]
277pub enum ShieldStateCode {
278 AuthenticityNotGuaranteed,
280 UnknownDevice,
282 UnsignedDevice,
284 UnverifiedIdentity,
286 SentInClear,
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)]
324pub struct EncryptionInfo {
325 pub sender: OwnedUserId,
328 pub sender_device: Option<OwnedDeviceId>,
331 pub algorithm_info: AlgorithmInfo,
333 pub verification_state: VerificationState,
340}
341
342impl EncryptionInfo {
343 pub fn session_id(&self) -> Option<&str> {
345 if let AlgorithmInfo::MegolmV1AesSha2 { session_id, .. } = &self.algorithm_info {
346 session_id.as_deref()
347 } else {
348 None
349 }
350 }
351}
352
353impl<'de> Deserialize<'de> for EncryptionInfo {
354 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
355 where
356 D: serde::Deserializer<'de>,
357 {
358 #[derive(Deserialize)]
361 struct Helper {
362 pub sender: OwnedUserId,
363 pub sender_device: Option<OwnedDeviceId>,
364 pub algorithm_info: AlgorithmInfo,
365 pub verification_state: VerificationState,
366 #[serde(rename = "session_id")]
367 pub old_session_id: Option<String>,
368 }
369
370 let Helper { sender, sender_device, algorithm_info, verification_state, old_session_id } =
371 Helper::deserialize(deserializer)?;
372
373 let algorithm_info = match algorithm_info {
374 AlgorithmInfo::MegolmV1AesSha2 { curve25519_key, sender_claimed_keys, session_id } => {
375 AlgorithmInfo::MegolmV1AesSha2 {
376 session_id: session_id.or(old_session_id),
378 curve25519_key,
379 sender_claimed_keys,
380 }
381 }
382 other => other,
383 };
384
385 Ok(EncryptionInfo { sender, sender_device, algorithm_info, verification_state })
386 }
387}
388
389#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
400pub struct ThreadSummary {
401 #[serde(skip_serializing_if = "Option::is_none")]
403 pub latest_reply: Option<OwnedEventId>,
404
405 pub num_replies: u32,
411}
412
413#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)]
415pub enum ThreadSummaryStatus {
416 #[default]
418 Unknown,
419 None,
421 Some(ThreadSummary),
423}
424
425impl ThreadSummaryStatus {
426 pub fn from_opt(summary: Option<ThreadSummary>) -> Self {
428 match summary {
429 None => ThreadSummaryStatus::None,
430 Some(summary) => ThreadSummaryStatus::Some(summary),
431 }
432 }
433
434 fn is_unknown(&self) -> bool {
436 matches!(self, ThreadSummaryStatus::Unknown)
437 }
438
439 pub fn summary(&self) -> Option<&ThreadSummary> {
442 match self {
443 ThreadSummaryStatus::Unknown | ThreadSummaryStatus::None => None,
444 ThreadSummaryStatus::Some(thread_summary) => Some(thread_summary),
445 }
446 }
447}
448
449#[derive(Clone, Debug, Serialize)]
472pub struct TimelineEvent {
473 pub kind: TimelineEventKind,
475
476 pub timestamp: Option<MilliSecondsSinceUnixEpoch>,
482
483 #[serde(skip_serializing_if = "skip_serialize_push_actions")]
488 push_actions: Option<Vec<Action>>,
489
490 #[serde(default, skip_serializing_if = "ThreadSummaryStatus::is_unknown")]
492 pub thread_summary: ThreadSummaryStatus,
493
494 #[serde(skip)]
499 pub bundled_latest_thread_event: Option<Box<TimelineEvent>>,
500}
501
502fn skip_serialize_push_actions(push_actions: &Option<Vec<Action>>) -> bool {
504 push_actions.as_ref().is_none_or(|v| v.is_empty())
505}
506
507#[cfg(not(feature = "test-send-sync"))]
509unsafe impl Send for TimelineEvent {}
510
511#[cfg(not(feature = "test-send-sync"))]
513unsafe impl Sync for TimelineEvent {}
514
515#[cfg(feature = "test-send-sync")]
516#[test]
517fn test_send_sync_for_sync_timeline_event() {
519 fn assert_send_sync<T: crate::SendOutsideWasm + crate::SyncOutsideWasm>() {}
520
521 assert_send_sync::<TimelineEvent>();
522}
523
524impl TimelineEvent {
525 pub fn from_plaintext(event: Raw<AnySyncTimelineEvent>) -> Self {
530 Self::from_plaintext_with_max_timestamp(event, MilliSecondsSinceUnixEpoch::now())
531 }
532
533 pub fn from_plaintext_with_max_timestamp(
535 event: Raw<AnySyncTimelineEvent>,
536 max_timestamp: MilliSecondsSinceUnixEpoch,
537 ) -> Self {
538 Self::new(TimelineEventKind::PlainText { event }, None, max_timestamp)
539 }
540
541 pub fn from_decrypted(
543 decrypted: DecryptedRoomEvent,
544 push_actions: Option<Vec<Action>>,
545 ) -> Self {
546 Self::from_decrypted_with_max_timestamp(
547 decrypted,
548 push_actions,
549 MilliSecondsSinceUnixEpoch::now(),
550 )
551 }
552
553 pub fn from_decrypted_with_max_timestamp(
555 decrypted: DecryptedRoomEvent,
556 push_actions: Option<Vec<Action>>,
557 max_timestamp: MilliSecondsSinceUnixEpoch,
558 ) -> Self {
559 Self::new(TimelineEventKind::Decrypted(decrypted), push_actions, max_timestamp)
560 }
561
562 pub fn from_utd(event: Raw<AnySyncTimelineEvent>, utd_info: UnableToDecryptInfo) -> Self {
565 Self::from_utd_with_max_timestamp(event, utd_info, MilliSecondsSinceUnixEpoch::now())
566 }
567
568 pub fn from_utd_with_max_timestamp(
570 event: Raw<AnySyncTimelineEvent>,
571 utd_info: UnableToDecryptInfo,
572 max_timestamp: MilliSecondsSinceUnixEpoch,
573 ) -> Self {
574 Self::new(TimelineEventKind::UnableToDecrypt { event, utd_info }, None, max_timestamp)
575 }
576
577 fn new(
582 kind: TimelineEventKind,
583 push_actions: Option<Vec<Action>>,
584 max_timestamp: MilliSecondsSinceUnixEpoch,
585 ) -> Self {
586 let raw = kind.raw();
587
588 let (thread_summary, latest_thread_event) = extract_bundled_thread_summary(raw);
589
590 let bundled_latest_thread_event =
591 Self::from_bundled_latest_event(&kind, latest_thread_event, max_timestamp);
592
593 let timestamp = extract_timestamp(raw, max_timestamp);
594
595 Self { kind, push_actions, timestamp, thread_summary, bundled_latest_thread_event }
596 }
597
598 pub fn to_decrypted(
606 &self,
607 decrypted: DecryptedRoomEvent,
608 push_actions: Option<Vec<Action>>,
609 ) -> Self {
610 debug_assert!(
611 matches!(self.kind, TimelineEventKind::Decrypted(_)).not(),
612 "`TimelineEvent::to_decrypted` has been called on an already decrypted `TimelineEvent`."
613 );
614
615 Self {
616 kind: TimelineEventKind::Decrypted(decrypted),
617 timestamp: self.timestamp,
618 push_actions,
619 thread_summary: self.thread_summary.clone(),
620 bundled_latest_thread_event: self.bundled_latest_thread_event.clone(),
621 }
622 }
623
624 pub fn to_utd(&self, utd_info: UnableToDecryptInfo) -> Self {
632 debug_assert!(
633 matches!(self.kind, TimelineEventKind::UnableToDecrypt { .. }).not(),
634 "`TimelineEvent::to_utd` has been called on an already UTD `TimelineEvent`."
635 );
636
637 Self {
638 kind: TimelineEventKind::UnableToDecrypt { event: self.raw().clone(), utd_info },
639 timestamp: self.timestamp,
640 push_actions: None,
641 thread_summary: self.thread_summary.clone(),
642 bundled_latest_thread_event: self.bundled_latest_thread_event.clone(),
643 }
644 }
645
646 fn from_bundled_latest_event(
650 kind: &TimelineEventKind,
651 latest_event: Option<Raw<AnySyncMessageLikeEvent>>,
652 max_timestamp: MilliSecondsSinceUnixEpoch,
653 ) -> Option<Box<Self>> {
654 let latest_event = latest_event?;
655
656 match kind {
657 TimelineEventKind::Decrypted(decrypted) => {
658 if let Some(unsigned_decryption_result) =
659 decrypted.unsigned_encryption_info.as_ref().and_then(|unsigned_map| {
660 unsigned_map.get(&UnsignedEventLocation::RelationsThreadLatestEvent)
661 })
662 {
663 match unsigned_decryption_result {
664 UnsignedDecryptionResult::Decrypted(encryption_info) => {
665 return Some(Box::new(
668 TimelineEvent::from_decrypted_with_max_timestamp(
669 DecryptedRoomEvent {
670 event: latest_event.cast_unchecked(),
673 encryption_info: encryption_info.clone(),
674 unsigned_encryption_info: None,
679 },
680 None,
681 max_timestamp,
682 ),
683 ));
684 }
685
686 UnsignedDecryptionResult::UnableToDecrypt(utd_info) => {
687 return Some(Box::new(TimelineEvent::from_utd_with_max_timestamp(
689 latest_event.cast(),
690 utd_info.clone(),
691 max_timestamp,
692 )));
693 }
694 }
695 }
696 }
697
698 TimelineEventKind::UnableToDecrypt { .. } | TimelineEventKind::PlainText { .. } => {
699 }
701 }
702
703 match latest_event.get_field::<MessageLikeEventType>("type") {
704 Ok(None) => {
705 let event_id = latest_event.get_field::<OwnedEventId>("event_id").ok().flatten();
706 warn!(
707 ?event_id,
708 "couldn't deserialize bundled latest thread event: missing `type` field \
709 in bundled latest thread event"
710 );
711 None
712 }
713
714 Ok(Some(MessageLikeEventType::RoomEncrypted)) => {
715 Some(Box::new(TimelineEvent::from_utd_with_max_timestamp(
719 latest_event.cast(),
720 UnableToDecryptInfo {
721 session_id: None,
722 reason: UnableToDecryptReason::Unknown,
723 },
724 max_timestamp,
725 )))
726 }
727
728 Ok(_) => Some(Box::new(TimelineEvent::from_plaintext_with_max_timestamp(
729 latest_event.cast(),
730 max_timestamp,
731 ))),
732
733 Err(err) => {
734 let event_id = latest_event.get_field::<OwnedEventId>("event_id").ok().flatten();
735 warn!(?event_id, "couldn't deserialize bundled latest thread event's type: {err}");
736 None
737 }
738 }
739 }
740
741 pub fn push_actions(&self) -> Option<&[Action]> {
746 self.push_actions.as_deref()
747 }
748
749 pub fn set_push_actions(&mut self, push_actions: Vec<Action>) {
751 self.push_actions = Some(push_actions);
752 }
753
754 pub fn event_id(&self) -> Option<OwnedEventId> {
757 self.kind.event_id()
758 }
759
760 pub fn raw(&self) -> &Raw<AnySyncTimelineEvent> {
763 self.kind.raw()
764 }
765
766 pub fn replace_raw(&mut self, replacement: Raw<AnyTimelineEvent>) {
768 match &mut self.kind {
769 TimelineEventKind::Decrypted(decrypted) => decrypted.event = replacement,
770 TimelineEventKind::UnableToDecrypt { event, .. }
771 | TimelineEventKind::PlainText { event } => {
772 *event = replacement.cast();
775 }
776 }
777 }
778
779 pub fn timestamp(&self) -> Option<MilliSecondsSinceUnixEpoch> {
788 self.timestamp.or_else(|| {
789 warn!("`TimelineEvent::timestamp` is parsing the raw event to extract the `timestamp`");
790
791 extract_timestamp(self.raw(), MilliSecondsSinceUnixEpoch::now())
792 })
793 }
794
795 pub fn timestamp_raw(&self) -> Option<MilliSecondsSinceUnixEpoch> {
797 self.timestamp
798 }
799
800 pub fn encryption_info(&self) -> Option<&Arc<EncryptionInfo>> {
803 self.kind.encryption_info()
804 }
805
806 pub fn into_raw(self) -> Raw<AnySyncTimelineEvent> {
809 self.kind.into_raw()
810 }
811}
812
813impl<'de> Deserialize<'de> for TimelineEvent {
814 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
822 where
823 D: serde::Deserializer<'de>,
824 {
825 use serde_json::{Map, Value};
826
827 let value = Map::<String, Value>::deserialize(deserializer)?;
829
830 if value.contains_key("event") {
832 let v0: SyncTimelineEventDeserializationHelperV0 =
833 serde_json::from_value(Value::Object(value)).map_err(|e| {
834 serde::de::Error::custom(format!(
835 "Unable to deserialize V0-format TimelineEvent: {e}",
836 ))
837 })?;
838 Ok(v0.into())
839 }
840 else {
842 let v1: SyncTimelineEventDeserializationHelperV1 =
843 serde_json::from_value(Value::Object(value)).map_err(|e| {
844 serde::de::Error::custom(format!(
845 "Unable to deserialize V1-format TimelineEvent: {e}",
846 ))
847 })?;
848 Ok(v1.into())
849 }
850 }
851}
852
853#[derive(Clone, Serialize, Deserialize)]
855pub enum TimelineEventKind {
856 Decrypted(DecryptedRoomEvent),
858
859 UnableToDecrypt {
861 event: Raw<AnySyncTimelineEvent>,
865
866 utd_info: UnableToDecryptInfo,
868 },
869
870 PlainText {
872 event: Raw<AnySyncTimelineEvent>,
876 },
877}
878
879impl TimelineEventKind {
880 pub fn raw(&self) -> &Raw<AnySyncTimelineEvent> {
883 match self {
884 TimelineEventKind::Decrypted(d) => d.event.cast_ref(),
890 TimelineEventKind::UnableToDecrypt { event, .. } => event,
891 TimelineEventKind::PlainText { event } => event,
892 }
893 }
894
895 pub fn event_id(&self) -> Option<OwnedEventId> {
898 self.raw().get_field::<OwnedEventId>("event_id").ok().flatten()
899 }
900
901 pub fn is_utd(&self) -> bool {
903 matches!(self, TimelineEventKind::UnableToDecrypt { .. })
904 }
905
906 pub fn encryption_info(&self) -> Option<&Arc<EncryptionInfo>> {
909 match self {
910 TimelineEventKind::Decrypted(d) => Some(&d.encryption_info),
911 TimelineEventKind::UnableToDecrypt { .. } | TimelineEventKind::PlainText { .. } => None,
912 }
913 }
914
915 pub fn unsigned_encryption_map(
918 &self,
919 ) -> Option<&BTreeMap<UnsignedEventLocation, UnsignedDecryptionResult>> {
920 match self {
921 TimelineEventKind::Decrypted(d) => d.unsigned_encryption_info.as_ref(),
922 TimelineEventKind::UnableToDecrypt { .. } | TimelineEventKind::PlainText { .. } => None,
923 }
924 }
925
926 pub fn into_raw(self) -> Raw<AnySyncTimelineEvent> {
929 match self {
930 TimelineEventKind::Decrypted(d) => d.event.cast(),
936 TimelineEventKind::UnableToDecrypt { event, .. } => event,
937 TimelineEventKind::PlainText { event } => event,
938 }
939 }
940
941 pub fn session_id(&self) -> Option<&str> {
944 match self {
945 TimelineEventKind::Decrypted(decrypted_room_event) => {
946 decrypted_room_event.encryption_info.session_id()
947 }
948 TimelineEventKind::UnableToDecrypt { utd_info, .. } => utd_info.session_id.as_deref(),
949 TimelineEventKind::PlainText { .. } => None,
950 }
951 }
952}
953
954#[cfg(not(tarpaulin_include))]
955impl fmt::Debug for TimelineEventKind {
956 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
957 match &self {
958 Self::PlainText { event } => f
959 .debug_struct("TimelineEventDecryptionResult::PlainText")
960 .field("event", &DebugRawEvent(event))
961 .finish(),
962
963 Self::UnableToDecrypt { event, utd_info } => f
964 .debug_struct("TimelineEventDecryptionResult::UnableToDecrypt")
965 .field("event", &DebugRawEvent(event))
966 .field("utd_info", &utd_info)
967 .finish(),
968
969 Self::Decrypted(decrypted) => {
970 f.debug_tuple("TimelineEventDecryptionResult::Decrypted").field(decrypted).finish()
971 }
972 }
973 }
974}
975
976#[derive(Clone, Serialize, Deserialize)]
977pub struct DecryptedRoomEvent {
979 pub event: Raw<AnyTimelineEvent>,
987
988 pub encryption_info: Arc<EncryptionInfo>,
990
991 #[serde(skip_serializing_if = "Option::is_none")]
996 pub unsigned_encryption_info: Option<BTreeMap<UnsignedEventLocation, UnsignedDecryptionResult>>,
997}
998
999#[cfg(not(tarpaulin_include))]
1000impl fmt::Debug for DecryptedRoomEvent {
1001 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1002 let DecryptedRoomEvent { event, encryption_info, unsigned_encryption_info } = self;
1003
1004 f.debug_struct("DecryptedRoomEvent")
1005 .field("event", &DebugRawEvent(event))
1006 .field("encryption_info", encryption_info)
1007 .maybe_field("unsigned_encryption_info", unsigned_encryption_info)
1008 .finish()
1009 }
1010}
1011
1012#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
1014pub enum UnsignedEventLocation {
1015 RelationsReplace,
1018 RelationsThreadLatestEvent,
1021}
1022
1023impl UnsignedEventLocation {
1024 pub fn find_mut<'a>(&self, unsigned: &'a mut JsonObject) -> Option<&'a mut serde_json::Value> {
1031 let relations = unsigned.get_mut("m.relations")?.as_object_mut()?;
1032
1033 match self {
1034 Self::RelationsReplace => relations.get_mut("m.replace"),
1035 Self::RelationsThreadLatestEvent => {
1036 relations.get_mut("m.thread")?.as_object_mut()?.get_mut("latest_event")
1037 }
1038 }
1039 }
1040}
1041
1042#[derive(Debug, Clone, Serialize, Deserialize)]
1044pub enum UnsignedDecryptionResult {
1045 Decrypted(Arc<EncryptionInfo>),
1047 UnableToDecrypt(UnableToDecryptInfo),
1049}
1050
1051impl UnsignedDecryptionResult {
1052 pub fn encryption_info(&self) -> Option<&Arc<EncryptionInfo>> {
1055 match self {
1056 Self::Decrypted(info) => Some(info),
1057 Self::UnableToDecrypt(_) => None,
1058 }
1059 }
1060}
1061
1062#[derive(Debug, Clone, Serialize, Deserialize)]
1064pub struct UnableToDecryptInfo {
1065 #[serde(skip_serializing_if = "Option::is_none")]
1068 pub session_id: Option<String>,
1069
1070 #[serde(default = "unknown_utd_reason", deserialize_with = "deserialize_utd_reason")]
1072 pub reason: UnableToDecryptReason,
1073}
1074
1075fn unknown_utd_reason() -> UnableToDecryptReason {
1076 UnableToDecryptReason::Unknown
1077}
1078
1079pub fn deserialize_utd_reason<'de, D>(d: D) -> Result<UnableToDecryptReason, D::Error>
1082where
1083 D: serde::Deserializer<'de>,
1084{
1085 let v: serde_json::Value = Deserialize::deserialize(d)?;
1087 if v.as_str().is_some_and(|s| s == "MissingMegolmSession") {
1090 return Ok(UnableToDecryptReason::MissingMegolmSession { withheld_code: None });
1091 }
1092 serde_json::from_value::<UnableToDecryptReason>(v).map_err(serde::de::Error::custom)
1095}
1096
1097#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1099pub enum UnableToDecryptReason {
1100 #[doc(hidden)]
1103 Unknown,
1104
1105 MalformedEncryptedEvent,
1109
1110 MissingMegolmSession {
1113 withheld_code: Option<WithheldCode>,
1116 },
1117
1118 UnknownMegolmMessageIndex,
1121
1122 MegolmDecryptionFailure,
1129
1130 PayloadDeserializationFailure,
1132
1133 MismatchedIdentityKeys,
1137
1138 SenderIdentityNotTrusted(VerificationLevel),
1142
1143 #[cfg(feature = "experimental-encrypted-state-events")]
1146 StateKeyVerificationFailed,
1147}
1148
1149impl UnableToDecryptReason {
1150 pub fn is_missing_room_key(&self) -> bool {
1153 matches!(
1156 self,
1157 Self::MissingMegolmSession { withheld_code: None } | Self::UnknownMegolmMessageIndex
1158 )
1159 }
1160}
1161
1162#[derive(
1166 Clone,
1167 PartialEq,
1168 Eq,
1169 Hash,
1170 AsStrAsRefStr,
1171 AsRefStr,
1172 FromString,
1173 DebugAsRefStr,
1174 SerializeAsRefStr,
1175 DeserializeFromCowStr,
1176)]
1177pub enum WithheldCode {
1178 #[ruma_enum(rename = "m.blacklisted")]
1180 Blacklisted,
1181
1182 #[ruma_enum(rename = "m.unverified")]
1184 Unverified,
1185
1186 #[ruma_enum(rename = "m.unauthorised")]
1190 Unauthorised,
1191
1192 #[ruma_enum(rename = "m.unavailable")]
1195 Unavailable,
1196
1197 #[ruma_enum(rename = "m.no_olm")]
1201 NoOlm,
1202
1203 #[doc(hidden)]
1204 _Custom(PrivOwnedStr),
1205}
1206
1207impl fmt::Display for WithheldCode {
1208 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
1209 let string = match self {
1210 WithheldCode::Blacklisted => "The sender has blocked you.",
1211 WithheldCode::Unverified => "The sender has disabled encrypting to unverified devices.",
1212 WithheldCode::Unauthorised => "You are not authorised to read the message.",
1213 WithheldCode::Unavailable => "The requested key was not found.",
1214 WithheldCode::NoOlm => "Unable to establish a secure channel.",
1215 _ => self.as_str(),
1216 };
1217
1218 f.write_str(string)
1219 }
1220}
1221
1222#[doc(hidden)]
1226#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
1227pub struct PrivOwnedStr(pub Box<str>);
1228
1229#[cfg(not(tarpaulin_include))]
1230impl fmt::Debug for PrivOwnedStr {
1231 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1232 self.0.fmt(f)
1233 }
1234}
1235
1236#[derive(Debug, Deserialize)]
1241struct SyncTimelineEventDeserializationHelperV1 {
1242 kind: TimelineEventKind,
1244
1245 #[serde(default)]
1248 timestamp: Option<MilliSecondsSinceUnixEpoch>,
1249
1250 #[serde(default)]
1252 push_actions: Vec<Action>,
1253
1254 #[serde(default)]
1256 thread_summary: ThreadSummaryStatus,
1257}
1258
1259impl From<SyncTimelineEventDeserializationHelperV1> for TimelineEvent {
1260 fn from(value: SyncTimelineEventDeserializationHelperV1) -> Self {
1261 let SyncTimelineEventDeserializationHelperV1 {
1262 kind,
1263 timestamp,
1264 push_actions,
1265 thread_summary,
1266 } = value;
1267
1268 TimelineEvent {
1277 kind,
1278 timestamp,
1279 push_actions: Some(push_actions),
1280 thread_summary,
1281 bundled_latest_thread_event: None,
1283 }
1284 }
1285}
1286
1287#[derive(Deserialize)]
1289struct SyncTimelineEventDeserializationHelperV0 {
1290 event: Raw<AnySyncTimelineEvent>,
1292
1293 encryption_info: Option<Arc<EncryptionInfo>>,
1297
1298 #[serde(default)]
1300 push_actions: Vec<Action>,
1301
1302 unsigned_encryption_info: Option<BTreeMap<UnsignedEventLocation, UnsignedDecryptionResult>>,
1307}
1308
1309impl From<SyncTimelineEventDeserializationHelperV0> for TimelineEvent {
1310 fn from(value: SyncTimelineEventDeserializationHelperV0) -> Self {
1311 let SyncTimelineEventDeserializationHelperV0 {
1312 event,
1313 encryption_info,
1314 push_actions,
1315 unsigned_encryption_info,
1316 } = value;
1317
1318 let timestamp = None;
1325
1326 let kind = match encryption_info {
1327 Some(encryption_info) => {
1328 TimelineEventKind::Decrypted(DecryptedRoomEvent {
1329 event: event.cast_unchecked(),
1336 encryption_info,
1337 unsigned_encryption_info,
1338 })
1339 }
1340
1341 None => TimelineEventKind::PlainText { event },
1342 };
1343
1344 TimelineEvent {
1345 kind,
1346 timestamp,
1347 push_actions: Some(push_actions),
1348 thread_summary: ThreadSummaryStatus::Unknown,
1350 bundled_latest_thread_event: None,
1352 }
1353 }
1354}
1355
1356#[derive(Debug, Clone, PartialEq)]
1358pub enum ToDeviceUnableToDecryptReason {
1359 DecryptionFailure,
1362
1363 UnverifiedSenderDevice,
1367
1368 NoOlmMachine,
1371
1372 EncryptionIsDisabled,
1374}
1375
1376#[derive(Clone, Debug)]
1378pub struct ToDeviceUnableToDecryptInfo {
1379 pub reason: ToDeviceUnableToDecryptReason,
1381}
1382
1383#[derive(Clone, Debug)]
1385pub enum ProcessedToDeviceEvent {
1386 Decrypted {
1389 raw: Raw<AnyToDeviceEvent>,
1391 encryption_info: EncryptionInfo,
1393 },
1394
1395 UnableToDecrypt {
1397 encrypted_event: Raw<AnyToDeviceEvent>,
1398 utd_info: ToDeviceUnableToDecryptInfo,
1399 },
1400
1401 PlainText(Raw<AnyToDeviceEvent>),
1403
1404 Invalid(Raw<AnyToDeviceEvent>),
1408}
1409
1410impl ProcessedToDeviceEvent {
1411 pub fn to_raw(&self) -> Raw<AnyToDeviceEvent> {
1414 match self {
1415 ProcessedToDeviceEvent::Decrypted { raw, .. } => raw.clone(),
1416 ProcessedToDeviceEvent::UnableToDecrypt { encrypted_event, .. } => {
1417 encrypted_event.clone()
1418 }
1419 ProcessedToDeviceEvent::PlainText(event) => event.clone(),
1420 ProcessedToDeviceEvent::Invalid(event) => event.clone(),
1421 }
1422 }
1423
1424 pub fn as_raw(&self) -> &Raw<AnyToDeviceEvent> {
1426 match self {
1427 ProcessedToDeviceEvent::Decrypted { raw, .. } => raw,
1428 ProcessedToDeviceEvent::UnableToDecrypt { encrypted_event, .. } => encrypted_event,
1429 ProcessedToDeviceEvent::PlainText(event) => event,
1430 ProcessedToDeviceEvent::Invalid(event) => event,
1431 }
1432 }
1433}
1434
1435#[cfg(test)]
1436mod tests {
1437 use std::{collections::BTreeMap, sync::Arc};
1438
1439 use assert_matches::assert_matches;
1440 use assert_matches2::assert_let;
1441 use insta::{assert_json_snapshot, with_settings};
1442 use ruma::{
1443 DeviceKeyAlgorithm, MilliSecondsSinceUnixEpoch, UInt, device_id, event_id,
1444 events::room::message::RoomMessageEventContent, serde::Raw, user_id,
1445 };
1446 use serde::Deserialize;
1447 use serde_json::json;
1448
1449 use super::{
1450 AlgorithmInfo, DecryptedRoomEvent, DeviceLinkProblem, EncryptionInfo, ShieldState,
1451 ShieldStateCode, TimelineEvent, TimelineEventKind, UnableToDecryptInfo,
1452 UnableToDecryptReason, UnsignedDecryptionResult, UnsignedEventLocation, VerificationLevel,
1453 VerificationState, WithheldCode,
1454 };
1455 use crate::deserialized_responses::{ThreadSummary, ThreadSummaryStatus};
1456
1457 fn example_event() -> serde_json::Value {
1458 json!({
1459 "content": RoomMessageEventContent::text_plain("secret"),
1460 "type": "m.room.message",
1461 "event_id": "$xxxxx:example.org",
1462 "room_id": "!someroom:example.com",
1463 "origin_server_ts": 2189,
1464 "sender": "@carl:example.com",
1465 })
1466 }
1467
1468 #[test]
1469 fn sync_timeline_debug_content() {
1470 let room_event =
1471 TimelineEvent::from_plaintext(Raw::new(&example_event()).unwrap().cast_unchecked());
1472 let debug_s = format!("{room_event:?}");
1473 assert!(
1474 !debug_s.contains("secret"),
1475 "Debug representation contains event content!\n{debug_s}"
1476 );
1477 }
1478
1479 #[test]
1480 fn old_verification_state_to_new_migration() {
1481 #[derive(Deserialize)]
1482 struct State {
1483 state: VerificationState,
1484 }
1485
1486 let state = json!({
1487 "state": "Trusted",
1488 });
1489 let deserialized: State =
1490 serde_json::from_value(state).expect("We can deserialize the old trusted value");
1491 assert_eq!(deserialized.state, VerificationState::Verified);
1492
1493 let state = json!({
1494 "state": "UnknownDevice",
1495 });
1496
1497 let deserialized: State =
1498 serde_json::from_value(state).expect("We can deserialize the old unknown device value");
1499
1500 assert_eq!(
1501 deserialized.state,
1502 VerificationState::Unverified(VerificationLevel::None(
1503 DeviceLinkProblem::MissingDevice
1504 ))
1505 );
1506
1507 let state = json!({
1508 "state": "Untrusted",
1509 });
1510 let deserialized: State =
1511 serde_json::from_value(state).expect("We can deserialize the old trusted value");
1512
1513 assert_eq!(
1514 deserialized.state,
1515 VerificationState::Unverified(VerificationLevel::UnsignedDevice)
1516 );
1517 }
1518
1519 #[test]
1520 fn test_verification_level_deserializes() {
1521 #[derive(Deserialize)]
1523 struct Container {
1524 verification_level: VerificationLevel,
1525 }
1526 let container = json!({ "verification_level": "VerificationViolation" });
1527
1528 let deserialized: Container = serde_json::from_value(container)
1530 .expect("We can deserialize the old PreviouslyVerified value");
1531
1532 assert_eq!(deserialized.verification_level, VerificationLevel::VerificationViolation);
1534 }
1535
1536 #[test]
1537 fn test_verification_level_deserializes_from_old_previously_verified_value() {
1538 #[derive(Deserialize)]
1540 struct Container {
1541 verification_level: VerificationLevel,
1542 }
1543 let container = json!({ "verification_level": "PreviouslyVerified" });
1544
1545 let deserialized: Container = serde_json::from_value(container)
1547 .expect("We can deserialize the old PreviouslyVerified value");
1548
1549 assert_eq!(deserialized.verification_level, VerificationLevel::VerificationViolation);
1551 }
1552
1553 #[test]
1554 fn test_shield_state_code_deserializes() {
1555 #[derive(Deserialize)]
1557 struct Container {
1558 shield_state_code: ShieldStateCode,
1559 }
1560 let container = json!({ "shield_state_code": "VerificationViolation" });
1561
1562 let deserialized: Container = serde_json::from_value(container)
1564 .expect("We can deserialize the old PreviouslyVerified value");
1565
1566 assert_eq!(deserialized.shield_state_code, ShieldStateCode::VerificationViolation);
1568 }
1569
1570 #[test]
1571 fn test_shield_state_code_deserializes_from_old_previously_verified_value() {
1572 #[derive(Deserialize)]
1574 struct Container {
1575 shield_state_code: ShieldStateCode,
1576 }
1577 let container = json!({ "shield_state_code": "PreviouslyVerified" });
1578
1579 let deserialized: Container = serde_json::from_value(container)
1581 .expect("We can deserialize the old PreviouslyVerified value");
1582
1583 assert_eq!(deserialized.shield_state_code, ShieldStateCode::VerificationViolation);
1585 }
1586
1587 #[test]
1588 fn sync_timeline_event_serialisation() {
1589 let room_event = TimelineEvent {
1590 kind: TimelineEventKind::Decrypted(DecryptedRoomEvent {
1591 event: Raw::new(&example_event()).unwrap().cast_unchecked(),
1592 encryption_info: Arc::new(EncryptionInfo {
1593 sender: user_id!("@sender:example.com").to_owned(),
1594 sender_device: None,
1595 algorithm_info: AlgorithmInfo::MegolmV1AesSha2 {
1596 curve25519_key: "xxx".to_owned(),
1597 sender_claimed_keys: Default::default(),
1598 session_id: Some("xyz".to_owned()),
1599 },
1600 verification_state: VerificationState::Verified,
1601 }),
1602 unsigned_encryption_info: Some(BTreeMap::from([(
1603 UnsignedEventLocation::RelationsReplace,
1604 UnsignedDecryptionResult::UnableToDecrypt(UnableToDecryptInfo {
1605 session_id: Some("xyz".to_owned()),
1606 reason: UnableToDecryptReason::MalformedEncryptedEvent,
1607 }),
1608 )])),
1609 }),
1610 timestamp: Some(MilliSecondsSinceUnixEpoch(UInt::new_saturating(2189))),
1611 push_actions: Default::default(),
1612 thread_summary: ThreadSummaryStatus::Unknown,
1613 bundled_latest_thread_event: None,
1614 };
1615
1616 let serialized = serde_json::to_value(&room_event).unwrap();
1617
1618 assert_eq!(
1620 serialized,
1621 json!({
1622 "kind": {
1623 "Decrypted": {
1624 "event": {
1625 "content": {"body": "secret", "msgtype": "m.text"},
1626 "event_id": "$xxxxx:example.org",
1627 "origin_server_ts": 2189,
1628 "room_id": "!someroom:example.com",
1629 "sender": "@carl:example.com",
1630 "type": "m.room.message",
1631 },
1632 "encryption_info": {
1633 "sender": "@sender:example.com",
1634 "sender_device": null,
1635 "algorithm_info": {
1636 "MegolmV1AesSha2": {
1637 "curve25519_key": "xxx",
1638 "sender_claimed_keys": {},
1639 "session_id": "xyz",
1640 }
1641 },
1642 "verification_state": "Verified",
1643 },
1644 "unsigned_encryption_info": {
1645 "RelationsReplace": {"UnableToDecrypt": {
1646 "session_id": "xyz",
1647 "reason": "MalformedEncryptedEvent",
1648 }}
1649 }
1650 }
1651 },
1652 "timestamp": 2189,
1653 })
1654 );
1655
1656 let event: TimelineEvent = serde_json::from_value(serialized).unwrap();
1658 assert_eq!(event.event_id(), Some(event_id!("$xxxxx:example.org").to_owned()));
1659 assert_matches!(
1660 event.encryption_info().unwrap().algorithm_info,
1661 AlgorithmInfo::MegolmV1AesSha2 { .. }
1662 );
1663 assert_eq!(event.timestamp(), Some(MilliSecondsSinceUnixEpoch(UInt::new_saturating(2189))));
1664 assert_eq!(event.timestamp(), event.timestamp_raw());
1665
1666 let serialized = json!({
1668 "event": {
1669 "content": {"body": "secret", "msgtype": "m.text"},
1670 "event_id": "$xxxxx:example.org",
1671 "origin_server_ts": 2189,
1672 "room_id": "!someroom:example.com",
1673 "sender": "@carl:example.com",
1674 "type": "m.room.message",
1675 },
1676 "encryption_info": {
1677 "sender": "@sender:example.com",
1678 "sender_device": null,
1679 "algorithm_info": {
1680 "MegolmV1AesSha2": {
1681 "curve25519_key": "xxx",
1682 "sender_claimed_keys": {}
1683 }
1684 },
1685 "verification_state": "Verified",
1686 },
1687 });
1688 let event: TimelineEvent = serde_json::from_value(serialized).unwrap();
1689 assert_eq!(event.event_id(), Some(event_id!("$xxxxx:example.org").to_owned()));
1690 assert_matches!(
1691 event.encryption_info().unwrap().algorithm_info,
1692 AlgorithmInfo::MegolmV1AesSha2 { session_id: None, .. }
1693 );
1694 assert_eq!(event.timestamp(), Some(MilliSecondsSinceUnixEpoch(UInt::new_saturating(2189))));
1695 assert!(event.timestamp_raw().is_none());
1696
1697 let serialized = json!({
1700 "event": {
1701 "content": {"body": "secret", "msgtype": "m.text"},
1702 "event_id": "$xxxxx:example.org",
1703 "origin_server_ts": 2189,
1704 "room_id": "!someroom:example.com",
1705 "sender": "@carl:example.com",
1706 "type": "m.room.message",
1707 },
1708 "encryption_info": {
1709 "sender": "@sender:example.com",
1710 "sender_device": null,
1711 "algorithm_info": {
1712 "MegolmV1AesSha2": {
1713 "curve25519_key": "xxx",
1714 "sender_claimed_keys": {}
1715 }
1716 },
1717 "verification_state": "Verified",
1718 },
1719 "unsigned_encryption_info": {
1720 "RelationsReplace": {"UnableToDecrypt": {"session_id": "xyz"}}
1721 }
1722 });
1723 let event: TimelineEvent = serde_json::from_value(serialized).unwrap();
1724 assert_eq!(event.event_id(), Some(event_id!("$xxxxx:example.org").to_owned()));
1725 assert_matches!(
1726 event.encryption_info().unwrap().algorithm_info,
1727 AlgorithmInfo::MegolmV1AesSha2 { .. }
1728 );
1729 assert_eq!(event.timestamp(), Some(MilliSecondsSinceUnixEpoch(UInt::new_saturating(2189))));
1730 assert!(event.timestamp_raw().is_none());
1731 assert_matches!(event.kind, TimelineEventKind::Decrypted(decrypted) => {
1732 assert_matches!(decrypted.unsigned_encryption_info, Some(map) => {
1733 assert_eq!(map.len(), 1);
1734 let (location, result) = map.into_iter().next().unwrap();
1735 assert_eq!(location, UnsignedEventLocation::RelationsReplace);
1736 assert_matches!(result, UnsignedDecryptionResult::UnableToDecrypt(utd_info) => {
1737 assert_eq!(utd_info.session_id, Some("xyz".to_owned()));
1738 assert_eq!(utd_info.reason, UnableToDecryptReason::Unknown);
1739 })
1740 });
1741 });
1742 }
1743
1744 #[test]
1745 fn test_creating_or_deserializing_an_event_extracts_summary() {
1746 let event = json!({
1747 "event_id": "$eid:example.com",
1748 "type": "m.room.message",
1749 "sender": "@alice:example.com",
1750 "origin_server_ts": 42,
1751 "content": {
1752 "body": "Hello, world!",
1753 },
1754 "unsigned": {
1755 "m.relations": {
1756 "m.thread": {
1757 "latest_event": {
1758 "event_id": "$latest_event:example.com",
1759 "type": "m.room.message",
1760 "sender": "@bob:example.com",
1761 "origin_server_ts": 42,
1762 "content": {
1763 "body": "Hello to you too!",
1764 "msgtype": "m.text",
1765 }
1766 },
1767 "count": 2,
1768 "current_user_participated": true,
1769 }
1770 }
1771 }
1772 });
1773
1774 let raw = Raw::new(&event).unwrap().cast_unchecked();
1775
1776 let timeline_event = TimelineEvent::from_plaintext(raw);
1779 assert_matches!(timeline_event.thread_summary, ThreadSummaryStatus::Some(ThreadSummary { num_replies, latest_reply }) => {
1780 assert_eq!(num_replies, 2);
1781 assert_eq!(latest_reply.as_deref(), Some(event_id!("$latest_event:example.com")));
1782 });
1783
1784 assert!(timeline_event.bundled_latest_thread_event.is_some());
1785
1786 let serialized_timeline_item = json!({
1789 "kind": {
1790 "PlainText": {
1791 "event": event
1792 }
1793 }
1794 });
1795
1796 let timeline_event: TimelineEvent =
1797 serde_json::from_value(serialized_timeline_item).unwrap();
1798 assert_matches!(timeline_event.thread_summary, ThreadSummaryStatus::Unknown);
1799
1800 assert!(timeline_event.bundled_latest_thread_event.is_none());
1803 }
1804
1805 #[test]
1806 fn sync_timeline_event_deserialisation_migration_for_withheld() {
1807 let serialized = json!({
1824 "kind": {
1825 "UnableToDecrypt": {
1826 "event": {
1827 "content": {
1828 "algorithm": "m.megolm.v1.aes-sha2",
1829 "ciphertext": "AwgAEoABzL1JYhqhjW9jXrlT3M6H8mJ4qffYtOQOnPuAPNxsuG20oiD/Fnpv6jnQGhU6YbV9pNM+1mRnTvxW3CbWOPjLKqCWTJTc7Q0vDEVtYePg38ncXNcwMmfhgnNAoW9S7vNs8C003x3yUl6NeZ8bH+ci870BZL+kWM/lMl10tn6U7snNmSjnE3ckvRdO+11/R4//5VzFQpZdf4j036lNSls/WIiI67Fk9iFpinz9xdRVWJFVdrAiPFwb8L5xRZ8aX+e2JDMlc1eW8gk",
1830 "device_id": "SKCGPNUWAU",
1831 "sender_key": "Gim/c7uQdSXyrrUbmUOrBT6sMC0gO7QSLmOK6B7NOm0",
1832 "session_id": "hgLyeSqXfb8vc5AjQLsg6TSHVu0HJ7HZ4B6jgMvxkrs"
1833 },
1834 "event_id": "$xxxxx:example.org",
1835 "origin_server_ts": 2189,
1836 "room_id": "!someroom:example.com",
1837 "sender": "@carl:example.com",
1838 "type": "m.room.message"
1839 },
1840 "utd_info": {
1841 "reason": "MissingMegolmSession",
1842 "session_id": "session000"
1843 }
1844 }
1845 }
1846 });
1847
1848 let result = serde_json::from_value(serialized);
1849 assert!(result.is_ok());
1850
1851 let event: TimelineEvent = result.unwrap();
1853 assert_matches!(
1854 event.kind,
1855 TimelineEventKind::UnableToDecrypt { utd_info, .. }=> {
1856 assert_matches!(
1857 utd_info.reason,
1858 UnableToDecryptReason::MissingMegolmSession { withheld_code: None }
1859 );
1860 }
1861 )
1862 }
1863
1864 #[test]
1865 fn unable_to_decrypt_info_migration_for_withheld() {
1866 let old_format = json!({
1867 "reason": "MissingMegolmSession",
1868 "session_id": "session000"
1869 });
1870
1871 let deserialized = serde_json::from_value::<UnableToDecryptInfo>(old_format).unwrap();
1872 let session_id = Some("session000".to_owned());
1873
1874 assert_eq!(deserialized.session_id, session_id);
1875 assert_eq!(
1876 deserialized.reason,
1877 UnableToDecryptReason::MissingMegolmSession { withheld_code: None },
1878 );
1879
1880 let new_format = json!({
1881 "session_id": "session000",
1882 "reason": {
1883 "MissingMegolmSession": {
1884 "withheld_code": null
1885 }
1886 }
1887 });
1888
1889 let deserialized = serde_json::from_value::<UnableToDecryptInfo>(new_format).unwrap();
1890
1891 assert_eq!(
1892 deserialized.reason,
1893 UnableToDecryptReason::MissingMegolmSession { withheld_code: None },
1894 );
1895 assert_eq!(deserialized.session_id, session_id);
1896 }
1897
1898 #[test]
1899 fn unable_to_decrypt_reason_is_missing_room_key() {
1900 let reason = UnableToDecryptReason::MissingMegolmSession { withheld_code: None };
1901 assert!(reason.is_missing_room_key());
1902
1903 let reason = UnableToDecryptReason::MissingMegolmSession {
1904 withheld_code: Some(WithheldCode::Blacklisted),
1905 };
1906 assert!(!reason.is_missing_room_key());
1907
1908 let reason = UnableToDecryptReason::UnknownMegolmMessageIndex;
1909 assert!(reason.is_missing_room_key());
1910 }
1911
1912 #[test]
1913 fn snapshot_test_verification_level() {
1914 with_settings!({ prepend_module_to_snapshot => false }, {
1915 assert_json_snapshot!(VerificationLevel::VerificationViolation);
1916 assert_json_snapshot!(VerificationLevel::UnsignedDevice);
1917 assert_json_snapshot!(VerificationLevel::None(DeviceLinkProblem::InsecureSource));
1918 assert_json_snapshot!(VerificationLevel::None(DeviceLinkProblem::MissingDevice));
1919 assert_json_snapshot!(VerificationLevel::UnverifiedIdentity);
1920 });
1921 }
1922
1923 #[test]
1924 fn snapshot_test_verification_states() {
1925 with_settings!({ prepend_module_to_snapshot => false }, {
1926 assert_json_snapshot!(VerificationState::Unverified(VerificationLevel::UnsignedDevice));
1927 assert_json_snapshot!(VerificationState::Unverified(
1928 VerificationLevel::VerificationViolation
1929 ));
1930 assert_json_snapshot!(VerificationState::Unverified(VerificationLevel::None(
1931 DeviceLinkProblem::InsecureSource,
1932 )));
1933 assert_json_snapshot!(VerificationState::Unverified(VerificationLevel::None(
1934 DeviceLinkProblem::MissingDevice,
1935 )));
1936 assert_json_snapshot!(VerificationState::Verified);
1937 });
1938 }
1939
1940 #[test]
1941 fn snapshot_test_shield_states() {
1942 with_settings!({ prepend_module_to_snapshot => false }, {
1943 assert_json_snapshot!(ShieldState::None);
1944 assert_json_snapshot!(ShieldState::Red {
1945 code: ShieldStateCode::UnverifiedIdentity,
1946 message: "a message"
1947 });
1948 assert_json_snapshot!(ShieldState::Grey {
1949 code: ShieldStateCode::AuthenticityNotGuaranteed,
1950 message: "authenticity of this message cannot be guaranteed",
1951 });
1952 });
1953 }
1954
1955 #[test]
1956 fn snapshot_test_shield_codes() {
1957 with_settings!({ prepend_module_to_snapshot => false }, {
1958 assert_json_snapshot!(ShieldStateCode::AuthenticityNotGuaranteed);
1959 assert_json_snapshot!(ShieldStateCode::UnknownDevice);
1960 assert_json_snapshot!(ShieldStateCode::UnsignedDevice);
1961 assert_json_snapshot!(ShieldStateCode::UnverifiedIdentity);
1962 assert_json_snapshot!(ShieldStateCode::SentInClear);
1963 assert_json_snapshot!(ShieldStateCode::VerificationViolation);
1964 });
1965 }
1966
1967 #[test]
1968 fn snapshot_test_algorithm_info() {
1969 let mut map = BTreeMap::new();
1970 map.insert(DeviceKeyAlgorithm::Curve25519, "claimedclaimedcurve25519".to_owned());
1971 map.insert(DeviceKeyAlgorithm::Ed25519, "claimedclaimeded25519".to_owned());
1972 let info = AlgorithmInfo::MegolmV1AesSha2 {
1973 curve25519_key: "curvecurvecurve".into(),
1974 sender_claimed_keys: BTreeMap::from([
1975 (DeviceKeyAlgorithm::Curve25519, "claimedclaimedcurve25519".to_owned()),
1976 (DeviceKeyAlgorithm::Ed25519, "claimedclaimeded25519".to_owned()),
1977 ]),
1978 session_id: None,
1979 };
1980
1981 with_settings!({ prepend_module_to_snapshot => false }, {
1982 assert_json_snapshot!(info)
1983 });
1984 }
1985
1986 #[test]
1987 fn test_encryption_info_migration() {
1988 let old_format = json!({
1991 "sender": "@alice:localhost",
1992 "sender_device": "ABCDEFGH",
1993 "algorithm_info": {
1994 "MegolmV1AesSha2": {
1995 "curve25519_key": "curvecurvecurve",
1996 "sender_claimed_keys": {}
1997 }
1998 },
1999 "verification_state": "Verified",
2000 "session_id": "mysessionid76"
2001 });
2002
2003 let deserialized = serde_json::from_value::<EncryptionInfo>(old_format).unwrap();
2004 let expected_session_id = Some("mysessionid76".to_owned());
2005
2006 assert_let!(
2007 AlgorithmInfo::MegolmV1AesSha2 { session_id, .. } = deserialized.algorithm_info.clone()
2008 );
2009 assert_eq!(session_id, expected_session_id);
2010
2011 assert_json_snapshot!(deserialized);
2012 }
2013
2014 #[test]
2015 fn snapshot_test_encryption_info() {
2016 let info = EncryptionInfo {
2017 sender: user_id!("@alice:localhost").to_owned(),
2018 sender_device: Some(device_id!("ABCDEFGH").to_owned()),
2019 algorithm_info: AlgorithmInfo::MegolmV1AesSha2 {
2020 curve25519_key: "curvecurvecurve".into(),
2021 sender_claimed_keys: Default::default(),
2022 session_id: Some("mysessionid76".to_owned()),
2023 },
2024 verification_state: VerificationState::Verified,
2025 };
2026
2027 with_settings!({ sort_maps => true, prepend_module_to_snapshot => false }, {
2028 assert_json_snapshot!(info)
2029 })
2030 }
2031
2032 #[test]
2033 fn snapshot_test_sync_timeline_event() {
2034 let room_event = TimelineEvent {
2035 kind: TimelineEventKind::Decrypted(DecryptedRoomEvent {
2036 event: Raw::new(&example_event()).unwrap().cast_unchecked(),
2037 encryption_info: Arc::new(EncryptionInfo {
2038 sender: user_id!("@sender:example.com").to_owned(),
2039 sender_device: Some(device_id!("ABCDEFGHIJ").to_owned()),
2040 algorithm_info: AlgorithmInfo::MegolmV1AesSha2 {
2041 curve25519_key: "xxx".to_owned(),
2042 sender_claimed_keys: BTreeMap::from([
2043 (
2044 DeviceKeyAlgorithm::Ed25519,
2045 "I3YsPwqMZQXHkSQbjFNEs7b529uac2xBpI83eN3LUXo".to_owned(),
2046 ),
2047 (
2048 DeviceKeyAlgorithm::Curve25519,
2049 "qzdW3F5IMPFl0HQgz5w/L5Oi/npKUFn8Um84acIHfPY".to_owned(),
2050 ),
2051 ]),
2052 session_id: Some("mysessionid112".to_owned()),
2053 },
2054 verification_state: VerificationState::Verified,
2055 }),
2056 unsigned_encryption_info: Some(BTreeMap::from([(
2057 UnsignedEventLocation::RelationsThreadLatestEvent,
2058 UnsignedDecryptionResult::UnableToDecrypt(UnableToDecryptInfo {
2059 session_id: Some("xyz".to_owned()),
2060 reason: UnableToDecryptReason::MissingMegolmSession {
2061 withheld_code: Some(WithheldCode::Unverified),
2062 },
2063 }),
2064 )])),
2065 }),
2066 timestamp: Some(MilliSecondsSinceUnixEpoch(UInt::new_saturating(2189))),
2067 push_actions: Default::default(),
2068 thread_summary: ThreadSummaryStatus::Some(ThreadSummary {
2069 num_replies: 2,
2070 latest_reply: None,
2071 }),
2072 bundled_latest_thread_event: None,
2073 };
2074
2075 with_settings!({ sort_maps => true, prepend_module_to_snapshot => false }, {
2076 assert_json_snapshot! {
2079 serde_json::to_value(&room_event).unwrap(),
2080 }
2081 });
2082 }
2083}