1use std::{collections::BTreeMap, fmt, sync::Arc};
16
17#[cfg(doc)]
18use ruma::events::AnyTimelineEvent;
19use ruma::{
20 DeviceKeyAlgorithm, OwnedDeviceId, OwnedEventId, OwnedUserId,
21 events::{
22 AnyMessageLikeEvent, AnySyncMessageLikeEvent, AnySyncTimelineEvent, AnyToDeviceEvent,
23 MessageLikeEventType,
24 },
25 push::Action,
26 serde::{
27 AsRefStr, AsStrAsRefStr, DebugAsRefStr, DeserializeFromCowStr, FromString, JsonObject, Raw,
28 SerializeAsRefStr,
29 },
30};
31use serde::{Deserialize, Serialize};
32use tracing::warn;
33#[cfg(target_family = "wasm")]
34use wasm_bindgen::prelude::*;
35
36use crate::{
37 debug::{DebugRawEvent, DebugStructExt},
38 serde_helpers::extract_bundled_thread_summary,
39};
40
41const AUTHENTICITY_NOT_GUARANTEED: &str =
42 "The authenticity of this encrypted message can't be guaranteed on this device.";
43const UNVERIFIED_IDENTITY: &str = "Encrypted by an unverified user.";
44const VERIFICATION_VIOLATION: &str =
45 "Encrypted by a previously-verified user who is no longer verified.";
46const UNSIGNED_DEVICE: &str = "Encrypted by a device not verified by its owner.";
47const UNKNOWN_DEVICE: &str = "Encrypted by an unknown or deleted device.";
48const MISMATCHED_SENDER: &str = "\
49 The sender of the event does not match the owner of the device \
50 that created the Megolm session.";
51pub const SENT_IN_CLEAR: &str = "Not encrypted.";
52
53#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
56#[serde(from = "OldVerificationStateHelper")]
57pub enum VerificationState {
58 Verified,
63
64 Unverified(VerificationLevel),
69}
70
71#[derive(Clone, Debug, Deserialize)]
74enum OldVerificationStateHelper {
75 Untrusted,
76 UnknownDevice,
77 #[serde(alias = "Trusted")]
78 Verified,
79 Unverified(VerificationLevel),
80}
81
82impl From<OldVerificationStateHelper> for VerificationState {
83 fn from(value: OldVerificationStateHelper) -> Self {
84 match value {
85 OldVerificationStateHelper::Untrusted => {
88 VerificationState::Unverified(VerificationLevel::UnsignedDevice)
89 }
90 OldVerificationStateHelper::UnknownDevice => {
91 Self::Unverified(VerificationLevel::None(DeviceLinkProblem::MissingDevice))
92 }
93 OldVerificationStateHelper::Verified => Self::Verified,
94 OldVerificationStateHelper::Unverified(l) => Self::Unverified(l),
95 }
96 }
97}
98
99impl VerificationState {
100 pub fn to_shield_state_strict(&self) -> ShieldState {
107 match self {
108 VerificationState::Verified => ShieldState::None,
109 VerificationState::Unverified(level) => match level {
110 VerificationLevel::UnverifiedIdentity
111 | VerificationLevel::VerificationViolation
112 | VerificationLevel::UnsignedDevice => ShieldState::Red {
113 code: ShieldStateCode::UnverifiedIdentity,
114 message: UNVERIFIED_IDENTITY,
115 },
116 VerificationLevel::None(link) => match link {
117 DeviceLinkProblem::MissingDevice => ShieldState::Red {
118 code: ShieldStateCode::UnknownDevice,
119 message: UNKNOWN_DEVICE,
120 },
121 DeviceLinkProblem::InsecureSource => ShieldState::Red {
122 code: ShieldStateCode::AuthenticityNotGuaranteed,
123 message: AUTHENTICITY_NOT_GUARANTEED,
124 },
125 },
126 VerificationLevel::MismatchedSender => ShieldState::Red {
127 code: ShieldStateCode::MismatchedSender,
128 message: MISMATCHED_SENDER,
129 },
130 },
131 }
132 }
133
134 pub fn to_shield_state_lax(&self) -> ShieldState {
142 match self {
143 VerificationState::Verified => ShieldState::None,
144 VerificationState::Unverified(level) => match level {
145 VerificationLevel::UnverifiedIdentity => {
146 ShieldState::None
149 }
150 VerificationLevel::VerificationViolation => {
151 ShieldState::Red {
154 code: ShieldStateCode::VerificationViolation,
155 message: VERIFICATION_VIOLATION,
156 }
157 }
158 VerificationLevel::UnsignedDevice => {
159 ShieldState::Red {
161 code: ShieldStateCode::UnsignedDevice,
162 message: UNSIGNED_DEVICE,
163 }
164 }
165 VerificationLevel::None(link) => match link {
166 DeviceLinkProblem::MissingDevice => {
167 ShieldState::Red {
171 code: ShieldStateCode::UnknownDevice,
172 message: UNKNOWN_DEVICE,
173 }
174 }
175 DeviceLinkProblem::InsecureSource => {
176 ShieldState::Grey {
179 code: ShieldStateCode::AuthenticityNotGuaranteed,
180 message: AUTHENTICITY_NOT_GUARANTEED,
181 }
182 }
183 },
184 VerificationLevel::MismatchedSender => ShieldState::Red {
185 code: ShieldStateCode::MismatchedSender,
186 message: MISMATCHED_SENDER,
187 },
188 },
189 }
190 }
191}
192
193#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
196pub enum VerificationLevel {
197 UnverifiedIdentity,
199
200 #[serde(alias = "PreviouslyVerified")]
203 VerificationViolation,
204
205 UnsignedDevice,
208
209 None(DeviceLinkProblem),
215
216 MismatchedSender,
219}
220
221impl fmt::Display for VerificationLevel {
222 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
223 let display = match self {
224 VerificationLevel::UnverifiedIdentity => "The sender's identity was not verified",
225 VerificationLevel::VerificationViolation => {
226 "The sender's identity was previously verified but has changed"
227 }
228 VerificationLevel::UnsignedDevice => {
229 "The sending device was not signed by the user's identity"
230 }
231 VerificationLevel::None(..) => "The sending device is not known",
232 VerificationLevel::MismatchedSender => MISMATCHED_SENDER,
233 };
234 write!(f, "{display}")
235 }
236}
237
238#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
241pub enum DeviceLinkProblem {
242 MissingDevice,
246 InsecureSource,
249}
250
251#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq)]
254pub enum ShieldState {
255 Red {
258 code: ShieldStateCode,
260 message: &'static str,
262 },
263 Grey {
266 code: ShieldStateCode,
268 message: &'static str,
270 },
271 None,
273}
274
275#[derive(Clone, Copy, Debug, Deserialize, Serialize, Eq, PartialEq)]
277#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
278#[cfg_attr(target_family = "wasm", wasm_bindgen)]
279pub enum ShieldStateCode {
280 AuthenticityNotGuaranteed,
282 UnknownDevice,
284 UnsignedDevice,
286 UnverifiedIdentity,
288 SentInClear,
290 #[serde(alias = "PreviouslyVerified")]
292 VerificationViolation,
293 MismatchedSender,
296}
297
298#[derive(Clone, Debug, Deserialize, Serialize)]
300pub enum AlgorithmInfo {
301 MegolmV1AesSha2 {
303 curve25519_key: String,
306 sender_claimed_keys: BTreeMap<DeviceKeyAlgorithm, String>,
310
311 #[serde(default, skip_serializing_if = "Option::is_none")]
314 session_id: Option<String>,
315 },
316
317 OlmV1Curve25519AesSha2 {
319 curve25519_public_key_base64: String,
321 },
322}
323
324#[derive(Clone, Debug, Serialize)]
326pub struct EncryptionInfo {
327 pub sender: OwnedUserId,
330 pub sender_device: Option<OwnedDeviceId>,
333 pub algorithm_info: AlgorithmInfo,
335 pub verification_state: VerificationState,
342}
343
344impl EncryptionInfo {
345 pub fn session_id(&self) -> Option<&str> {
347 if let AlgorithmInfo::MegolmV1AesSha2 { session_id, .. } = &self.algorithm_info {
348 session_id.as_deref()
349 } else {
350 None
351 }
352 }
353}
354
355impl<'de> Deserialize<'de> for EncryptionInfo {
356 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
357 where
358 D: serde::Deserializer<'de>,
359 {
360 #[derive(Deserialize)]
363 struct Helper {
364 pub sender: OwnedUserId,
365 pub sender_device: Option<OwnedDeviceId>,
366 pub algorithm_info: AlgorithmInfo,
367 pub verification_state: VerificationState,
368 #[serde(rename = "session_id")]
369 pub old_session_id: Option<String>,
370 }
371
372 let Helper { sender, sender_device, algorithm_info, verification_state, old_session_id } =
373 Helper::deserialize(deserializer)?;
374
375 let algorithm_info = match algorithm_info {
376 AlgorithmInfo::MegolmV1AesSha2 { curve25519_key, sender_claimed_keys, session_id } => {
377 AlgorithmInfo::MegolmV1AesSha2 {
378 session_id: session_id.or(old_session_id),
380 curve25519_key,
381 sender_claimed_keys,
382 }
383 }
384 other => other,
385 };
386
387 Ok(EncryptionInfo { sender, sender_device, algorithm_info, verification_state })
388 }
389}
390
391#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
402pub struct ThreadSummary {
403 #[serde(skip_serializing_if = "Option::is_none")]
405 pub latest_reply: Option<OwnedEventId>,
406
407 pub num_replies: u32,
413}
414
415#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)]
417pub enum ThreadSummaryStatus {
418 #[default]
420 Unknown,
421 None,
423 Some(ThreadSummary),
425}
426
427impl ThreadSummaryStatus {
428 fn is_unknown(&self) -> bool {
430 matches!(self, ThreadSummaryStatus::Unknown)
431 }
432
433 pub fn summary(&self) -> Option<&ThreadSummary> {
436 match self {
437 ThreadSummaryStatus::Unknown | ThreadSummaryStatus::None => None,
438 ThreadSummaryStatus::Some(thread_summary) => Some(thread_summary),
439 }
440 }
441}
442
443#[derive(Clone, Debug, Serialize)]
466pub struct TimelineEvent {
467 pub kind: TimelineEventKind,
469
470 #[serde(skip_serializing_if = "skip_serialize_push_actions")]
475 push_actions: Option<Vec<Action>>,
476
477 #[serde(default, skip_serializing_if = "ThreadSummaryStatus::is_unknown")]
479 pub thread_summary: ThreadSummaryStatus,
480
481 #[serde(skip)]
486 pub bundled_latest_thread_event: Option<Box<TimelineEvent>>,
487}
488
489fn skip_serialize_push_actions(push_actions: &Option<Vec<Action>>) -> bool {
491 push_actions.as_ref().is_none_or(|v| v.is_empty())
492}
493
494#[cfg(not(feature = "test-send-sync"))]
496unsafe impl Send for TimelineEvent {}
497
498#[cfg(not(feature = "test-send-sync"))]
500unsafe impl Sync for TimelineEvent {}
501
502#[cfg(feature = "test-send-sync")]
503#[test]
504fn test_send_sync_for_sync_timeline_event() {
506 fn assert_send_sync<T: crate::SendOutsideWasm + crate::SyncOutsideWasm>() {}
507
508 assert_send_sync::<TimelineEvent>();
509}
510
511impl TimelineEvent {
512 pub fn from_plaintext(event: Raw<AnySyncTimelineEvent>) -> Self {
517 Self::new(TimelineEventKind::PlainText { event }, None)
518 }
519
520 pub fn from_decrypted(
522 decrypted: DecryptedRoomEvent,
523 push_actions: Option<Vec<Action>>,
524 ) -> Self {
525 Self::new(TimelineEventKind::Decrypted(decrypted), push_actions)
526 }
527
528 pub fn from_utd(event: Raw<AnySyncTimelineEvent>, utd_info: UnableToDecryptInfo) -> Self {
531 Self::new(TimelineEventKind::UnableToDecrypt { event, utd_info }, None)
532 }
533
534 fn new(kind: TimelineEventKind, push_actions: Option<Vec<Action>>) -> Self {
537 let (thread_summary, latest_thread_event) = extract_bundled_thread_summary(kind.raw());
538 let bundled_latest_thread_event =
539 Self::from_bundled_latest_event(&kind, latest_thread_event);
540 Self { kind, push_actions, thread_summary, bundled_latest_thread_event }
541 }
542
543 fn from_bundled_latest_event(
547 this: &TimelineEventKind,
548 latest_event: Option<Raw<AnySyncMessageLikeEvent>>,
549 ) -> Option<Box<Self>> {
550 let latest_event = latest_event?;
551
552 match this {
553 TimelineEventKind::Decrypted(decrypted) => {
554 if let Some(unsigned_decryption_result) =
555 decrypted.unsigned_encryption_info.as_ref().and_then(|unsigned_map| {
556 unsigned_map.get(&UnsignedEventLocation::RelationsThreadLatestEvent)
557 })
558 {
559 match unsigned_decryption_result {
560 UnsignedDecryptionResult::Decrypted(encryption_info) => {
561 return Some(Box::new(TimelineEvent::from_decrypted(
564 DecryptedRoomEvent {
565 event: latest_event.cast_unchecked(),
568 encryption_info: encryption_info.clone(),
569 unsigned_encryption_info: None,
573 },
574 None,
575 )));
576 }
577
578 UnsignedDecryptionResult::UnableToDecrypt(utd_info) => {
579 return Some(Box::new(TimelineEvent::from_utd(
581 latest_event.cast(),
582 utd_info.clone(),
583 )));
584 }
585 }
586 }
587 }
588
589 TimelineEventKind::UnableToDecrypt { .. } | TimelineEventKind::PlainText { .. } => {
590 }
592 }
593
594 match latest_event.get_field::<MessageLikeEventType>("type") {
595 Ok(None) => {
596 let event_id = latest_event.get_field::<OwnedEventId>("event_id").ok().flatten();
597 warn!(
598 ?event_id,
599 "couldn't deserialize bundled latest thread event: missing `type` field \
600 in bundled latest thread event"
601 );
602 None
603 }
604
605 Ok(Some(MessageLikeEventType::RoomEncrypted)) => {
606 Some(Box::new(TimelineEvent::from_utd(
610 latest_event.cast(),
611 UnableToDecryptInfo {
612 session_id: None,
613 reason: UnableToDecryptReason::Unknown,
614 },
615 )))
616 }
617
618 Ok(_) => Some(Box::new(TimelineEvent::from_plaintext(latest_event.cast()))),
619
620 Err(err) => {
621 let event_id = latest_event.get_field::<OwnedEventId>("event_id").ok().flatten();
622 warn!(?event_id, "couldn't deserialize bundled latest thread event's type: {err}");
623 None
624 }
625 }
626 }
627
628 pub fn push_actions(&self) -> Option<&[Action]> {
633 self.push_actions.as_deref()
634 }
635
636 pub fn set_push_actions(&mut self, push_actions: Vec<Action>) {
638 self.push_actions = Some(push_actions);
639 }
640
641 pub fn event_id(&self) -> Option<OwnedEventId> {
644 self.kind.event_id()
645 }
646
647 pub fn raw(&self) -> &Raw<AnySyncTimelineEvent> {
650 self.kind.raw()
651 }
652
653 pub fn replace_raw(&mut self, replacement: Raw<AnyMessageLikeEvent>) {
655 match &mut self.kind {
656 TimelineEventKind::Decrypted(decrypted) => decrypted.event = replacement,
657 TimelineEventKind::UnableToDecrypt { event, .. }
658 | TimelineEventKind::PlainText { event } => {
659 *event = replacement.cast();
662 }
663 }
664 }
665
666 pub fn encryption_info(&self) -> Option<&Arc<EncryptionInfo>> {
669 self.kind.encryption_info()
670 }
671
672 pub fn into_raw(self) -> Raw<AnySyncTimelineEvent> {
675 self.kind.into_raw()
676 }
677}
678
679impl<'de> Deserialize<'de> for TimelineEvent {
680 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
688 where
689 D: serde::Deserializer<'de>,
690 {
691 use serde_json::{Map, Value};
692
693 let value = Map::<String, Value>::deserialize(deserializer)?;
695
696 if value.contains_key("event") {
698 let v0: SyncTimelineEventDeserializationHelperV0 =
699 serde_json::from_value(Value::Object(value)).map_err(|e| {
700 serde::de::Error::custom(format!(
701 "Unable to deserialize V0-format TimelineEvent: {e}",
702 ))
703 })?;
704 Ok(v0.into())
705 }
706 else {
708 let v1: SyncTimelineEventDeserializationHelperV1 =
709 serde_json::from_value(Value::Object(value)).map_err(|e| {
710 serde::de::Error::custom(format!(
711 "Unable to deserialize V1-format TimelineEvent: {e}",
712 ))
713 })?;
714 Ok(v1.into())
715 }
716 }
717}
718
719#[derive(Clone, Serialize, Deserialize)]
721pub enum TimelineEventKind {
722 Decrypted(DecryptedRoomEvent),
724
725 UnableToDecrypt {
727 event: Raw<AnySyncTimelineEvent>,
731
732 utd_info: UnableToDecryptInfo,
734 },
735
736 PlainText {
738 event: Raw<AnySyncTimelineEvent>,
742 },
743}
744
745impl TimelineEventKind {
746 pub fn raw(&self) -> &Raw<AnySyncTimelineEvent> {
749 match self {
750 TimelineEventKind::Decrypted(d) => d.event.cast_ref(),
756 TimelineEventKind::UnableToDecrypt { event, .. } => event,
757 TimelineEventKind::PlainText { event } => event,
758 }
759 }
760
761 pub fn event_id(&self) -> Option<OwnedEventId> {
764 self.raw().get_field::<OwnedEventId>("event_id").ok().flatten()
765 }
766
767 pub fn encryption_info(&self) -> Option<&Arc<EncryptionInfo>> {
770 match self {
771 TimelineEventKind::Decrypted(d) => Some(&d.encryption_info),
772 TimelineEventKind::UnableToDecrypt { .. } | TimelineEventKind::PlainText { .. } => None,
773 }
774 }
775
776 pub fn unsigned_encryption_map(
779 &self,
780 ) -> Option<&BTreeMap<UnsignedEventLocation, UnsignedDecryptionResult>> {
781 match self {
782 TimelineEventKind::Decrypted(d) => d.unsigned_encryption_info.as_ref(),
783 TimelineEventKind::UnableToDecrypt { .. } | TimelineEventKind::PlainText { .. } => None,
784 }
785 }
786
787 pub fn into_raw(self) -> Raw<AnySyncTimelineEvent> {
790 match self {
791 TimelineEventKind::Decrypted(d) => d.event.cast(),
797 TimelineEventKind::UnableToDecrypt { event, .. } => event,
798 TimelineEventKind::PlainText { event } => event,
799 }
800 }
801
802 pub fn session_id(&self) -> Option<&str> {
805 match self {
806 TimelineEventKind::Decrypted(decrypted_room_event) => {
807 decrypted_room_event.encryption_info.session_id()
808 }
809 TimelineEventKind::UnableToDecrypt { utd_info, .. } => utd_info.session_id.as_deref(),
810 TimelineEventKind::PlainText { .. } => None,
811 }
812 }
813}
814
815#[cfg(not(tarpaulin_include))]
816impl fmt::Debug for TimelineEventKind {
817 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
818 match &self {
819 Self::PlainText { event } => f
820 .debug_struct("TimelineEventDecryptionResult::PlainText")
821 .field("event", &DebugRawEvent(event))
822 .finish(),
823
824 Self::UnableToDecrypt { event, utd_info } => f
825 .debug_struct("TimelineEventDecryptionResult::UnableToDecrypt")
826 .field("event", &DebugRawEvent(event))
827 .field("utd_info", &utd_info)
828 .finish(),
829
830 Self::Decrypted(decrypted) => {
831 f.debug_tuple("TimelineEventDecryptionResult::Decrypted").field(decrypted).finish()
832 }
833 }
834 }
835}
836
837#[derive(Clone, Serialize, Deserialize)]
838pub struct DecryptedRoomEvent {
840 pub event: Raw<AnyMessageLikeEvent>,
847
848 pub encryption_info: Arc<EncryptionInfo>,
850
851 #[serde(skip_serializing_if = "Option::is_none")]
856 pub unsigned_encryption_info: Option<BTreeMap<UnsignedEventLocation, UnsignedDecryptionResult>>,
857}
858
859#[cfg(not(tarpaulin_include))]
860impl fmt::Debug for DecryptedRoomEvent {
861 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
862 let DecryptedRoomEvent { event, encryption_info, unsigned_encryption_info } = self;
863
864 f.debug_struct("DecryptedRoomEvent")
865 .field("event", &DebugRawEvent(event))
866 .field("encryption_info", encryption_info)
867 .maybe_field("unsigned_encryption_info", unsigned_encryption_info)
868 .finish()
869 }
870}
871
872#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
874pub enum UnsignedEventLocation {
875 RelationsReplace,
878 RelationsThreadLatestEvent,
881}
882
883impl UnsignedEventLocation {
884 pub fn find_mut<'a>(&self, unsigned: &'a mut JsonObject) -> Option<&'a mut serde_json::Value> {
891 let relations = unsigned.get_mut("m.relations")?.as_object_mut()?;
892
893 match self {
894 Self::RelationsReplace => relations.get_mut("m.replace"),
895 Self::RelationsThreadLatestEvent => {
896 relations.get_mut("m.thread")?.as_object_mut()?.get_mut("latest_event")
897 }
898 }
899 }
900}
901
902#[derive(Debug, Clone, Serialize, Deserialize)]
904pub enum UnsignedDecryptionResult {
905 Decrypted(Arc<EncryptionInfo>),
907 UnableToDecrypt(UnableToDecryptInfo),
909}
910
911impl UnsignedDecryptionResult {
912 pub fn encryption_info(&self) -> Option<&Arc<EncryptionInfo>> {
915 match self {
916 Self::Decrypted(info) => Some(info),
917 Self::UnableToDecrypt(_) => None,
918 }
919 }
920}
921
922#[derive(Debug, Clone, Serialize, Deserialize)]
924pub struct UnableToDecryptInfo {
925 #[serde(skip_serializing_if = "Option::is_none")]
928 pub session_id: Option<String>,
929
930 #[serde(default = "unknown_utd_reason", deserialize_with = "deserialize_utd_reason")]
932 pub reason: UnableToDecryptReason,
933}
934
935fn unknown_utd_reason() -> UnableToDecryptReason {
936 UnableToDecryptReason::Unknown
937}
938
939pub fn deserialize_utd_reason<'de, D>(d: D) -> Result<UnableToDecryptReason, D::Error>
942where
943 D: serde::Deserializer<'de>,
944{
945 let v: serde_json::Value = Deserialize::deserialize(d)?;
947 if v.as_str().is_some_and(|s| s == "MissingMegolmSession") {
950 return Ok(UnableToDecryptReason::MissingMegolmSession { withheld_code: None });
951 }
952 serde_json::from_value::<UnableToDecryptReason>(v).map_err(serde::de::Error::custom)
955}
956
957#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
959pub enum UnableToDecryptReason {
960 #[doc(hidden)]
963 Unknown,
964
965 MalformedEncryptedEvent,
969
970 MissingMegolmSession {
973 withheld_code: Option<WithheldCode>,
976 },
977
978 UnknownMegolmMessageIndex,
981
982 MegolmDecryptionFailure,
989
990 PayloadDeserializationFailure,
992
993 MismatchedIdentityKeys,
997
998 SenderIdentityNotTrusted(VerificationLevel),
1002}
1003
1004impl UnableToDecryptReason {
1005 pub fn is_missing_room_key(&self) -> bool {
1008 matches!(
1011 self,
1012 Self::MissingMegolmSession { withheld_code: None } | Self::UnknownMegolmMessageIndex
1013 )
1014 }
1015}
1016
1017#[derive(
1021 Clone,
1022 PartialEq,
1023 Eq,
1024 Hash,
1025 AsStrAsRefStr,
1026 AsRefStr,
1027 FromString,
1028 DebugAsRefStr,
1029 SerializeAsRefStr,
1030 DeserializeFromCowStr,
1031)]
1032pub enum WithheldCode {
1033 #[ruma_enum(rename = "m.blacklisted")]
1035 Blacklisted,
1036
1037 #[ruma_enum(rename = "m.unverified")]
1039 Unverified,
1040
1041 #[ruma_enum(rename = "m.unauthorised")]
1045 Unauthorised,
1046
1047 #[ruma_enum(rename = "m.unavailable")]
1050 Unavailable,
1051
1052 #[ruma_enum(rename = "m.no_olm")]
1056 NoOlm,
1057
1058 #[doc(hidden)]
1059 _Custom(PrivOwnedStr),
1060}
1061
1062impl fmt::Display for WithheldCode {
1063 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
1064 let string = match self {
1065 WithheldCode::Blacklisted => "The sender has blocked you.",
1066 WithheldCode::Unverified => "The sender has disabled encrypting to unverified devices.",
1067 WithheldCode::Unauthorised => "You are not authorised to read the message.",
1068 WithheldCode::Unavailable => "The requested key was not found.",
1069 WithheldCode::NoOlm => "Unable to establish a secure channel.",
1070 _ => self.as_str(),
1071 };
1072
1073 f.write_str(string)
1074 }
1075}
1076
1077#[doc(hidden)]
1081#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
1082pub struct PrivOwnedStr(pub Box<str>);
1083
1084#[cfg(not(tarpaulin_include))]
1085impl fmt::Debug for PrivOwnedStr {
1086 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1087 self.0.fmt(f)
1088 }
1089}
1090
1091#[derive(Debug, Deserialize)]
1096struct SyncTimelineEventDeserializationHelperV1 {
1097 kind: TimelineEventKind,
1099
1100 #[serde(default)]
1102 push_actions: Vec<Action>,
1103
1104 #[serde(default)]
1106 thread_summary: ThreadSummaryStatus,
1107}
1108
1109impl From<SyncTimelineEventDeserializationHelperV1> for TimelineEvent {
1110 fn from(value: SyncTimelineEventDeserializationHelperV1) -> Self {
1111 let SyncTimelineEventDeserializationHelperV1 { kind, push_actions, thread_summary } = value;
1112 TimelineEvent {
1113 kind,
1114 push_actions: Some(push_actions),
1115 thread_summary,
1116 bundled_latest_thread_event: None,
1118 }
1119 }
1120}
1121
1122#[derive(Deserialize)]
1124struct SyncTimelineEventDeserializationHelperV0 {
1125 event: Raw<AnySyncTimelineEvent>,
1127
1128 encryption_info: Option<Arc<EncryptionInfo>>,
1132
1133 #[serde(default)]
1135 push_actions: Vec<Action>,
1136
1137 unsigned_encryption_info: Option<BTreeMap<UnsignedEventLocation, UnsignedDecryptionResult>>,
1142}
1143
1144impl From<SyncTimelineEventDeserializationHelperV0> for TimelineEvent {
1145 fn from(value: SyncTimelineEventDeserializationHelperV0) -> Self {
1146 let SyncTimelineEventDeserializationHelperV0 {
1147 event,
1148 encryption_info,
1149 push_actions,
1150 unsigned_encryption_info,
1151 } = value;
1152
1153 let kind = match encryption_info {
1154 Some(encryption_info) => {
1155 TimelineEventKind::Decrypted(DecryptedRoomEvent {
1156 event: event.cast_unchecked(),
1163 encryption_info,
1164 unsigned_encryption_info,
1165 })
1166 }
1167
1168 None => TimelineEventKind::PlainText { event },
1169 };
1170
1171 TimelineEvent {
1172 kind,
1173 push_actions: Some(push_actions),
1174 thread_summary: ThreadSummaryStatus::Unknown,
1176 bundled_latest_thread_event: None,
1178 }
1179 }
1180}
1181
1182#[derive(Debug, Clone, PartialEq)]
1184pub enum ToDeviceUnableToDecryptReason {
1185 DecryptionFailure,
1188
1189 UnverifiedSenderDevice,
1193
1194 NoOlmMachine,
1197
1198 EncryptionIsDisabled,
1200}
1201
1202#[derive(Clone, Debug)]
1204pub struct ToDeviceUnableToDecryptInfo {
1205 pub reason: ToDeviceUnableToDecryptReason,
1207}
1208
1209#[derive(Clone, Debug)]
1211pub enum ProcessedToDeviceEvent {
1212 Decrypted {
1215 raw: Raw<AnyToDeviceEvent>,
1217 encryption_info: EncryptionInfo,
1219 },
1220
1221 UnableToDecrypt {
1223 encrypted_event: Raw<AnyToDeviceEvent>,
1224 utd_info: ToDeviceUnableToDecryptInfo,
1225 },
1226
1227 PlainText(Raw<AnyToDeviceEvent>),
1229
1230 Invalid(Raw<AnyToDeviceEvent>),
1234}
1235
1236impl ProcessedToDeviceEvent {
1237 pub fn to_raw(&self) -> Raw<AnyToDeviceEvent> {
1240 match self {
1241 ProcessedToDeviceEvent::Decrypted { raw, .. } => raw.clone(),
1242 ProcessedToDeviceEvent::UnableToDecrypt { encrypted_event, .. } => {
1243 encrypted_event.clone()
1244 }
1245 ProcessedToDeviceEvent::PlainText(event) => event.clone(),
1246 ProcessedToDeviceEvent::Invalid(event) => event.clone(),
1247 }
1248 }
1249
1250 pub fn as_raw(&self) -> &Raw<AnyToDeviceEvent> {
1252 match self {
1253 ProcessedToDeviceEvent::Decrypted { raw, .. } => raw,
1254 ProcessedToDeviceEvent::UnableToDecrypt { encrypted_event, .. } => encrypted_event,
1255 ProcessedToDeviceEvent::PlainText(event) => event,
1256 ProcessedToDeviceEvent::Invalid(event) => event,
1257 }
1258 }
1259}
1260
1261#[cfg(test)]
1262mod tests {
1263 use std::{collections::BTreeMap, sync::Arc};
1264
1265 use assert_matches::assert_matches;
1266 use assert_matches2::assert_let;
1267 use insta::{assert_json_snapshot, with_settings};
1268 use ruma::{
1269 DeviceKeyAlgorithm, device_id, event_id, events::room::message::RoomMessageEventContent,
1270 serde::Raw, user_id,
1271 };
1272 use serde::Deserialize;
1273 use serde_json::json;
1274
1275 use super::{
1276 AlgorithmInfo, DecryptedRoomEvent, DeviceLinkProblem, EncryptionInfo, ShieldState,
1277 ShieldStateCode, TimelineEvent, TimelineEventKind, UnableToDecryptInfo,
1278 UnableToDecryptReason, UnsignedDecryptionResult, UnsignedEventLocation, VerificationLevel,
1279 VerificationState, WithheldCode,
1280 };
1281 use crate::deserialized_responses::{ThreadSummary, ThreadSummaryStatus};
1282
1283 fn example_event() -> serde_json::Value {
1284 json!({
1285 "content": RoomMessageEventContent::text_plain("secret"),
1286 "type": "m.room.message",
1287 "event_id": "$xxxxx:example.org",
1288 "room_id": "!someroom:example.com",
1289 "origin_server_ts": 2189,
1290 "sender": "@carl:example.com",
1291 })
1292 }
1293
1294 #[test]
1295 fn sync_timeline_debug_content() {
1296 let room_event =
1297 TimelineEvent::from_plaintext(Raw::new(&example_event()).unwrap().cast_unchecked());
1298 let debug_s = format!("{room_event:?}");
1299 assert!(
1300 !debug_s.contains("secret"),
1301 "Debug representation contains event content!\n{debug_s}"
1302 );
1303 }
1304
1305 #[test]
1306 fn old_verification_state_to_new_migration() {
1307 #[derive(Deserialize)]
1308 struct State {
1309 state: VerificationState,
1310 }
1311
1312 let state = json!({
1313 "state": "Trusted",
1314 });
1315 let deserialized: State =
1316 serde_json::from_value(state).expect("We can deserialize the old trusted value");
1317 assert_eq!(deserialized.state, VerificationState::Verified);
1318
1319 let state = json!({
1320 "state": "UnknownDevice",
1321 });
1322
1323 let deserialized: State =
1324 serde_json::from_value(state).expect("We can deserialize the old unknown device value");
1325
1326 assert_eq!(
1327 deserialized.state,
1328 VerificationState::Unverified(VerificationLevel::None(
1329 DeviceLinkProblem::MissingDevice
1330 ))
1331 );
1332
1333 let state = json!({
1334 "state": "Untrusted",
1335 });
1336 let deserialized: State =
1337 serde_json::from_value(state).expect("We can deserialize the old trusted value");
1338
1339 assert_eq!(
1340 deserialized.state,
1341 VerificationState::Unverified(VerificationLevel::UnsignedDevice)
1342 );
1343 }
1344
1345 #[test]
1346 fn test_verification_level_deserializes() {
1347 #[derive(Deserialize)]
1349 struct Container {
1350 verification_level: VerificationLevel,
1351 }
1352 let container = json!({ "verification_level": "VerificationViolation" });
1353
1354 let deserialized: Container = serde_json::from_value(container)
1356 .expect("We can deserialize the old PreviouslyVerified value");
1357
1358 assert_eq!(deserialized.verification_level, VerificationLevel::VerificationViolation);
1360 }
1361
1362 #[test]
1363 fn test_verification_level_deserializes_from_old_previously_verified_value() {
1364 #[derive(Deserialize)]
1366 struct Container {
1367 verification_level: VerificationLevel,
1368 }
1369 let container = json!({ "verification_level": "PreviouslyVerified" });
1370
1371 let deserialized: Container = serde_json::from_value(container)
1373 .expect("We can deserialize the old PreviouslyVerified value");
1374
1375 assert_eq!(deserialized.verification_level, VerificationLevel::VerificationViolation);
1377 }
1378
1379 #[test]
1380 fn test_shield_state_code_deserializes() {
1381 #[derive(Deserialize)]
1383 struct Container {
1384 shield_state_code: ShieldStateCode,
1385 }
1386 let container = json!({ "shield_state_code": "VerificationViolation" });
1387
1388 let deserialized: Container = serde_json::from_value(container)
1390 .expect("We can deserialize the old PreviouslyVerified value");
1391
1392 assert_eq!(deserialized.shield_state_code, ShieldStateCode::VerificationViolation);
1394 }
1395
1396 #[test]
1397 fn test_shield_state_code_deserializes_from_old_previously_verified_value() {
1398 #[derive(Deserialize)]
1400 struct Container {
1401 shield_state_code: ShieldStateCode,
1402 }
1403 let container = json!({ "shield_state_code": "PreviouslyVerified" });
1404
1405 let deserialized: Container = serde_json::from_value(container)
1407 .expect("We can deserialize the old PreviouslyVerified value");
1408
1409 assert_eq!(deserialized.shield_state_code, ShieldStateCode::VerificationViolation);
1411 }
1412
1413 #[test]
1414 fn sync_timeline_event_serialisation() {
1415 let room_event = TimelineEvent {
1416 kind: TimelineEventKind::Decrypted(DecryptedRoomEvent {
1417 event: Raw::new(&example_event()).unwrap().cast_unchecked(),
1418 encryption_info: Arc::new(EncryptionInfo {
1419 sender: user_id!("@sender:example.com").to_owned(),
1420 sender_device: None,
1421 algorithm_info: AlgorithmInfo::MegolmV1AesSha2 {
1422 curve25519_key: "xxx".to_owned(),
1423 sender_claimed_keys: Default::default(),
1424 session_id: Some("xyz".to_owned()),
1425 },
1426 verification_state: VerificationState::Verified,
1427 }),
1428 unsigned_encryption_info: Some(BTreeMap::from([(
1429 UnsignedEventLocation::RelationsReplace,
1430 UnsignedDecryptionResult::UnableToDecrypt(UnableToDecryptInfo {
1431 session_id: Some("xyz".to_owned()),
1432 reason: UnableToDecryptReason::MalformedEncryptedEvent,
1433 }),
1434 )])),
1435 }),
1436 push_actions: Default::default(),
1437 thread_summary: ThreadSummaryStatus::Unknown,
1438 bundled_latest_thread_event: None,
1439 };
1440
1441 let serialized = serde_json::to_value(&room_event).unwrap();
1442
1443 assert_eq!(
1445 serialized,
1446 json!({
1447 "kind": {
1448 "Decrypted": {
1449 "event": {
1450 "content": {"body": "secret", "msgtype": "m.text"},
1451 "event_id": "$xxxxx:example.org",
1452 "origin_server_ts": 2189,
1453 "room_id": "!someroom:example.com",
1454 "sender": "@carl:example.com",
1455 "type": "m.room.message",
1456 },
1457 "encryption_info": {
1458 "sender": "@sender:example.com",
1459 "sender_device": null,
1460 "algorithm_info": {
1461 "MegolmV1AesSha2": {
1462 "curve25519_key": "xxx",
1463 "sender_claimed_keys": {},
1464 "session_id": "xyz",
1465 }
1466 },
1467 "verification_state": "Verified",
1468 },
1469 "unsigned_encryption_info": {
1470 "RelationsReplace": {"UnableToDecrypt": {
1471 "session_id": "xyz",
1472 "reason": "MalformedEncryptedEvent",
1473 }}
1474 }
1475 }
1476 }
1477 })
1478 );
1479
1480 let event: TimelineEvent = serde_json::from_value(serialized).unwrap();
1482 assert_eq!(event.event_id(), Some(event_id!("$xxxxx:example.org").to_owned()));
1483 assert_matches!(
1484 event.encryption_info().unwrap().algorithm_info,
1485 AlgorithmInfo::MegolmV1AesSha2 { .. }
1486 );
1487
1488 let serialized = json!({
1490 "event": {
1491 "content": {"body": "secret", "msgtype": "m.text"},
1492 "event_id": "$xxxxx:example.org",
1493 "origin_server_ts": 2189,
1494 "room_id": "!someroom:example.com",
1495 "sender": "@carl:example.com",
1496 "type": "m.room.message",
1497 },
1498 "encryption_info": {
1499 "sender": "@sender:example.com",
1500 "sender_device": null,
1501 "algorithm_info": {
1502 "MegolmV1AesSha2": {
1503 "curve25519_key": "xxx",
1504 "sender_claimed_keys": {}
1505 }
1506 },
1507 "verification_state": "Verified",
1508 },
1509 });
1510 let event: TimelineEvent = serde_json::from_value(serialized).unwrap();
1511 assert_eq!(event.event_id(), Some(event_id!("$xxxxx:example.org").to_owned()));
1512 assert_matches!(
1513 event.encryption_info().unwrap().algorithm_info,
1514 AlgorithmInfo::MegolmV1AesSha2 { session_id: None, .. }
1515 );
1516
1517 let serialized = json!({
1520 "event": {
1521 "content": {"body": "secret", "msgtype": "m.text"},
1522 "event_id": "$xxxxx:example.org",
1523 "origin_server_ts": 2189,
1524 "room_id": "!someroom:example.com",
1525 "sender": "@carl:example.com",
1526 "type": "m.room.message",
1527 },
1528 "encryption_info": {
1529 "sender": "@sender:example.com",
1530 "sender_device": null,
1531 "algorithm_info": {
1532 "MegolmV1AesSha2": {
1533 "curve25519_key": "xxx",
1534 "sender_claimed_keys": {}
1535 }
1536 },
1537 "verification_state": "Verified",
1538 },
1539 "unsigned_encryption_info": {
1540 "RelationsReplace": {"UnableToDecrypt": {"session_id": "xyz"}}
1541 }
1542 });
1543 let event: TimelineEvent = serde_json::from_value(serialized).unwrap();
1544 assert_eq!(event.event_id(), Some(event_id!("$xxxxx:example.org").to_owned()));
1545 assert_matches!(
1546 event.encryption_info().unwrap().algorithm_info,
1547 AlgorithmInfo::MegolmV1AesSha2 { .. }
1548 );
1549 assert_matches!(event.kind, TimelineEventKind::Decrypted(decrypted) => {
1550 assert_matches!(decrypted.unsigned_encryption_info, Some(map) => {
1551 assert_eq!(map.len(), 1);
1552 let (location, result) = map.into_iter().next().unwrap();
1553 assert_eq!(location, UnsignedEventLocation::RelationsReplace);
1554 assert_matches!(result, UnsignedDecryptionResult::UnableToDecrypt(utd_info) => {
1555 assert_eq!(utd_info.session_id, Some("xyz".to_owned()));
1556 assert_eq!(utd_info.reason, UnableToDecryptReason::Unknown);
1557 })
1558 });
1559 });
1560 }
1561
1562 #[test]
1563 fn test_creating_or_deserializing_an_event_extracts_summary() {
1564 let event = json!({
1565 "event_id": "$eid:example.com",
1566 "type": "m.room.message",
1567 "sender": "@alice:example.com",
1568 "origin_server_ts": 42,
1569 "content": {
1570 "body": "Hello, world!",
1571 },
1572 "unsigned": {
1573 "m.relations": {
1574 "m.thread": {
1575 "latest_event": {
1576 "event_id": "$latest_event:example.com",
1577 "type": "m.room.message",
1578 "sender": "@bob:example.com",
1579 "origin_server_ts": 42,
1580 "content": {
1581 "body": "Hello to you too!",
1582 "msgtype": "m.text",
1583 }
1584 },
1585 "count": 2,
1586 "current_user_participated": true,
1587 }
1588 }
1589 }
1590 });
1591
1592 let raw = Raw::new(&event).unwrap().cast_unchecked();
1593
1594 let timeline_event = TimelineEvent::from_plaintext(raw);
1597 assert_matches!(timeline_event.thread_summary, ThreadSummaryStatus::Some(ThreadSummary { num_replies, latest_reply }) => {
1598 assert_eq!(num_replies, 2);
1599 assert_eq!(latest_reply.as_deref(), Some(event_id!("$latest_event:example.com")));
1600 });
1601
1602 assert!(timeline_event.bundled_latest_thread_event.is_some());
1603
1604 let serialized_timeline_item = json!({
1607 "kind": {
1608 "PlainText": {
1609 "event": event
1610 }
1611 }
1612 });
1613
1614 let timeline_event: TimelineEvent =
1615 serde_json::from_value(serialized_timeline_item).unwrap();
1616 assert_matches!(timeline_event.thread_summary, ThreadSummaryStatus::Unknown);
1617
1618 assert!(timeline_event.bundled_latest_thread_event.is_none());
1621 }
1622
1623 #[test]
1624 fn sync_timeline_event_deserialisation_migration_for_withheld() {
1625 let serialized = json!({
1642 "kind": {
1643 "UnableToDecrypt": {
1644 "event": {
1645 "content": {
1646 "algorithm": "m.megolm.v1.aes-sha2",
1647 "ciphertext": "AwgAEoABzL1JYhqhjW9jXrlT3M6H8mJ4qffYtOQOnPuAPNxsuG20oiD/Fnpv6jnQGhU6YbV9pNM+1mRnTvxW3CbWOPjLKqCWTJTc7Q0vDEVtYePg38ncXNcwMmfhgnNAoW9S7vNs8C003x3yUl6NeZ8bH+ci870BZL+kWM/lMl10tn6U7snNmSjnE3ckvRdO+11/R4//5VzFQpZdf4j036lNSls/WIiI67Fk9iFpinz9xdRVWJFVdrAiPFwb8L5xRZ8aX+e2JDMlc1eW8gk",
1648 "device_id": "SKCGPNUWAU",
1649 "sender_key": "Gim/c7uQdSXyrrUbmUOrBT6sMC0gO7QSLmOK6B7NOm0",
1650 "session_id": "hgLyeSqXfb8vc5AjQLsg6TSHVu0HJ7HZ4B6jgMvxkrs"
1651 },
1652 "event_id": "$xxxxx:example.org",
1653 "origin_server_ts": 2189,
1654 "room_id": "!someroom:example.com",
1655 "sender": "@carl:example.com",
1656 "type": "m.room.message"
1657 },
1658 "utd_info": {
1659 "reason": "MissingMegolmSession",
1660 "session_id": "session000"
1661 }
1662 }
1663 }
1664 });
1665
1666 let result = serde_json::from_value(serialized);
1667 assert!(result.is_ok());
1668
1669 let event: TimelineEvent = result.unwrap();
1671 assert_matches!(
1672 event.kind,
1673 TimelineEventKind::UnableToDecrypt { utd_info, .. }=> {
1674 assert_matches!(
1675 utd_info.reason,
1676 UnableToDecryptReason::MissingMegolmSession { withheld_code: None }
1677 );
1678 }
1679 )
1680 }
1681
1682 #[test]
1683 fn unable_to_decrypt_info_migration_for_withheld() {
1684 let old_format = json!({
1685 "reason": "MissingMegolmSession",
1686 "session_id": "session000"
1687 });
1688
1689 let deserialized = serde_json::from_value::<UnableToDecryptInfo>(old_format).unwrap();
1690 let session_id = Some("session000".to_owned());
1691
1692 assert_eq!(deserialized.session_id, session_id);
1693 assert_eq!(
1694 deserialized.reason,
1695 UnableToDecryptReason::MissingMegolmSession { withheld_code: None },
1696 );
1697
1698 let new_format = json!({
1699 "session_id": "session000",
1700 "reason": {
1701 "MissingMegolmSession": {
1702 "withheld_code": null
1703 }
1704 }
1705 });
1706
1707 let deserialized = serde_json::from_value::<UnableToDecryptInfo>(new_format).unwrap();
1708
1709 assert_eq!(
1710 deserialized.reason,
1711 UnableToDecryptReason::MissingMegolmSession { withheld_code: None },
1712 );
1713 assert_eq!(deserialized.session_id, session_id);
1714 }
1715
1716 #[test]
1717 fn unable_to_decrypt_reason_is_missing_room_key() {
1718 let reason = UnableToDecryptReason::MissingMegolmSession { withheld_code: None };
1719 assert!(reason.is_missing_room_key());
1720
1721 let reason = UnableToDecryptReason::MissingMegolmSession {
1722 withheld_code: Some(WithheldCode::Blacklisted),
1723 };
1724 assert!(!reason.is_missing_room_key());
1725
1726 let reason = UnableToDecryptReason::UnknownMegolmMessageIndex;
1727 assert!(reason.is_missing_room_key());
1728 }
1729
1730 #[test]
1731 fn snapshot_test_verification_level() {
1732 with_settings!({ prepend_module_to_snapshot => false }, {
1733 assert_json_snapshot!(VerificationLevel::VerificationViolation);
1734 assert_json_snapshot!(VerificationLevel::UnsignedDevice);
1735 assert_json_snapshot!(VerificationLevel::None(DeviceLinkProblem::InsecureSource));
1736 assert_json_snapshot!(VerificationLevel::None(DeviceLinkProblem::MissingDevice));
1737 assert_json_snapshot!(VerificationLevel::UnverifiedIdentity);
1738 });
1739 }
1740
1741 #[test]
1742 fn snapshot_test_verification_states() {
1743 with_settings!({ prepend_module_to_snapshot => false }, {
1744 assert_json_snapshot!(VerificationState::Unverified(VerificationLevel::UnsignedDevice));
1745 assert_json_snapshot!(VerificationState::Unverified(
1746 VerificationLevel::VerificationViolation
1747 ));
1748 assert_json_snapshot!(VerificationState::Unverified(VerificationLevel::None(
1749 DeviceLinkProblem::InsecureSource,
1750 )));
1751 assert_json_snapshot!(VerificationState::Unverified(VerificationLevel::None(
1752 DeviceLinkProblem::MissingDevice,
1753 )));
1754 assert_json_snapshot!(VerificationState::Verified);
1755 });
1756 }
1757
1758 #[test]
1759 fn snapshot_test_shield_states() {
1760 with_settings!({ prepend_module_to_snapshot => false }, {
1761 assert_json_snapshot!(ShieldState::None);
1762 assert_json_snapshot!(ShieldState::Red {
1763 code: ShieldStateCode::UnverifiedIdentity,
1764 message: "a message"
1765 });
1766 assert_json_snapshot!(ShieldState::Grey {
1767 code: ShieldStateCode::AuthenticityNotGuaranteed,
1768 message: "authenticity of this message cannot be guaranteed",
1769 });
1770 });
1771 }
1772
1773 #[test]
1774 fn snapshot_test_shield_codes() {
1775 with_settings!({ prepend_module_to_snapshot => false }, {
1776 assert_json_snapshot!(ShieldStateCode::AuthenticityNotGuaranteed);
1777 assert_json_snapshot!(ShieldStateCode::UnknownDevice);
1778 assert_json_snapshot!(ShieldStateCode::UnsignedDevice);
1779 assert_json_snapshot!(ShieldStateCode::UnverifiedIdentity);
1780 assert_json_snapshot!(ShieldStateCode::SentInClear);
1781 assert_json_snapshot!(ShieldStateCode::VerificationViolation);
1782 });
1783 }
1784
1785 #[test]
1786 fn snapshot_test_algorithm_info() {
1787 let mut map = BTreeMap::new();
1788 map.insert(DeviceKeyAlgorithm::Curve25519, "claimedclaimedcurve25519".to_owned());
1789 map.insert(DeviceKeyAlgorithm::Ed25519, "claimedclaimeded25519".to_owned());
1790 let info = AlgorithmInfo::MegolmV1AesSha2 {
1791 curve25519_key: "curvecurvecurve".into(),
1792 sender_claimed_keys: BTreeMap::from([
1793 (DeviceKeyAlgorithm::Curve25519, "claimedclaimedcurve25519".to_owned()),
1794 (DeviceKeyAlgorithm::Ed25519, "claimedclaimeded25519".to_owned()),
1795 ]),
1796 session_id: None,
1797 };
1798
1799 with_settings!({ prepend_module_to_snapshot => false }, {
1800 assert_json_snapshot!(info)
1801 });
1802 }
1803
1804 #[test]
1805 fn test_encryption_info_migration() {
1806 let old_format = json!({
1809 "sender": "@alice:localhost",
1810 "sender_device": "ABCDEFGH",
1811 "algorithm_info": {
1812 "MegolmV1AesSha2": {
1813 "curve25519_key": "curvecurvecurve",
1814 "sender_claimed_keys": {}
1815 }
1816 },
1817 "verification_state": "Verified",
1818 "session_id": "mysessionid76"
1819 });
1820
1821 let deserialized = serde_json::from_value::<EncryptionInfo>(old_format).unwrap();
1822 let expected_session_id = Some("mysessionid76".to_owned());
1823
1824 assert_let!(
1825 AlgorithmInfo::MegolmV1AesSha2 { session_id, .. } = deserialized.algorithm_info.clone()
1826 );
1827 assert_eq!(session_id, expected_session_id);
1828
1829 assert_json_snapshot!(deserialized);
1830 }
1831
1832 #[test]
1833 fn snapshot_test_encryption_info() {
1834 let info = EncryptionInfo {
1835 sender: user_id!("@alice:localhost").to_owned(),
1836 sender_device: Some(device_id!("ABCDEFGH").to_owned()),
1837 algorithm_info: AlgorithmInfo::MegolmV1AesSha2 {
1838 curve25519_key: "curvecurvecurve".into(),
1839 sender_claimed_keys: Default::default(),
1840 session_id: Some("mysessionid76".to_owned()),
1841 },
1842 verification_state: VerificationState::Verified,
1843 };
1844
1845 with_settings!({ sort_maps => true, prepend_module_to_snapshot => false }, {
1846 assert_json_snapshot!(info)
1847 })
1848 }
1849
1850 #[test]
1851 fn snapshot_test_sync_timeline_event() {
1852 let room_event = TimelineEvent {
1853 kind: TimelineEventKind::Decrypted(DecryptedRoomEvent {
1854 event: Raw::new(&example_event()).unwrap().cast_unchecked(),
1855 encryption_info: Arc::new(EncryptionInfo {
1856 sender: user_id!("@sender:example.com").to_owned(),
1857 sender_device: Some(device_id!("ABCDEFGHIJ").to_owned()),
1858 algorithm_info: AlgorithmInfo::MegolmV1AesSha2 {
1859 curve25519_key: "xxx".to_owned(),
1860 sender_claimed_keys: BTreeMap::from([
1861 (
1862 DeviceKeyAlgorithm::Ed25519,
1863 "I3YsPwqMZQXHkSQbjFNEs7b529uac2xBpI83eN3LUXo".to_owned(),
1864 ),
1865 (
1866 DeviceKeyAlgorithm::Curve25519,
1867 "qzdW3F5IMPFl0HQgz5w/L5Oi/npKUFn8Um84acIHfPY".to_owned(),
1868 ),
1869 ]),
1870 session_id: Some("mysessionid112".to_owned()),
1871 },
1872 verification_state: VerificationState::Verified,
1873 }),
1874 unsigned_encryption_info: Some(BTreeMap::from([(
1875 UnsignedEventLocation::RelationsThreadLatestEvent,
1876 UnsignedDecryptionResult::UnableToDecrypt(UnableToDecryptInfo {
1877 session_id: Some("xyz".to_owned()),
1878 reason: UnableToDecryptReason::MissingMegolmSession {
1879 withheld_code: Some(WithheldCode::Unverified),
1880 },
1881 }),
1882 )])),
1883 }),
1884 push_actions: Default::default(),
1885 thread_summary: ThreadSummaryStatus::Some(ThreadSummary {
1886 num_replies: 2,
1887 latest_reply: None,
1888 }),
1889 bundled_latest_thread_event: None,
1890 };
1891
1892 with_settings!({ sort_maps => true, prepend_module_to_snapshot => false }, {
1893 assert_json_snapshot! {
1896 serde_json::to_value(&room_event).unwrap(),
1897 }
1898 });
1899 }
1900}