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 pub fn event_type(&self) -> Option<String> {
958 self.raw().get_field("type").ok().flatten()
959 }
960}
961
962#[cfg(not(tarpaulin_include))]
963impl fmt::Debug for TimelineEventKind {
964 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
965 match &self {
966 Self::PlainText { event } => f
967 .debug_struct("TimelineEventDecryptionResult::PlainText")
968 .field("event", &DebugRawEvent(event))
969 .finish(),
970
971 Self::UnableToDecrypt { event, utd_info } => f
972 .debug_struct("TimelineEventDecryptionResult::UnableToDecrypt")
973 .field("event", &DebugRawEvent(event))
974 .field("utd_info", &utd_info)
975 .finish(),
976
977 Self::Decrypted(decrypted) => {
978 f.debug_tuple("TimelineEventDecryptionResult::Decrypted").field(decrypted).finish()
979 }
980 }
981 }
982}
983
984#[derive(Clone, Serialize, Deserialize)]
985pub struct DecryptedRoomEvent {
987 pub event: Raw<AnyTimelineEvent>,
995
996 pub encryption_info: Arc<EncryptionInfo>,
998
999 #[serde(skip_serializing_if = "Option::is_none")]
1004 pub unsigned_encryption_info: Option<BTreeMap<UnsignedEventLocation, UnsignedDecryptionResult>>,
1005}
1006
1007#[cfg(not(tarpaulin_include))]
1008impl fmt::Debug for DecryptedRoomEvent {
1009 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1010 let DecryptedRoomEvent { event, encryption_info, unsigned_encryption_info } = self;
1011
1012 f.debug_struct("DecryptedRoomEvent")
1013 .field("event", &DebugRawEvent(event))
1014 .field("encryption_info", encryption_info)
1015 .maybe_field("unsigned_encryption_info", unsigned_encryption_info)
1016 .finish()
1017 }
1018}
1019
1020#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
1022pub enum UnsignedEventLocation {
1023 RelationsReplace,
1026 RelationsThreadLatestEvent,
1029}
1030
1031impl UnsignedEventLocation {
1032 pub fn find_mut<'a>(&self, unsigned: &'a mut JsonObject) -> Option<&'a mut serde_json::Value> {
1039 let relations = unsigned.get_mut("m.relations")?.as_object_mut()?;
1040
1041 match self {
1042 Self::RelationsReplace => relations.get_mut("m.replace"),
1043 Self::RelationsThreadLatestEvent => {
1044 relations.get_mut("m.thread")?.as_object_mut()?.get_mut("latest_event")
1045 }
1046 }
1047 }
1048}
1049
1050#[derive(Debug, Clone, Serialize, Deserialize)]
1052pub enum UnsignedDecryptionResult {
1053 Decrypted(Arc<EncryptionInfo>),
1055 UnableToDecrypt(UnableToDecryptInfo),
1057}
1058
1059impl UnsignedDecryptionResult {
1060 pub fn encryption_info(&self) -> Option<&Arc<EncryptionInfo>> {
1063 match self {
1064 Self::Decrypted(info) => Some(info),
1065 Self::UnableToDecrypt(_) => None,
1066 }
1067 }
1068}
1069
1070#[derive(Debug, Clone, Serialize, Deserialize)]
1072pub struct UnableToDecryptInfo {
1073 #[serde(skip_serializing_if = "Option::is_none")]
1076 pub session_id: Option<String>,
1077
1078 #[serde(default = "unknown_utd_reason", deserialize_with = "deserialize_utd_reason")]
1080 pub reason: UnableToDecryptReason,
1081}
1082
1083fn unknown_utd_reason() -> UnableToDecryptReason {
1084 UnableToDecryptReason::Unknown
1085}
1086
1087pub fn deserialize_utd_reason<'de, D>(d: D) -> Result<UnableToDecryptReason, D::Error>
1090where
1091 D: serde::Deserializer<'de>,
1092{
1093 let v: serde_json::Value = Deserialize::deserialize(d)?;
1095 if v.as_str().is_some_and(|s| s == "MissingMegolmSession") {
1098 return Ok(UnableToDecryptReason::MissingMegolmSession { withheld_code: None });
1099 }
1100 serde_json::from_value::<UnableToDecryptReason>(v).map_err(serde::de::Error::custom)
1103}
1104
1105#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1107pub enum UnableToDecryptReason {
1108 #[doc(hidden)]
1111 Unknown,
1112
1113 MalformedEncryptedEvent,
1117
1118 MissingMegolmSession {
1121 withheld_code: Option<WithheldCode>,
1124 },
1125
1126 UnknownMegolmMessageIndex,
1129
1130 MegolmDecryptionFailure,
1137
1138 PayloadDeserializationFailure,
1140
1141 MismatchedIdentityKeys,
1145
1146 SenderIdentityNotTrusted(VerificationLevel),
1150
1151 #[cfg(feature = "experimental-encrypted-state-events")]
1154 StateKeyVerificationFailed,
1155}
1156
1157impl UnableToDecryptReason {
1158 pub fn is_missing_room_key(&self) -> bool {
1161 matches!(
1164 self,
1165 Self::MissingMegolmSession { withheld_code: None } | Self::UnknownMegolmMessageIndex
1166 )
1167 }
1168}
1169
1170#[derive(
1174 Clone,
1175 PartialEq,
1176 Eq,
1177 Hash,
1178 AsStrAsRefStr,
1179 AsRefStr,
1180 FromString,
1181 DebugAsRefStr,
1182 SerializeAsRefStr,
1183 DeserializeFromCowStr,
1184)]
1185pub enum WithheldCode {
1186 #[ruma_enum(rename = "m.blacklisted")]
1188 Blacklisted,
1189
1190 #[ruma_enum(rename = "m.unverified")]
1192 Unverified,
1193
1194 #[ruma_enum(rename = "m.unauthorised")]
1198 Unauthorised,
1199
1200 #[ruma_enum(rename = "m.unavailable")]
1203 Unavailable,
1204
1205 #[ruma_enum(rename = "m.no_olm")]
1209 NoOlm,
1210
1211 #[ruma_enum(rename = "io.element.msc4268.history_not_shared", alias = "m.history_not_shared")]
1216 HistoryNotShared,
1217
1218 #[doc(hidden)]
1219 _Custom(PrivOwnedStr),
1220}
1221
1222impl fmt::Display for WithheldCode {
1223 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
1224 let string = match self {
1225 WithheldCode::Blacklisted => "The sender has blocked you.",
1226 WithheldCode::Unverified => "The sender has disabled encrypting to unverified devices.",
1227 WithheldCode::Unauthorised => "You are not authorised to read the message.",
1228 WithheldCode::Unavailable => "The requested key was not found.",
1229 WithheldCode::NoOlm => "Unable to establish a secure channel.",
1230 WithheldCode::HistoryNotShared => "The sender disabled sharing encrypted history.",
1231 _ => self.as_str(),
1232 };
1233
1234 f.write_str(string)
1235 }
1236}
1237
1238#[doc(hidden)]
1242#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
1243pub struct PrivOwnedStr(pub Box<str>);
1244
1245#[cfg(not(tarpaulin_include))]
1246impl fmt::Debug for PrivOwnedStr {
1247 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1248 self.0.fmt(f)
1249 }
1250}
1251
1252#[derive(Debug, Deserialize)]
1257struct SyncTimelineEventDeserializationHelperV1 {
1258 kind: TimelineEventKind,
1260
1261 #[serde(default)]
1264 timestamp: Option<MilliSecondsSinceUnixEpoch>,
1265
1266 #[serde(default)]
1268 push_actions: Vec<Action>,
1269
1270 #[serde(default)]
1272 thread_summary: ThreadSummaryStatus,
1273}
1274
1275impl From<SyncTimelineEventDeserializationHelperV1> for TimelineEvent {
1276 fn from(value: SyncTimelineEventDeserializationHelperV1) -> Self {
1277 let SyncTimelineEventDeserializationHelperV1 {
1278 kind,
1279 timestamp,
1280 push_actions,
1281 thread_summary,
1282 } = value;
1283
1284 TimelineEvent {
1293 kind,
1294 timestamp,
1295 push_actions: Some(push_actions),
1296 thread_summary,
1297 bundled_latest_thread_event: None,
1299 }
1300 }
1301}
1302
1303#[derive(Deserialize)]
1305struct SyncTimelineEventDeserializationHelperV0 {
1306 event: Raw<AnySyncTimelineEvent>,
1308
1309 encryption_info: Option<Arc<EncryptionInfo>>,
1313
1314 #[serde(default)]
1316 push_actions: Vec<Action>,
1317
1318 unsigned_encryption_info: Option<BTreeMap<UnsignedEventLocation, UnsignedDecryptionResult>>,
1323}
1324
1325impl From<SyncTimelineEventDeserializationHelperV0> for TimelineEvent {
1326 fn from(value: SyncTimelineEventDeserializationHelperV0) -> Self {
1327 let SyncTimelineEventDeserializationHelperV0 {
1328 event,
1329 encryption_info,
1330 push_actions,
1331 unsigned_encryption_info,
1332 } = value;
1333
1334 let timestamp = None;
1341
1342 let kind = match encryption_info {
1343 Some(encryption_info) => {
1344 TimelineEventKind::Decrypted(DecryptedRoomEvent {
1345 event: event.cast_unchecked(),
1352 encryption_info,
1353 unsigned_encryption_info,
1354 })
1355 }
1356
1357 None => TimelineEventKind::PlainText { event },
1358 };
1359
1360 TimelineEvent {
1361 kind,
1362 timestamp,
1363 push_actions: Some(push_actions),
1364 thread_summary: ThreadSummaryStatus::Unknown,
1366 bundled_latest_thread_event: None,
1368 }
1369 }
1370}
1371
1372#[derive(Debug, Clone, PartialEq)]
1374pub enum ToDeviceUnableToDecryptReason {
1375 DecryptionFailure,
1378
1379 UnverifiedSenderDevice,
1383
1384 NoOlmMachine,
1387
1388 EncryptionIsDisabled,
1390}
1391
1392#[derive(Clone, Debug)]
1394pub struct ToDeviceUnableToDecryptInfo {
1395 pub reason: ToDeviceUnableToDecryptReason,
1397}
1398
1399#[derive(Clone, Debug)]
1401pub enum ProcessedToDeviceEvent {
1402 Decrypted {
1405 raw: Raw<AnyToDeviceEvent>,
1407 encryption_info: EncryptionInfo,
1409 },
1410
1411 UnableToDecrypt {
1413 encrypted_event: Raw<AnyToDeviceEvent>,
1414 utd_info: ToDeviceUnableToDecryptInfo,
1415 },
1416
1417 PlainText(Raw<AnyToDeviceEvent>),
1419
1420 Invalid(Raw<AnyToDeviceEvent>),
1424}
1425
1426impl ProcessedToDeviceEvent {
1427 pub fn to_raw(&self) -> Raw<AnyToDeviceEvent> {
1430 match self {
1431 ProcessedToDeviceEvent::Decrypted { raw, .. } => raw.clone(),
1432 ProcessedToDeviceEvent::UnableToDecrypt { encrypted_event, .. } => {
1433 encrypted_event.clone()
1434 }
1435 ProcessedToDeviceEvent::PlainText(event) => event.clone(),
1436 ProcessedToDeviceEvent::Invalid(event) => event.clone(),
1437 }
1438 }
1439
1440 pub fn as_raw(&self) -> &Raw<AnyToDeviceEvent> {
1442 match self {
1443 ProcessedToDeviceEvent::Decrypted { raw, .. } => raw,
1444 ProcessedToDeviceEvent::UnableToDecrypt { encrypted_event, .. } => encrypted_event,
1445 ProcessedToDeviceEvent::PlainText(event) => event,
1446 ProcessedToDeviceEvent::Invalid(event) => event,
1447 }
1448 }
1449}
1450
1451#[cfg(test)]
1452mod tests {
1453 use std::{collections::BTreeMap, sync::Arc};
1454
1455 use assert_matches::assert_matches;
1456 use assert_matches2::assert_let;
1457 use insta::{assert_json_snapshot, with_settings};
1458 use ruma::{
1459 DeviceKeyAlgorithm, MilliSecondsSinceUnixEpoch, UInt, device_id, event_id,
1460 events::room::message::RoomMessageEventContent, serde::Raw, user_id,
1461 };
1462 use serde::Deserialize;
1463 use serde_json::json;
1464
1465 use super::{
1466 AlgorithmInfo, DecryptedRoomEvent, DeviceLinkProblem, EncryptionInfo, ShieldState,
1467 ShieldStateCode, TimelineEvent, TimelineEventKind, UnableToDecryptInfo,
1468 UnableToDecryptReason, UnsignedDecryptionResult, UnsignedEventLocation, VerificationLevel,
1469 VerificationState, WithheldCode,
1470 };
1471 use crate::deserialized_responses::{ThreadSummary, ThreadSummaryStatus};
1472
1473 fn example_event() -> serde_json::Value {
1474 json!({
1475 "content": RoomMessageEventContent::text_plain("secret"),
1476 "type": "m.room.message",
1477 "event_id": "$xxxxx:example.org",
1478 "room_id": "!someroom:example.com",
1479 "origin_server_ts": 2189,
1480 "sender": "@carl:example.com",
1481 })
1482 }
1483
1484 #[test]
1485 fn sync_timeline_debug_content() {
1486 let room_event =
1487 TimelineEvent::from_plaintext(Raw::new(&example_event()).unwrap().cast_unchecked());
1488 let debug_s = format!("{room_event:?}");
1489 assert!(
1490 !debug_s.contains("secret"),
1491 "Debug representation contains event content!\n{debug_s}"
1492 );
1493 }
1494
1495 #[test]
1496 fn old_verification_state_to_new_migration() {
1497 #[derive(Deserialize)]
1498 struct State {
1499 state: VerificationState,
1500 }
1501
1502 let state = json!({
1503 "state": "Trusted",
1504 });
1505 let deserialized: State =
1506 serde_json::from_value(state).expect("We can deserialize the old trusted value");
1507 assert_eq!(deserialized.state, VerificationState::Verified);
1508
1509 let state = json!({
1510 "state": "UnknownDevice",
1511 });
1512
1513 let deserialized: State =
1514 serde_json::from_value(state).expect("We can deserialize the old unknown device value");
1515
1516 assert_eq!(
1517 deserialized.state,
1518 VerificationState::Unverified(VerificationLevel::None(
1519 DeviceLinkProblem::MissingDevice
1520 ))
1521 );
1522
1523 let state = json!({
1524 "state": "Untrusted",
1525 });
1526 let deserialized: State =
1527 serde_json::from_value(state).expect("We can deserialize the old trusted value");
1528
1529 assert_eq!(
1530 deserialized.state,
1531 VerificationState::Unverified(VerificationLevel::UnsignedDevice)
1532 );
1533 }
1534
1535 #[test]
1536 fn test_verification_level_deserializes() {
1537 #[derive(Deserialize)]
1539 struct Container {
1540 verification_level: VerificationLevel,
1541 }
1542 let container = json!({ "verification_level": "VerificationViolation" });
1543
1544 let deserialized: Container = serde_json::from_value(container)
1546 .expect("We can deserialize the old PreviouslyVerified value");
1547
1548 assert_eq!(deserialized.verification_level, VerificationLevel::VerificationViolation);
1550 }
1551
1552 #[test]
1553 fn test_verification_level_deserializes_from_old_previously_verified_value() {
1554 #[derive(Deserialize)]
1556 struct Container {
1557 verification_level: VerificationLevel,
1558 }
1559 let container = json!({ "verification_level": "PreviouslyVerified" });
1560
1561 let deserialized: Container = serde_json::from_value(container)
1563 .expect("We can deserialize the old PreviouslyVerified value");
1564
1565 assert_eq!(deserialized.verification_level, VerificationLevel::VerificationViolation);
1567 }
1568
1569 #[test]
1570 fn test_shield_state_code_deserializes() {
1571 #[derive(Deserialize)]
1573 struct Container {
1574 shield_state_code: ShieldStateCode,
1575 }
1576 let container = json!({ "shield_state_code": "VerificationViolation" });
1577
1578 let deserialized: Container = serde_json::from_value(container)
1580 .expect("We can deserialize the old PreviouslyVerified value");
1581
1582 assert_eq!(deserialized.shield_state_code, ShieldStateCode::VerificationViolation);
1584 }
1585
1586 #[test]
1587 fn test_shield_state_code_deserializes_from_old_previously_verified_value() {
1588 #[derive(Deserialize)]
1590 struct Container {
1591 shield_state_code: ShieldStateCode,
1592 }
1593 let container = json!({ "shield_state_code": "PreviouslyVerified" });
1594
1595 let deserialized: Container = serde_json::from_value(container)
1597 .expect("We can deserialize the old PreviouslyVerified value");
1598
1599 assert_eq!(deserialized.shield_state_code, ShieldStateCode::VerificationViolation);
1601 }
1602
1603 #[test]
1604 fn sync_timeline_event_serialisation() {
1605 let room_event = TimelineEvent {
1606 kind: TimelineEventKind::Decrypted(DecryptedRoomEvent {
1607 event: Raw::new(&example_event()).unwrap().cast_unchecked(),
1608 encryption_info: Arc::new(EncryptionInfo {
1609 sender: user_id!("@sender:example.com").to_owned(),
1610 sender_device: None,
1611 algorithm_info: AlgorithmInfo::MegolmV1AesSha2 {
1612 curve25519_key: "xxx".to_owned(),
1613 sender_claimed_keys: Default::default(),
1614 session_id: Some("xyz".to_owned()),
1615 },
1616 verification_state: VerificationState::Verified,
1617 }),
1618 unsigned_encryption_info: Some(BTreeMap::from([(
1619 UnsignedEventLocation::RelationsReplace,
1620 UnsignedDecryptionResult::UnableToDecrypt(UnableToDecryptInfo {
1621 session_id: Some("xyz".to_owned()),
1622 reason: UnableToDecryptReason::MalformedEncryptedEvent,
1623 }),
1624 )])),
1625 }),
1626 timestamp: Some(MilliSecondsSinceUnixEpoch(UInt::new_saturating(2189))),
1627 push_actions: Default::default(),
1628 thread_summary: ThreadSummaryStatus::Unknown,
1629 bundled_latest_thread_event: None,
1630 };
1631
1632 let serialized = serde_json::to_value(&room_event).unwrap();
1633
1634 assert_eq!(
1636 serialized,
1637 json!({
1638 "kind": {
1639 "Decrypted": {
1640 "event": {
1641 "content": {"body": "secret", "msgtype": "m.text"},
1642 "event_id": "$xxxxx:example.org",
1643 "origin_server_ts": 2189,
1644 "room_id": "!someroom:example.com",
1645 "sender": "@carl:example.com",
1646 "type": "m.room.message",
1647 },
1648 "encryption_info": {
1649 "sender": "@sender:example.com",
1650 "sender_device": null,
1651 "algorithm_info": {
1652 "MegolmV1AesSha2": {
1653 "curve25519_key": "xxx",
1654 "sender_claimed_keys": {},
1655 "session_id": "xyz",
1656 }
1657 },
1658 "verification_state": "Verified",
1659 },
1660 "unsigned_encryption_info": {
1661 "RelationsReplace": {"UnableToDecrypt": {
1662 "session_id": "xyz",
1663 "reason": "MalformedEncryptedEvent",
1664 }}
1665 }
1666 }
1667 },
1668 "timestamp": 2189,
1669 })
1670 );
1671
1672 let event: TimelineEvent = serde_json::from_value(serialized).unwrap();
1674 assert_eq!(event.event_id(), Some(event_id!("$xxxxx:example.org").to_owned()));
1675 assert_matches!(
1676 event.encryption_info().unwrap().algorithm_info,
1677 AlgorithmInfo::MegolmV1AesSha2 { .. }
1678 );
1679 assert_eq!(event.timestamp(), Some(MilliSecondsSinceUnixEpoch(UInt::new_saturating(2189))));
1680 assert_eq!(event.timestamp(), event.timestamp_raw());
1681
1682 let serialized = json!({
1684 "event": {
1685 "content": {"body": "secret", "msgtype": "m.text"},
1686 "event_id": "$xxxxx:example.org",
1687 "origin_server_ts": 2189,
1688 "room_id": "!someroom:example.com",
1689 "sender": "@carl:example.com",
1690 "type": "m.room.message",
1691 },
1692 "encryption_info": {
1693 "sender": "@sender:example.com",
1694 "sender_device": null,
1695 "algorithm_info": {
1696 "MegolmV1AesSha2": {
1697 "curve25519_key": "xxx",
1698 "sender_claimed_keys": {}
1699 }
1700 },
1701 "verification_state": "Verified",
1702 },
1703 });
1704 let event: TimelineEvent = serde_json::from_value(serialized).unwrap();
1705 assert_eq!(event.event_id(), Some(event_id!("$xxxxx:example.org").to_owned()));
1706 assert_matches!(
1707 event.encryption_info().unwrap().algorithm_info,
1708 AlgorithmInfo::MegolmV1AesSha2 { session_id: None, .. }
1709 );
1710 assert_eq!(event.timestamp(), Some(MilliSecondsSinceUnixEpoch(UInt::new_saturating(2189))));
1711 assert!(event.timestamp_raw().is_none());
1712
1713 let serialized = json!({
1716 "event": {
1717 "content": {"body": "secret", "msgtype": "m.text"},
1718 "event_id": "$xxxxx:example.org",
1719 "origin_server_ts": 2189,
1720 "room_id": "!someroom:example.com",
1721 "sender": "@carl:example.com",
1722 "type": "m.room.message",
1723 },
1724 "encryption_info": {
1725 "sender": "@sender:example.com",
1726 "sender_device": null,
1727 "algorithm_info": {
1728 "MegolmV1AesSha2": {
1729 "curve25519_key": "xxx",
1730 "sender_claimed_keys": {}
1731 }
1732 },
1733 "verification_state": "Verified",
1734 },
1735 "unsigned_encryption_info": {
1736 "RelationsReplace": {"UnableToDecrypt": {"session_id": "xyz"}}
1737 }
1738 });
1739 let event: TimelineEvent = serde_json::from_value(serialized).unwrap();
1740 assert_eq!(event.event_id(), Some(event_id!("$xxxxx:example.org").to_owned()));
1741 assert_matches!(
1742 event.encryption_info().unwrap().algorithm_info,
1743 AlgorithmInfo::MegolmV1AesSha2 { .. }
1744 );
1745 assert_eq!(event.timestamp(), Some(MilliSecondsSinceUnixEpoch(UInt::new_saturating(2189))));
1746 assert!(event.timestamp_raw().is_none());
1747 assert_matches!(event.kind, TimelineEventKind::Decrypted(decrypted) => {
1748 assert_matches!(decrypted.unsigned_encryption_info, Some(map) => {
1749 assert_eq!(map.len(), 1);
1750 let (location, result) = map.into_iter().next().unwrap();
1751 assert_eq!(location, UnsignedEventLocation::RelationsReplace);
1752 assert_matches!(result, UnsignedDecryptionResult::UnableToDecrypt(utd_info) => {
1753 assert_eq!(utd_info.session_id, Some("xyz".to_owned()));
1754 assert_eq!(utd_info.reason, UnableToDecryptReason::Unknown);
1755 })
1756 });
1757 });
1758 }
1759
1760 #[test]
1761 fn test_creating_or_deserializing_an_event_extracts_summary() {
1762 let event = json!({
1763 "event_id": "$eid:example.com",
1764 "type": "m.room.message",
1765 "sender": "@alice:example.com",
1766 "origin_server_ts": 42,
1767 "content": {
1768 "body": "Hello, world!",
1769 },
1770 "unsigned": {
1771 "m.relations": {
1772 "m.thread": {
1773 "latest_event": {
1774 "event_id": "$latest_event:example.com",
1775 "type": "m.room.message",
1776 "sender": "@bob:example.com",
1777 "origin_server_ts": 42,
1778 "content": {
1779 "body": "Hello to you too!",
1780 "msgtype": "m.text",
1781 }
1782 },
1783 "count": 2,
1784 "current_user_participated": true,
1785 }
1786 }
1787 }
1788 });
1789
1790 let raw = Raw::new(&event).unwrap().cast_unchecked();
1791
1792 let timeline_event = TimelineEvent::from_plaintext(raw);
1795 assert_matches!(timeline_event.thread_summary, ThreadSummaryStatus::Some(ThreadSummary { num_replies, latest_reply }) => {
1796 assert_eq!(num_replies, 2);
1797 assert_eq!(latest_reply.as_deref(), Some(event_id!("$latest_event:example.com")));
1798 });
1799
1800 assert!(timeline_event.bundled_latest_thread_event.is_some());
1801
1802 let serialized_timeline_item = json!({
1805 "kind": {
1806 "PlainText": {
1807 "event": event
1808 }
1809 }
1810 });
1811
1812 let timeline_event: TimelineEvent =
1813 serde_json::from_value(serialized_timeline_item).unwrap();
1814 assert_matches!(timeline_event.thread_summary, ThreadSummaryStatus::Unknown);
1815
1816 assert!(timeline_event.bundled_latest_thread_event.is_none());
1819 }
1820
1821 #[test]
1822 fn sync_timeline_event_deserialisation_migration_for_withheld() {
1823 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::SentInClear);
1979 assert_json_snapshot!(ShieldStateCode::VerificationViolation);
1980 });
1981 }
1982
1983 #[test]
1984 fn snapshot_test_algorithm_info() {
1985 let mut map = BTreeMap::new();
1986 map.insert(DeviceKeyAlgorithm::Curve25519, "claimedclaimedcurve25519".to_owned());
1987 map.insert(DeviceKeyAlgorithm::Ed25519, "claimedclaimeded25519".to_owned());
1988 let info = AlgorithmInfo::MegolmV1AesSha2 {
1989 curve25519_key: "curvecurvecurve".into(),
1990 sender_claimed_keys: BTreeMap::from([
1991 (DeviceKeyAlgorithm::Curve25519, "claimedclaimedcurve25519".to_owned()),
1992 (DeviceKeyAlgorithm::Ed25519, "claimedclaimeded25519".to_owned()),
1993 ]),
1994 session_id: None,
1995 };
1996
1997 with_settings!({ prepend_module_to_snapshot => false }, {
1998 assert_json_snapshot!(info)
1999 });
2000 }
2001
2002 #[test]
2003 fn test_encryption_info_migration() {
2004 let old_format = json!({
2007 "sender": "@alice:localhost",
2008 "sender_device": "ABCDEFGH",
2009 "algorithm_info": {
2010 "MegolmV1AesSha2": {
2011 "curve25519_key": "curvecurvecurve",
2012 "sender_claimed_keys": {}
2013 }
2014 },
2015 "verification_state": "Verified",
2016 "session_id": "mysessionid76"
2017 });
2018
2019 let deserialized = serde_json::from_value::<EncryptionInfo>(old_format).unwrap();
2020 let expected_session_id = Some("mysessionid76".to_owned());
2021
2022 assert_let!(
2023 AlgorithmInfo::MegolmV1AesSha2 { session_id, .. } = deserialized.algorithm_info.clone()
2024 );
2025 assert_eq!(session_id, expected_session_id);
2026
2027 assert_json_snapshot!(deserialized);
2028 }
2029
2030 #[test]
2031 fn snapshot_test_encryption_info() {
2032 let info = EncryptionInfo {
2033 sender: user_id!("@alice:localhost").to_owned(),
2034 sender_device: Some(device_id!("ABCDEFGH").to_owned()),
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 room_event = TimelineEvent {
2051 kind: TimelineEventKind::Decrypted(DecryptedRoomEvent {
2052 event: Raw::new(&example_event()).unwrap().cast_unchecked(),
2053 encryption_info: Arc::new(EncryptionInfo {
2054 sender: user_id!("@sender:example.com").to_owned(),
2055 sender_device: Some(device_id!("ABCDEFGHIJ").to_owned()),
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 timestamp: Some(MilliSecondsSinceUnixEpoch(UInt::new_saturating(2189))),
2083 push_actions: Default::default(),
2084 thread_summary: ThreadSummaryStatus::Some(ThreadSummary {
2085 num_replies: 2,
2086 latest_reply: None,
2087 }),
2088 bundled_latest_thread_event: None,
2089 };
2090
2091 with_settings!({ sort_maps => true, prepend_module_to_snapshot => false }, {
2092 assert_json_snapshot! {
2095 serde_json::to_value(&room_event).unwrap(),
2096 }
2097 });
2098 }
2099}