1use std::{collections::BTreeMap, fmt};
16
17#[cfg(doc)]
18use ruma::events::AnyTimelineEvent;
19use ruma::{
20 events::{AnyMessageLikeEvent, AnySyncTimelineEvent},
21 push::Action,
22 serde::{
23 AsRefStr, AsStrAsRefStr, DebugAsRefStr, DeserializeFromCowStr, FromString, JsonObject, Raw,
24 SerializeAsRefStr,
25 },
26 DeviceKeyAlgorithm, OwnedDeviceId, OwnedEventId, OwnedUserId,
27};
28use serde::{Deserialize, Serialize};
29#[cfg(target_arch = "wasm32")]
30use wasm_bindgen::prelude::*;
31
32use crate::debug::{DebugRawEvent, DebugStructExt};
33
34const AUTHENTICITY_NOT_GUARANTEED: &str =
35 "The authenticity of this encrypted message can't be guaranteed on this device.";
36const UNVERIFIED_IDENTITY: &str = "Encrypted by an unverified user.";
37const VERIFICATION_VIOLATION: &str =
38 "Encrypted by a previously-verified user who is no longer verified.";
39const UNSIGNED_DEVICE: &str = "Encrypted by a device not verified by its owner.";
40const UNKNOWN_DEVICE: &str = "Encrypted by an unknown or deleted device.";
41pub const SENT_IN_CLEAR: &str = "Not encrypted.";
42
43#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
46#[serde(from = "OldVerificationStateHelper")]
47pub enum VerificationState {
48 Verified,
53
54 Unverified(VerificationLevel),
59}
60
61#[derive(Clone, Debug, Deserialize)]
64enum OldVerificationStateHelper {
65 Untrusted,
66 UnknownDevice,
67 #[serde(alias = "Trusted")]
68 Verified,
69 Unverified(VerificationLevel),
70}
71
72impl From<OldVerificationStateHelper> for VerificationState {
73 fn from(value: OldVerificationStateHelper) -> Self {
74 match value {
75 OldVerificationStateHelper::Untrusted => {
78 VerificationState::Unverified(VerificationLevel::UnsignedDevice)
79 }
80 OldVerificationStateHelper::UnknownDevice => {
81 Self::Unverified(VerificationLevel::None(DeviceLinkProblem::MissingDevice))
82 }
83 OldVerificationStateHelper::Verified => Self::Verified,
84 OldVerificationStateHelper::Unverified(l) => Self::Unverified(l),
85 }
86 }
87}
88
89impl VerificationState {
90 pub fn to_shield_state_strict(&self) -> ShieldState {
97 match self {
98 VerificationState::Verified => ShieldState::None,
99 VerificationState::Unverified(level) => match level {
100 VerificationLevel::UnverifiedIdentity
101 | VerificationLevel::VerificationViolation
102 | VerificationLevel::UnsignedDevice => ShieldState::Red {
103 code: ShieldStateCode::UnverifiedIdentity,
104 message: UNVERIFIED_IDENTITY,
105 },
106 VerificationLevel::None(link) => match link {
107 DeviceLinkProblem::MissingDevice => ShieldState::Red {
108 code: ShieldStateCode::UnknownDevice,
109 message: UNKNOWN_DEVICE,
110 },
111 DeviceLinkProblem::InsecureSource => ShieldState::Red {
112 code: ShieldStateCode::AuthenticityNotGuaranteed,
113 message: AUTHENTICITY_NOT_GUARANTEED,
114 },
115 },
116 },
117 }
118 }
119
120 pub fn to_shield_state_lax(&self) -> ShieldState {
128 match self {
129 VerificationState::Verified => ShieldState::None,
130 VerificationState::Unverified(level) => match level {
131 VerificationLevel::UnverifiedIdentity => {
132 ShieldState::None
135 }
136 VerificationLevel::VerificationViolation => {
137 ShieldState::Red {
140 code: ShieldStateCode::VerificationViolation,
141 message: VERIFICATION_VIOLATION,
142 }
143 }
144 VerificationLevel::UnsignedDevice => {
145 ShieldState::Red {
147 code: ShieldStateCode::UnsignedDevice,
148 message: UNSIGNED_DEVICE,
149 }
150 }
151 VerificationLevel::None(link) => match link {
152 DeviceLinkProblem::MissingDevice => {
153 ShieldState::Red {
157 code: ShieldStateCode::UnknownDevice,
158 message: UNKNOWN_DEVICE,
159 }
160 }
161 DeviceLinkProblem::InsecureSource => {
162 ShieldState::Grey {
165 code: ShieldStateCode::AuthenticityNotGuaranteed,
166 message: AUTHENTICITY_NOT_GUARANTEED,
167 }
168 }
169 },
170 },
171 }
172 }
173}
174
175#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
178pub enum VerificationLevel {
179 UnverifiedIdentity,
181
182 #[serde(alias = "PreviouslyVerified")]
185 VerificationViolation,
186
187 UnsignedDevice,
190
191 None(DeviceLinkProblem),
197}
198
199impl fmt::Display for VerificationLevel {
200 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
201 let display = match self {
202 VerificationLevel::UnverifiedIdentity => "The sender's identity was not verified",
203 VerificationLevel::VerificationViolation => {
204 "The sender's identity was previously verified but has changed"
205 }
206 VerificationLevel::UnsignedDevice => {
207 "The sending device was not signed by the user's identity"
208 }
209 VerificationLevel::None(..) => "The sending device is not known",
210 };
211 write!(f, "{}", display)
212 }
213}
214
215#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
218pub enum DeviceLinkProblem {
219 MissingDevice,
223 InsecureSource,
226}
227
228#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq)]
231pub enum ShieldState {
232 Red {
235 code: ShieldStateCode,
237 message: &'static str,
239 },
240 Grey {
243 code: ShieldStateCode,
245 message: &'static str,
247 },
248 None,
250}
251
252#[derive(Clone, Copy, Debug, Deserialize, Serialize, Eq, PartialEq)]
254#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
255#[cfg_attr(target_arch = "wasm32", wasm_bindgen)]
256pub enum ShieldStateCode {
257 AuthenticityNotGuaranteed,
259 UnknownDevice,
261 UnsignedDevice,
263 UnverifiedIdentity,
265 SentInClear,
267 #[serde(alias = "PreviouslyVerified")]
269 VerificationViolation,
270}
271
272#[derive(Clone, Debug, Deserialize, Serialize)]
274pub enum AlgorithmInfo {
275 MegolmV1AesSha2 {
277 curve25519_key: String,
280 sender_claimed_keys: BTreeMap<DeviceKeyAlgorithm, String>,
284
285 #[serde(default, skip_serializing_if = "Option::is_none")]
288 session_id: Option<String>,
289 },
290}
291
292#[derive(Clone, Debug, Serialize)]
294pub struct EncryptionInfo {
295 pub sender: OwnedUserId,
298 pub sender_device: Option<OwnedDeviceId>,
301 pub algorithm_info: AlgorithmInfo,
303 pub verification_state: VerificationState,
310}
311
312impl EncryptionInfo {
313 pub fn session_id(&self) -> Option<&str> {
315 let AlgorithmInfo::MegolmV1AesSha2 { session_id, .. } = &self.algorithm_info;
316 session_id.as_deref()
317 }
318}
319
320impl<'de> Deserialize<'de> for EncryptionInfo {
321 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
322 where
323 D: serde::Deserializer<'de>,
324 {
325 #[derive(Deserialize)]
328 struct Helper {
329 pub sender: OwnedUserId,
330 pub sender_device: Option<OwnedDeviceId>,
331 pub algorithm_info: AlgorithmInfo,
332 pub verification_state: VerificationState,
333 #[serde(rename = "session_id")]
334 pub old_session_id: Option<String>,
335 }
336
337 let Helper {
338 sender,
339 sender_device,
340 algorithm_info:
341 AlgorithmInfo::MegolmV1AesSha2 { curve25519_key, sender_claimed_keys, session_id },
342 verification_state,
343 old_session_id,
344 } = Helper::deserialize(deserializer)?;
345
346 Ok(EncryptionInfo {
347 sender,
348 sender_device,
349 algorithm_info: AlgorithmInfo::MegolmV1AesSha2 {
350 session_id: session_id.or(old_session_id),
352 curve25519_key,
353 sender_claimed_keys,
354 },
355 verification_state,
356 })
357 }
358}
359
360#[derive(Clone, Debug, Serialize)]
383pub struct TimelineEvent {
384 pub kind: TimelineEventKind,
386
387 #[serde(skip_serializing_if = "Option::is_none")]
391 pub push_actions: Option<Vec<Action>>,
392}
393
394#[cfg(not(feature = "test-send-sync"))]
396unsafe impl Send for TimelineEvent {}
397
398#[cfg(not(feature = "test-send-sync"))]
400unsafe impl Sync for TimelineEvent {}
401
402#[cfg(feature = "test-send-sync")]
403#[test]
404fn test_send_sync_for_sync_timeline_event() {
406 fn assert_send_sync<T: Send + Sync>() {}
407
408 assert_send_sync::<TimelineEvent>();
409}
410
411impl TimelineEvent {
412 pub fn new(event: Raw<AnySyncTimelineEvent>) -> Self {
417 Self { kind: TimelineEventKind::PlainText { event }, push_actions: None }
418 }
419
420 pub fn new_with_push_actions(
426 event: Raw<AnySyncTimelineEvent>,
427 push_actions: Vec<Action>,
428 ) -> Self {
429 Self { kind: TimelineEventKind::PlainText { event }, push_actions: Some(push_actions) }
430 }
431
432 pub fn new_utd_event(event: Raw<AnySyncTimelineEvent>, utd_info: UnableToDecryptInfo) -> Self {
435 Self { kind: TimelineEventKind::UnableToDecrypt { event, utd_info }, push_actions: None }
436 }
437
438 pub fn event_id(&self) -> Option<OwnedEventId> {
441 self.kind.event_id()
442 }
443
444 pub fn raw(&self) -> &Raw<AnySyncTimelineEvent> {
447 self.kind.raw()
448 }
449
450 pub fn replace_raw(&mut self, replacement: Raw<AnyMessageLikeEvent>) {
452 match &mut self.kind {
453 TimelineEventKind::Decrypted(decrypted) => decrypted.event = replacement,
454 TimelineEventKind::UnableToDecrypt { event, .. }
455 | TimelineEventKind::PlainText { event } => {
456 *event = replacement.cast();
459 }
460 }
461 }
462
463 pub fn encryption_info(&self) -> Option<&EncryptionInfo> {
466 self.kind.encryption_info()
467 }
468
469 pub fn into_raw(self) -> Raw<AnySyncTimelineEvent> {
472 self.kind.into_raw()
473 }
474}
475
476impl From<DecryptedRoomEvent> for TimelineEvent {
477 fn from(decrypted: DecryptedRoomEvent) -> Self {
478 Self { kind: TimelineEventKind::Decrypted(decrypted), push_actions: None }
479 }
480}
481
482impl<'de> Deserialize<'de> for TimelineEvent {
483 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
491 where
492 D: serde::Deserializer<'de>,
493 {
494 use serde_json::{Map, Value};
495
496 let value = Map::<String, Value>::deserialize(deserializer)?;
498
499 if value.contains_key("event") {
501 let v0: SyncTimelineEventDeserializationHelperV0 =
502 serde_json::from_value(Value::Object(value)).map_err(|e| {
503 serde::de::Error::custom(format!(
504 "Unable to deserialize V0-format TimelineEvent: {}",
505 e
506 ))
507 })?;
508 Ok(v0.into())
509 }
510 else {
512 let v1: SyncTimelineEventDeserializationHelperV1 =
513 serde_json::from_value(Value::Object(value)).map_err(|e| {
514 serde::de::Error::custom(format!(
515 "Unable to deserialize V1-format TimelineEvent: {}",
516 e
517 ))
518 })?;
519 Ok(v1.into())
520 }
521 }
522}
523
524#[derive(Clone, Serialize, Deserialize)]
526pub enum TimelineEventKind {
527 Decrypted(DecryptedRoomEvent),
529
530 UnableToDecrypt {
532 event: Raw<AnySyncTimelineEvent>,
536
537 utd_info: UnableToDecryptInfo,
539 },
540
541 PlainText {
543 event: Raw<AnySyncTimelineEvent>,
547 },
548}
549
550impl TimelineEventKind {
551 pub fn raw(&self) -> &Raw<AnySyncTimelineEvent> {
554 match self {
555 TimelineEventKind::Decrypted(d) => d.event.cast_ref(),
561 TimelineEventKind::UnableToDecrypt { event, .. } => event.cast_ref(),
562 TimelineEventKind::PlainText { event } => event,
563 }
564 }
565
566 pub fn event_id(&self) -> Option<OwnedEventId> {
569 self.raw().get_field::<OwnedEventId>("event_id").ok().flatten()
570 }
571
572 pub fn encryption_info(&self) -> Option<&EncryptionInfo> {
575 match self {
576 TimelineEventKind::Decrypted(d) => Some(&d.encryption_info),
577 TimelineEventKind::UnableToDecrypt { .. } | TimelineEventKind::PlainText { .. } => None,
578 }
579 }
580
581 pub fn unsigned_encryption_map(
584 &self,
585 ) -> Option<&BTreeMap<UnsignedEventLocation, UnsignedDecryptionResult>> {
586 match self {
587 TimelineEventKind::Decrypted(d) => d.unsigned_encryption_info.as_ref(),
588 TimelineEventKind::UnableToDecrypt { .. } | TimelineEventKind::PlainText { .. } => None,
589 }
590 }
591
592 pub fn into_raw(self) -> Raw<AnySyncTimelineEvent> {
595 match self {
596 TimelineEventKind::Decrypted(d) => d.event.cast(),
602 TimelineEventKind::UnableToDecrypt { event, .. } => event.cast(),
603 TimelineEventKind::PlainText { event } => event,
604 }
605 }
606
607 pub fn session_id(&self) -> Option<&str> {
610 match self {
611 TimelineEventKind::Decrypted(decrypted_room_event) => {
612 decrypted_room_event.encryption_info.session_id()
613 }
614 TimelineEventKind::UnableToDecrypt { utd_info, .. } => utd_info.session_id.as_deref(),
615 TimelineEventKind::PlainText { .. } => None,
616 }
617 }
618}
619
620#[cfg(not(tarpaulin_include))]
621impl fmt::Debug for TimelineEventKind {
622 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
623 match &self {
624 Self::PlainText { event } => f
625 .debug_struct("TimelineEventDecryptionResult::PlainText")
626 .field("event", &DebugRawEvent(event))
627 .finish(),
628
629 Self::UnableToDecrypt { event, utd_info } => f
630 .debug_struct("TimelineEventDecryptionResult::UnableToDecrypt")
631 .field("event", &DebugRawEvent(event))
632 .field("utd_info", &utd_info)
633 .finish(),
634
635 Self::Decrypted(decrypted) => {
636 f.debug_tuple("TimelineEventDecryptionResult::Decrypted").field(decrypted).finish()
637 }
638 }
639 }
640}
641
642#[derive(Clone, Serialize, Deserialize)]
643pub struct DecryptedRoomEvent {
645 pub event: Raw<AnyMessageLikeEvent>,
652
653 pub encryption_info: EncryptionInfo,
655
656 #[serde(skip_serializing_if = "Option::is_none")]
661 pub unsigned_encryption_info: Option<BTreeMap<UnsignedEventLocation, UnsignedDecryptionResult>>,
662}
663
664#[cfg(not(tarpaulin_include))]
665impl fmt::Debug for DecryptedRoomEvent {
666 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
667 let DecryptedRoomEvent { event, encryption_info, unsigned_encryption_info } = self;
668
669 f.debug_struct("DecryptedRoomEvent")
670 .field("event", &DebugRawEvent(event))
671 .field("encryption_info", encryption_info)
672 .maybe_field("unsigned_encryption_info", unsigned_encryption_info)
673 .finish()
674 }
675}
676
677#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
679pub enum UnsignedEventLocation {
680 RelationsReplace,
683 RelationsThreadLatestEvent,
686}
687
688impl UnsignedEventLocation {
689 pub fn find_mut<'a>(&self, unsigned: &'a mut JsonObject) -> Option<&'a mut serde_json::Value> {
696 let relations = unsigned.get_mut("m.relations")?.as_object_mut()?;
697
698 match self {
699 Self::RelationsReplace => relations.get_mut("m.replace"),
700 Self::RelationsThreadLatestEvent => {
701 relations.get_mut("m.thread")?.as_object_mut()?.get_mut("latest_event")
702 }
703 }
704 }
705}
706
707#[derive(Debug, Clone, Serialize, Deserialize)]
709pub enum UnsignedDecryptionResult {
710 Decrypted(EncryptionInfo),
712 UnableToDecrypt(UnableToDecryptInfo),
714}
715
716impl UnsignedDecryptionResult {
717 pub fn encryption_info(&self) -> Option<&EncryptionInfo> {
720 match self {
721 Self::Decrypted(info) => Some(info),
722 Self::UnableToDecrypt(_) => None,
723 }
724 }
725}
726
727#[derive(Debug, Clone, Serialize, Deserialize)]
729pub struct UnableToDecryptInfo {
730 #[serde(skip_serializing_if = "Option::is_none")]
733 pub session_id: Option<String>,
734
735 #[serde(default = "unknown_utd_reason", deserialize_with = "deserialize_utd_reason")]
737 pub reason: UnableToDecryptReason,
738}
739
740fn unknown_utd_reason() -> UnableToDecryptReason {
741 UnableToDecryptReason::Unknown
742}
743
744pub fn deserialize_utd_reason<'de, D>(d: D) -> Result<UnableToDecryptReason, D::Error>
747where
748 D: serde::Deserializer<'de>,
749{
750 let v: serde_json::Value = Deserialize::deserialize(d)?;
752 if v.as_str().is_some_and(|s| s == "MissingMegolmSession") {
755 return Ok(UnableToDecryptReason::MissingMegolmSession { withheld_code: None });
756 }
757 serde_json::from_value::<UnableToDecryptReason>(v).map_err(serde::de::Error::custom)
760}
761
762#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
764pub enum UnableToDecryptReason {
765 #[doc(hidden)]
768 Unknown,
769
770 MalformedEncryptedEvent,
774
775 MissingMegolmSession {
778 withheld_code: Option<WithheldCode>,
781 },
782
783 UnknownMegolmMessageIndex,
786
787 MegolmDecryptionFailure,
794
795 PayloadDeserializationFailure,
797
798 MismatchedIdentityKeys,
802
803 SenderIdentityNotTrusted(VerificationLevel),
807}
808
809impl UnableToDecryptReason {
810 pub fn is_missing_room_key(&self) -> bool {
813 matches!(
816 self,
817 Self::MissingMegolmSession { withheld_code: None } | Self::UnknownMegolmMessageIndex
818 )
819 }
820}
821
822#[derive(
826 Clone,
827 PartialEq,
828 Eq,
829 Hash,
830 AsStrAsRefStr,
831 AsRefStr,
832 FromString,
833 DebugAsRefStr,
834 SerializeAsRefStr,
835 DeserializeFromCowStr,
836)]
837pub enum WithheldCode {
838 #[ruma_enum(rename = "m.blacklisted")]
840 Blacklisted,
841
842 #[ruma_enum(rename = "m.unverified")]
844 Unverified,
845
846 #[ruma_enum(rename = "m.unauthorised")]
850 Unauthorised,
851
852 #[ruma_enum(rename = "m.unavailable")]
855 Unavailable,
856
857 #[ruma_enum(rename = "m.no_olm")]
861 NoOlm,
862
863 #[doc(hidden)]
864 _Custom(PrivOwnedStr),
865}
866
867impl fmt::Display for WithheldCode {
868 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
869 let string = match self {
870 WithheldCode::Blacklisted => "The sender has blocked you.",
871 WithheldCode::Unverified => "The sender has disabled encrypting to unverified devices.",
872 WithheldCode::Unauthorised => "You are not authorised to read the message.",
873 WithheldCode::Unavailable => "The requested key was not found.",
874 WithheldCode::NoOlm => "Unable to establish a secure channel.",
875 _ => self.as_str(),
876 };
877
878 f.write_str(string)
879 }
880}
881
882#[doc(hidden)]
886#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
887pub struct PrivOwnedStr(pub Box<str>);
888
889#[cfg(not(tarpaulin_include))]
890impl fmt::Debug for PrivOwnedStr {
891 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
892 self.0.fmt(f)
893 }
894}
895
896#[derive(Debug, Deserialize)]
901struct SyncTimelineEventDeserializationHelperV1 {
902 kind: TimelineEventKind,
904
905 #[serde(default)]
907 push_actions: Vec<Action>,
908}
909
910impl From<SyncTimelineEventDeserializationHelperV1> for TimelineEvent {
911 fn from(value: SyncTimelineEventDeserializationHelperV1) -> Self {
912 let SyncTimelineEventDeserializationHelperV1 { kind, push_actions } = value;
913 TimelineEvent { kind, push_actions: Some(push_actions) }
914 }
915}
916
917#[derive(Deserialize)]
919struct SyncTimelineEventDeserializationHelperV0 {
920 event: Raw<AnySyncTimelineEvent>,
922
923 encryption_info: Option<EncryptionInfo>,
926
927 #[serde(default)]
929 push_actions: Vec<Action>,
930
931 unsigned_encryption_info: Option<BTreeMap<UnsignedEventLocation, UnsignedDecryptionResult>>,
936}
937
938impl From<SyncTimelineEventDeserializationHelperV0> for TimelineEvent {
939 fn from(value: SyncTimelineEventDeserializationHelperV0) -> Self {
940 let SyncTimelineEventDeserializationHelperV0 {
941 event,
942 encryption_info,
943 push_actions,
944 unsigned_encryption_info,
945 } = value;
946
947 let kind = match encryption_info {
948 Some(encryption_info) => {
949 TimelineEventKind::Decrypted(DecryptedRoomEvent {
950 event: event.cast(),
957 encryption_info,
958 unsigned_encryption_info,
959 })
960 }
961
962 None => TimelineEventKind::PlainText { event },
963 };
964
965 TimelineEvent { kind, push_actions: Some(push_actions) }
966 }
967}
968
969#[cfg(test)]
970mod tests {
971 use std::collections::BTreeMap;
972
973 use assert_matches::assert_matches;
974 use insta::{assert_json_snapshot, with_settings};
975 use ruma::{
976 device_id, event_id, events::room::message::RoomMessageEventContent, serde::Raw, user_id,
977 DeviceKeyAlgorithm,
978 };
979 use serde::Deserialize;
980 use serde_json::json;
981
982 use super::{
983 AlgorithmInfo, DecryptedRoomEvent, DeviceLinkProblem, EncryptionInfo, ShieldState,
984 ShieldStateCode, TimelineEvent, TimelineEventKind, UnableToDecryptInfo,
985 UnableToDecryptReason, UnsignedDecryptionResult, UnsignedEventLocation, VerificationLevel,
986 VerificationState, WithheldCode,
987 };
988
989 fn example_event() -> serde_json::Value {
990 json!({
991 "content": RoomMessageEventContent::text_plain("secret"),
992 "type": "m.room.message",
993 "event_id": "$xxxxx:example.org",
994 "room_id": "!someroom:example.com",
995 "origin_server_ts": 2189,
996 "sender": "@carl:example.com",
997 })
998 }
999
1000 #[test]
1001 fn sync_timeline_debug_content() {
1002 let room_event = TimelineEvent::new(Raw::new(&example_event()).unwrap().cast());
1003 let debug_s = format!("{room_event:?}");
1004 assert!(
1005 !debug_s.contains("secret"),
1006 "Debug representation contains event content!\n{debug_s}"
1007 );
1008 }
1009
1010 #[test]
1011 fn old_verification_state_to_new_migration() {
1012 #[derive(Deserialize)]
1013 struct State {
1014 state: VerificationState,
1015 }
1016
1017 let state = json!({
1018 "state": "Trusted",
1019 });
1020 let deserialized: State =
1021 serde_json::from_value(state).expect("We can deserialize the old trusted value");
1022 assert_eq!(deserialized.state, VerificationState::Verified);
1023
1024 let state = json!({
1025 "state": "UnknownDevice",
1026 });
1027
1028 let deserialized: State =
1029 serde_json::from_value(state).expect("We can deserialize the old unknown device value");
1030
1031 assert_eq!(
1032 deserialized.state,
1033 VerificationState::Unverified(VerificationLevel::None(
1034 DeviceLinkProblem::MissingDevice
1035 ))
1036 );
1037
1038 let state = json!({
1039 "state": "Untrusted",
1040 });
1041 let deserialized: State =
1042 serde_json::from_value(state).expect("We can deserialize the old trusted value");
1043
1044 assert_eq!(
1045 deserialized.state,
1046 VerificationState::Unverified(VerificationLevel::UnsignedDevice)
1047 );
1048 }
1049
1050 #[test]
1051 fn test_verification_level_deserializes() {
1052 #[derive(Deserialize)]
1054 struct Container {
1055 verification_level: VerificationLevel,
1056 }
1057 let container = json!({ "verification_level": "VerificationViolation" });
1058
1059 let deserialized: Container = serde_json::from_value(container)
1061 .expect("We can deserialize the old PreviouslyVerified value");
1062
1063 assert_eq!(deserialized.verification_level, VerificationLevel::VerificationViolation);
1065 }
1066
1067 #[test]
1068 fn test_verification_level_deserializes_from_old_previously_verified_value() {
1069 #[derive(Deserialize)]
1071 struct Container {
1072 verification_level: VerificationLevel,
1073 }
1074 let container = json!({ "verification_level": "PreviouslyVerified" });
1075
1076 let deserialized: Container = serde_json::from_value(container)
1078 .expect("We can deserialize the old PreviouslyVerified value");
1079
1080 assert_eq!(deserialized.verification_level, VerificationLevel::VerificationViolation);
1082 }
1083
1084 #[test]
1085 fn test_shield_state_code_deserializes() {
1086 #[derive(Deserialize)]
1088 struct Container {
1089 shield_state_code: ShieldStateCode,
1090 }
1091 let container = json!({ "shield_state_code": "VerificationViolation" });
1092
1093 let deserialized: Container = serde_json::from_value(container)
1095 .expect("We can deserialize the old PreviouslyVerified value");
1096
1097 assert_eq!(deserialized.shield_state_code, ShieldStateCode::VerificationViolation);
1099 }
1100
1101 #[test]
1102 fn test_shield_state_code_deserializes_from_old_previously_verified_value() {
1103 #[derive(Deserialize)]
1105 struct Container {
1106 shield_state_code: ShieldStateCode,
1107 }
1108 let container = json!({ "shield_state_code": "PreviouslyVerified" });
1109
1110 let deserialized: Container = serde_json::from_value(container)
1112 .expect("We can deserialize the old PreviouslyVerified value");
1113
1114 assert_eq!(deserialized.shield_state_code, ShieldStateCode::VerificationViolation);
1116 }
1117
1118 #[test]
1119 fn sync_timeline_event_serialisation() {
1120 let room_event = TimelineEvent {
1121 kind: TimelineEventKind::Decrypted(DecryptedRoomEvent {
1122 event: Raw::new(&example_event()).unwrap().cast(),
1123 encryption_info: EncryptionInfo {
1124 sender: user_id!("@sender:example.com").to_owned(),
1125 sender_device: None,
1126 algorithm_info: AlgorithmInfo::MegolmV1AesSha2 {
1127 curve25519_key: "xxx".to_owned(),
1128 sender_claimed_keys: Default::default(),
1129 session_id: Some("xyz".to_owned()),
1130 },
1131 verification_state: VerificationState::Verified,
1132 },
1133 unsigned_encryption_info: Some(BTreeMap::from([(
1134 UnsignedEventLocation::RelationsReplace,
1135 UnsignedDecryptionResult::UnableToDecrypt(UnableToDecryptInfo {
1136 session_id: Some("xyz".to_owned()),
1137 reason: UnableToDecryptReason::MalformedEncryptedEvent,
1138 }),
1139 )])),
1140 }),
1141 push_actions: Default::default(),
1142 };
1143
1144 let serialized = serde_json::to_value(&room_event).unwrap();
1145
1146 assert_eq!(
1148 serialized,
1149 json!({
1150 "kind": {
1151 "Decrypted": {
1152 "event": {
1153 "content": {"body": "secret", "msgtype": "m.text"},
1154 "event_id": "$xxxxx:example.org",
1155 "origin_server_ts": 2189,
1156 "room_id": "!someroom:example.com",
1157 "sender": "@carl:example.com",
1158 "type": "m.room.message",
1159 },
1160 "encryption_info": {
1161 "sender": "@sender:example.com",
1162 "sender_device": null,
1163 "algorithm_info": {
1164 "MegolmV1AesSha2": {
1165 "curve25519_key": "xxx",
1166 "sender_claimed_keys": {},
1167 "session_id": "xyz",
1168 }
1169 },
1170 "verification_state": "Verified",
1171 },
1172 "unsigned_encryption_info": {
1173 "RelationsReplace": {"UnableToDecrypt": {
1174 "session_id": "xyz",
1175 "reason": "MalformedEncryptedEvent",
1176 }}
1177 }
1178 }
1179 }
1180 })
1181 );
1182
1183 let event: TimelineEvent = serde_json::from_value(serialized).unwrap();
1185 assert_eq!(event.event_id(), Some(event_id!("$xxxxx:example.org").to_owned()));
1186 assert_matches!(
1187 event.encryption_info().unwrap().algorithm_info,
1188 AlgorithmInfo::MegolmV1AesSha2 { .. }
1189 );
1190
1191 let serialized = json!({
1193 "event": {
1194 "content": {"body": "secret", "msgtype": "m.text"},
1195 "event_id": "$xxxxx:example.org",
1196 "origin_server_ts": 2189,
1197 "room_id": "!someroom:example.com",
1198 "sender": "@carl:example.com",
1199 "type": "m.room.message",
1200 },
1201 "encryption_info": {
1202 "sender": "@sender:example.com",
1203 "sender_device": null,
1204 "algorithm_info": {
1205 "MegolmV1AesSha2": {
1206 "curve25519_key": "xxx",
1207 "sender_claimed_keys": {}
1208 }
1209 },
1210 "verification_state": "Verified",
1211 },
1212 });
1213 let event: TimelineEvent = serde_json::from_value(serialized).unwrap();
1214 assert_eq!(event.event_id(), Some(event_id!("$xxxxx:example.org").to_owned()));
1215 assert_matches!(
1216 event.encryption_info().unwrap().algorithm_info,
1217 AlgorithmInfo::MegolmV1AesSha2 { session_id: None, .. }
1218 );
1219
1220 let serialized = json!({
1223 "event": {
1224 "content": {"body": "secret", "msgtype": "m.text"},
1225 "event_id": "$xxxxx:example.org",
1226 "origin_server_ts": 2189,
1227 "room_id": "!someroom:example.com",
1228 "sender": "@carl:example.com",
1229 "type": "m.room.message",
1230 },
1231 "encryption_info": {
1232 "sender": "@sender:example.com",
1233 "sender_device": null,
1234 "algorithm_info": {
1235 "MegolmV1AesSha2": {
1236 "curve25519_key": "xxx",
1237 "sender_claimed_keys": {}
1238 }
1239 },
1240 "verification_state": "Verified",
1241 },
1242 "unsigned_encryption_info": {
1243 "RelationsReplace": {"UnableToDecrypt": {"session_id": "xyz"}}
1244 }
1245 });
1246 let event: TimelineEvent = serde_json::from_value(serialized).unwrap();
1247 assert_eq!(event.event_id(), Some(event_id!("$xxxxx:example.org").to_owned()));
1248 assert_matches!(
1249 event.encryption_info().unwrap().algorithm_info,
1250 AlgorithmInfo::MegolmV1AesSha2 { .. }
1251 );
1252 assert_matches!(event.kind, TimelineEventKind::Decrypted(decrypted) => {
1253 assert_matches!(decrypted.unsigned_encryption_info, Some(map) => {
1254 assert_eq!(map.len(), 1);
1255 let (location, result) = map.into_iter().next().unwrap();
1256 assert_eq!(location, UnsignedEventLocation::RelationsReplace);
1257 assert_matches!(result, UnsignedDecryptionResult::UnableToDecrypt(utd_info) => {
1258 assert_eq!(utd_info.session_id, Some("xyz".to_owned()));
1259 assert_eq!(utd_info.reason, UnableToDecryptReason::Unknown);
1260 })
1261 });
1262 });
1263 }
1264
1265 #[test]
1266 fn sync_timeline_event_deserialisation_migration_for_withheld() {
1267 let serialized = json!({
1284 "kind": {
1285 "UnableToDecrypt": {
1286 "event": {
1287 "content": {
1288 "algorithm": "m.megolm.v1.aes-sha2",
1289 "ciphertext": "AwgAEoABzL1JYhqhjW9jXrlT3M6H8mJ4qffYtOQOnPuAPNxsuG20oiD/Fnpv6jnQGhU6YbV9pNM+1mRnTvxW3CbWOPjLKqCWTJTc7Q0vDEVtYePg38ncXNcwMmfhgnNAoW9S7vNs8C003x3yUl6NeZ8bH+ci870BZL+kWM/lMl10tn6U7snNmSjnE3ckvRdO+11/R4//5VzFQpZdf4j036lNSls/WIiI67Fk9iFpinz9xdRVWJFVdrAiPFwb8L5xRZ8aX+e2JDMlc1eW8gk",
1290 "device_id": "SKCGPNUWAU",
1291 "sender_key": "Gim/c7uQdSXyrrUbmUOrBT6sMC0gO7QSLmOK6B7NOm0",
1292 "session_id": "hgLyeSqXfb8vc5AjQLsg6TSHVu0HJ7HZ4B6jgMvxkrs"
1293 },
1294 "event_id": "$xxxxx:example.org",
1295 "origin_server_ts": 2189,
1296 "room_id": "!someroom:example.com",
1297 "sender": "@carl:example.com",
1298 "type": "m.room.message"
1299 },
1300 "utd_info": {
1301 "reason": "MissingMegolmSession",
1302 "session_id": "session000"
1303 }
1304 }
1305 }
1306 });
1307
1308 let result = serde_json::from_value(serialized);
1309 assert!(result.is_ok());
1310
1311 let event: TimelineEvent = result.unwrap();
1313 assert_matches!(
1314 event.kind,
1315 TimelineEventKind::UnableToDecrypt { utd_info, .. }=> {
1316 assert_matches!(
1317 utd_info.reason,
1318 UnableToDecryptReason::MissingMegolmSession { withheld_code: None }
1319 );
1320 }
1321 )
1322 }
1323
1324 #[test]
1325 fn unable_to_decrypt_info_migration_for_withheld() {
1326 let old_format = json!({
1327 "reason": "MissingMegolmSession",
1328 "session_id": "session000"
1329 });
1330
1331 let deserialized = serde_json::from_value::<UnableToDecryptInfo>(old_format).unwrap();
1332 let session_id = Some("session000".to_owned());
1333
1334 assert_eq!(deserialized.session_id, session_id);
1335 assert_eq!(
1336 deserialized.reason,
1337 UnableToDecryptReason::MissingMegolmSession { withheld_code: None },
1338 );
1339
1340 let new_format = json!({
1341 "session_id": "session000",
1342 "reason": {
1343 "MissingMegolmSession": {
1344 "withheld_code": null
1345 }
1346 }
1347 });
1348
1349 let deserialized = serde_json::from_value::<UnableToDecryptInfo>(new_format).unwrap();
1350
1351 assert_eq!(
1352 deserialized.reason,
1353 UnableToDecryptReason::MissingMegolmSession { withheld_code: None },
1354 );
1355 assert_eq!(deserialized.session_id, session_id);
1356 }
1357
1358 #[test]
1359 fn unable_to_decrypt_reason_is_missing_room_key() {
1360 let reason = UnableToDecryptReason::MissingMegolmSession { withheld_code: None };
1361 assert!(reason.is_missing_room_key());
1362
1363 let reason = UnableToDecryptReason::MissingMegolmSession {
1364 withheld_code: Some(WithheldCode::Blacklisted),
1365 };
1366 assert!(!reason.is_missing_room_key());
1367
1368 let reason = UnableToDecryptReason::UnknownMegolmMessageIndex;
1369 assert!(reason.is_missing_room_key());
1370 }
1371
1372 #[test]
1373 fn snapshot_test_verification_level() {
1374 with_settings!({ prepend_module_to_snapshot => false }, {
1375 assert_json_snapshot!(VerificationLevel::VerificationViolation);
1376 assert_json_snapshot!(VerificationLevel::UnsignedDevice);
1377 assert_json_snapshot!(VerificationLevel::None(DeviceLinkProblem::InsecureSource));
1378 assert_json_snapshot!(VerificationLevel::None(DeviceLinkProblem::MissingDevice));
1379 assert_json_snapshot!(VerificationLevel::UnverifiedIdentity);
1380 });
1381 }
1382
1383 #[test]
1384 fn snapshot_test_verification_states() {
1385 with_settings!({ prepend_module_to_snapshot => false }, {
1386 assert_json_snapshot!(VerificationState::Unverified(VerificationLevel::UnsignedDevice));
1387 assert_json_snapshot!(VerificationState::Unverified(
1388 VerificationLevel::VerificationViolation
1389 ));
1390 assert_json_snapshot!(VerificationState::Unverified(VerificationLevel::None(
1391 DeviceLinkProblem::InsecureSource,
1392 )));
1393 assert_json_snapshot!(VerificationState::Unverified(VerificationLevel::None(
1394 DeviceLinkProblem::MissingDevice,
1395 )));
1396 assert_json_snapshot!(VerificationState::Verified);
1397 });
1398 }
1399
1400 #[test]
1401 fn snapshot_test_shield_states() {
1402 with_settings!({ prepend_module_to_snapshot => false }, {
1403 assert_json_snapshot!(ShieldState::None);
1404 assert_json_snapshot!(ShieldState::Red {
1405 code: ShieldStateCode::UnverifiedIdentity,
1406 message: "a message"
1407 });
1408 assert_json_snapshot!(ShieldState::Grey {
1409 code: ShieldStateCode::AuthenticityNotGuaranteed,
1410 message: "authenticity of this message cannot be guaranteed",
1411 });
1412 });
1413 }
1414
1415 #[test]
1416 fn snapshot_test_shield_codes() {
1417 with_settings!({ prepend_module_to_snapshot => false }, {
1418 assert_json_snapshot!(ShieldStateCode::AuthenticityNotGuaranteed);
1419 assert_json_snapshot!(ShieldStateCode::UnknownDevice);
1420 assert_json_snapshot!(ShieldStateCode::UnsignedDevice);
1421 assert_json_snapshot!(ShieldStateCode::UnverifiedIdentity);
1422 assert_json_snapshot!(ShieldStateCode::SentInClear);
1423 assert_json_snapshot!(ShieldStateCode::VerificationViolation);
1424 });
1425 }
1426
1427 #[test]
1428 fn snapshot_test_algorithm_info() {
1429 let mut map = BTreeMap::new();
1430 map.insert(DeviceKeyAlgorithm::Curve25519, "claimedclaimedcurve25519".to_owned());
1431 map.insert(DeviceKeyAlgorithm::Ed25519, "claimedclaimeded25519".to_owned());
1432 let info = AlgorithmInfo::MegolmV1AesSha2 {
1433 curve25519_key: "curvecurvecurve".into(),
1434 sender_claimed_keys: BTreeMap::from([
1435 (DeviceKeyAlgorithm::Curve25519, "claimedclaimedcurve25519".to_owned()),
1436 (DeviceKeyAlgorithm::Ed25519, "claimedclaimeded25519".to_owned()),
1437 ]),
1438 session_id: None,
1439 };
1440
1441 with_settings!({ prepend_module_to_snapshot => false }, {
1442 assert_json_snapshot!(info)
1443 });
1444 }
1445
1446 #[test]
1447 fn test_encryption_info_migration() {
1448 let old_format = json!({
1451 "sender": "@alice:localhost",
1452 "sender_device": "ABCDEFGH",
1453 "algorithm_info": {
1454 "MegolmV1AesSha2": {
1455 "curve25519_key": "curvecurvecurve",
1456 "sender_claimed_keys": {}
1457 }
1458 },
1459 "verification_state": "Verified",
1460 "session_id": "mysessionid76"
1461 });
1462
1463 let deserialized = serde_json::from_value::<EncryptionInfo>(old_format).unwrap();
1464 let expected_session_id = Some("mysessionid76".to_owned());
1465
1466 let AlgorithmInfo::MegolmV1AesSha2 { session_id, .. } = deserialized.algorithm_info.clone();
1467 assert_eq!(session_id, expected_session_id);
1468
1469 assert_json_snapshot!(deserialized);
1470 }
1471
1472 #[test]
1473 fn snapshot_test_encryption_info() {
1474 let info = EncryptionInfo {
1475 sender: user_id!("@alice:localhost").to_owned(),
1476 sender_device: Some(device_id!("ABCDEFGH").to_owned()),
1477 algorithm_info: AlgorithmInfo::MegolmV1AesSha2 {
1478 curve25519_key: "curvecurvecurve".into(),
1479 sender_claimed_keys: Default::default(),
1480 session_id: Some("mysessionid76".to_owned()),
1481 },
1482 verification_state: VerificationState::Verified,
1483 };
1484
1485 with_settings!({ sort_maps => true, prepend_module_to_snapshot => false }, {
1486 assert_json_snapshot!(info)
1487 })
1488 }
1489
1490 #[test]
1491 fn snapshot_test_sync_timeline_event() {
1492 let room_event = TimelineEvent {
1493 kind: TimelineEventKind::Decrypted(DecryptedRoomEvent {
1494 event: Raw::new(&example_event()).unwrap().cast(),
1495 encryption_info: EncryptionInfo {
1496 sender: user_id!("@sender:example.com").to_owned(),
1497 sender_device: Some(device_id!("ABCDEFGHIJ").to_owned()),
1498 algorithm_info: AlgorithmInfo::MegolmV1AesSha2 {
1499 curve25519_key: "xxx".to_owned(),
1500 sender_claimed_keys: BTreeMap::from([
1501 (
1502 DeviceKeyAlgorithm::Ed25519,
1503 "I3YsPwqMZQXHkSQbjFNEs7b529uac2xBpI83eN3LUXo".to_owned(),
1504 ),
1505 (
1506 DeviceKeyAlgorithm::Curve25519,
1507 "qzdW3F5IMPFl0HQgz5w/L5Oi/npKUFn8Um84acIHfPY".to_owned(),
1508 ),
1509 ]),
1510 session_id: Some("mysessionid112".to_owned()),
1511 },
1512 verification_state: VerificationState::Verified,
1513 },
1514 unsigned_encryption_info: Some(BTreeMap::from([(
1515 UnsignedEventLocation::RelationsThreadLatestEvent,
1516 UnsignedDecryptionResult::UnableToDecrypt(UnableToDecryptInfo {
1517 session_id: Some("xyz".to_owned()),
1518 reason: UnableToDecryptReason::MissingMegolmSession {
1519 withheld_code: Some(WithheldCode::Unverified),
1520 },
1521 }),
1522 )])),
1523 }),
1524 push_actions: Default::default(),
1525 };
1526
1527 with_settings!({ sort_maps => true, prepend_module_to_snapshot => false }, {
1528 assert_json_snapshot! {
1531 serde_json::to_value(&room_event).unwrap(),
1532 }
1533 });
1534 }
1535}