1use std::{collections::BTreeMap, fmt, sync::Arc};
16
17#[cfg(doc)]
18use ruma::events::AnyTimelineEvent;
19use ruma::{
20 events::{AnyMessageLikeEvent, AnySyncTimelineEvent, AnyToDeviceEvent, MessageLikeEventType},
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};
29use tracing::warn;
30#[cfg(target_family = "wasm")]
31use wasm_bindgen::prelude::*;
32
33use crate::{
34 debug::{DebugRawEvent, DebugStructExt},
35 serde_helpers::extract_bundled_thread_summary,
36};
37
38const AUTHENTICITY_NOT_GUARANTEED: &str =
39 "The authenticity of this encrypted message can't be guaranteed on this device.";
40const UNVERIFIED_IDENTITY: &str = "Encrypted by an unverified user.";
41const VERIFICATION_VIOLATION: &str =
42 "Encrypted by a previously-verified user who is no longer verified.";
43const UNSIGNED_DEVICE: &str = "Encrypted by a device not verified by its owner.";
44const UNKNOWN_DEVICE: &str = "Encrypted by an unknown or deleted device.";
45const MISMATCHED_SENDER: &str = "\
46 The sender of the event does not match the owner of the device \
47 that created the Megolm session.";
48pub const SENT_IN_CLEAR: &str = "Not encrypted.";
49
50#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
53#[serde(from = "OldVerificationStateHelper")]
54pub enum VerificationState {
55 Verified,
60
61 Unverified(VerificationLevel),
66}
67
68#[derive(Clone, Debug, Deserialize)]
71enum OldVerificationStateHelper {
72 Untrusted,
73 UnknownDevice,
74 #[serde(alias = "Trusted")]
75 Verified,
76 Unverified(VerificationLevel),
77}
78
79impl From<OldVerificationStateHelper> for VerificationState {
80 fn from(value: OldVerificationStateHelper) -> Self {
81 match value {
82 OldVerificationStateHelper::Untrusted => {
85 VerificationState::Unverified(VerificationLevel::UnsignedDevice)
86 }
87 OldVerificationStateHelper::UnknownDevice => {
88 Self::Unverified(VerificationLevel::None(DeviceLinkProblem::MissingDevice))
89 }
90 OldVerificationStateHelper::Verified => Self::Verified,
91 OldVerificationStateHelper::Unverified(l) => Self::Unverified(l),
92 }
93 }
94}
95
96impl VerificationState {
97 pub fn to_shield_state_strict(&self) -> ShieldState {
104 match self {
105 VerificationState::Verified => ShieldState::None,
106 VerificationState::Unverified(level) => match level {
107 VerificationLevel::UnverifiedIdentity
108 | VerificationLevel::VerificationViolation
109 | VerificationLevel::UnsignedDevice => ShieldState::Red {
110 code: ShieldStateCode::UnverifiedIdentity,
111 message: UNVERIFIED_IDENTITY,
112 },
113 VerificationLevel::None(link) => match link {
114 DeviceLinkProblem::MissingDevice => ShieldState::Red {
115 code: ShieldStateCode::UnknownDevice,
116 message: UNKNOWN_DEVICE,
117 },
118 DeviceLinkProblem::InsecureSource => ShieldState::Red {
119 code: ShieldStateCode::AuthenticityNotGuaranteed,
120 message: AUTHENTICITY_NOT_GUARANTEED,
121 },
122 },
123 VerificationLevel::MismatchedSender => ShieldState::Red {
124 code: ShieldStateCode::MismatchedSender,
125 message: MISMATCHED_SENDER,
126 },
127 },
128 }
129 }
130
131 pub fn to_shield_state_lax(&self) -> ShieldState {
139 match self {
140 VerificationState::Verified => ShieldState::None,
141 VerificationState::Unverified(level) => match level {
142 VerificationLevel::UnverifiedIdentity => {
143 ShieldState::None
146 }
147 VerificationLevel::VerificationViolation => {
148 ShieldState::Red {
151 code: ShieldStateCode::VerificationViolation,
152 message: VERIFICATION_VIOLATION,
153 }
154 }
155 VerificationLevel::UnsignedDevice => {
156 ShieldState::Red {
158 code: ShieldStateCode::UnsignedDevice,
159 message: UNSIGNED_DEVICE,
160 }
161 }
162 VerificationLevel::None(link) => match link {
163 DeviceLinkProblem::MissingDevice => {
164 ShieldState::Red {
168 code: ShieldStateCode::UnknownDevice,
169 message: UNKNOWN_DEVICE,
170 }
171 }
172 DeviceLinkProblem::InsecureSource => {
173 ShieldState::Grey {
176 code: ShieldStateCode::AuthenticityNotGuaranteed,
177 message: AUTHENTICITY_NOT_GUARANTEED,
178 }
179 }
180 },
181 VerificationLevel::MismatchedSender => ShieldState::Red {
182 code: ShieldStateCode::MismatchedSender,
183 message: MISMATCHED_SENDER,
184 },
185 },
186 }
187 }
188}
189
190#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
193pub enum VerificationLevel {
194 UnverifiedIdentity,
196
197 #[serde(alias = "PreviouslyVerified")]
200 VerificationViolation,
201
202 UnsignedDevice,
205
206 None(DeviceLinkProblem),
212
213 MismatchedSender,
216}
217
218impl fmt::Display for VerificationLevel {
219 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
220 let display = match self {
221 VerificationLevel::UnverifiedIdentity => "The sender's identity was not verified",
222 VerificationLevel::VerificationViolation => {
223 "The sender's identity was previously verified but has changed"
224 }
225 VerificationLevel::UnsignedDevice => {
226 "The sending device was not signed by the user's identity"
227 }
228 VerificationLevel::None(..) => "The sending device is not known",
229 VerificationLevel::MismatchedSender => MISMATCHED_SENDER,
230 };
231 write!(f, "{display}")
232 }
233}
234
235#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
238pub enum DeviceLinkProblem {
239 MissingDevice,
243 InsecureSource,
246}
247
248#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq)]
251pub enum ShieldState {
252 Red {
255 code: ShieldStateCode,
257 message: &'static str,
259 },
260 Grey {
263 code: ShieldStateCode,
265 message: &'static str,
267 },
268 None,
270}
271
272#[derive(Clone, Copy, Debug, Deserialize, Serialize, Eq, PartialEq)]
274#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
275#[cfg_attr(target_family = "wasm", wasm_bindgen)]
276pub enum ShieldStateCode {
277 AuthenticityNotGuaranteed,
279 UnknownDevice,
281 UnsignedDevice,
283 UnverifiedIdentity,
285 SentInClear,
287 #[serde(alias = "PreviouslyVerified")]
289 VerificationViolation,
290 MismatchedSender,
293}
294
295#[derive(Clone, Debug, Deserialize, Serialize)]
297pub enum AlgorithmInfo {
298 MegolmV1AesSha2 {
300 curve25519_key: String,
303 sender_claimed_keys: BTreeMap<DeviceKeyAlgorithm, String>,
307
308 #[serde(default, skip_serializing_if = "Option::is_none")]
311 session_id: Option<String>,
312 },
313
314 OlmV1Curve25519AesSha2 {
316 curve25519_public_key_base64: String,
318 },
319}
320
321#[derive(Clone, Debug, Serialize)]
323pub struct EncryptionInfo {
324 pub sender: OwnedUserId,
327 pub sender_device: Option<OwnedDeviceId>,
330 pub algorithm_info: AlgorithmInfo,
332 pub verification_state: VerificationState,
339}
340
341impl EncryptionInfo {
342 pub fn session_id(&self) -> Option<&str> {
344 if let AlgorithmInfo::MegolmV1AesSha2 { session_id, .. } = &self.algorithm_info {
345 session_id.as_deref()
346 } else {
347 None
348 }
349 }
350}
351
352impl<'de> Deserialize<'de> for EncryptionInfo {
353 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
354 where
355 D: serde::Deserializer<'de>,
356 {
357 #[derive(Deserialize)]
360 struct Helper {
361 pub sender: OwnedUserId,
362 pub sender_device: Option<OwnedDeviceId>,
363 pub algorithm_info: AlgorithmInfo,
364 pub verification_state: VerificationState,
365 #[serde(rename = "session_id")]
366 pub old_session_id: Option<String>,
367 }
368
369 let Helper { sender, sender_device, algorithm_info, verification_state, old_session_id } =
370 Helper::deserialize(deserializer)?;
371
372 let algorithm_info = match algorithm_info {
373 AlgorithmInfo::MegolmV1AesSha2 { curve25519_key, sender_claimed_keys, session_id } => {
374 AlgorithmInfo::MegolmV1AesSha2 {
375 session_id: session_id.or(old_session_id),
377 curve25519_key,
378 sender_claimed_keys,
379 }
380 }
381 other => other,
382 };
383
384 Ok(EncryptionInfo { sender, sender_device, algorithm_info, verification_state })
385 }
386}
387
388#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
399pub struct ThreadSummary {
400 #[serde(skip_serializing_if = "Option::is_none")]
402 pub latest_reply: Option<OwnedEventId>,
403
404 pub num_replies: u32,
410}
411
412#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)]
414pub enum ThreadSummaryStatus {
415 #[default]
417 Unknown,
418 None,
420 Some(ThreadSummary),
422}
423
424impl ThreadSummaryStatus {
425 fn is_unknown(&self) -> bool {
427 matches!(self, ThreadSummaryStatus::Unknown)
428 }
429
430 pub fn summary(&self) -> Option<&ThreadSummary> {
433 match self {
434 ThreadSummaryStatus::Unknown | ThreadSummaryStatus::None => None,
435 ThreadSummaryStatus::Some(thread_summary) => Some(thread_summary),
436 }
437 }
438}
439
440#[derive(Clone, Debug, Serialize)]
463pub struct TimelineEvent {
464 pub kind: TimelineEventKind,
466
467 #[serde(skip_serializing_if = "skip_serialize_push_actions")]
472 push_actions: Option<Vec<Action>>,
473
474 #[serde(default, skip_serializing_if = "ThreadSummaryStatus::is_unknown")]
476 pub thread_summary: ThreadSummaryStatus,
477
478 #[serde(skip)]
483 pub bundled_latest_thread_event: Option<Box<TimelineEvent>>,
484}
485
486fn skip_serialize_push_actions(push_actions: &Option<Vec<Action>>) -> bool {
488 push_actions.as_ref().is_none_or(|v| v.is_empty())
489}
490
491#[cfg(not(feature = "test-send-sync"))]
493unsafe impl Send for TimelineEvent {}
494
495#[cfg(not(feature = "test-send-sync"))]
497unsafe impl Sync for TimelineEvent {}
498
499#[cfg(feature = "test-send-sync")]
500#[test]
501fn test_send_sync_for_sync_timeline_event() {
503 fn assert_send_sync<T: crate::SendOutsideWasm + crate::SyncOutsideWasm>() {}
504
505 assert_send_sync::<TimelineEvent>();
506}
507
508impl TimelineEvent {
509 pub fn from_plaintext(event: Raw<AnySyncTimelineEvent>) -> Self {
514 Self::new(TimelineEventKind::PlainText { event }, None)
515 }
516
517 pub fn from_decrypted(
519 decrypted: DecryptedRoomEvent,
520 push_actions: Option<Vec<Action>>,
521 ) -> Self {
522 Self::new(TimelineEventKind::Decrypted(decrypted), push_actions)
523 }
524
525 pub fn from_utd(event: Raw<AnySyncTimelineEvent>, utd_info: UnableToDecryptInfo) -> Self {
528 Self::new(TimelineEventKind::UnableToDecrypt { event, utd_info }, None)
529 }
530
531 fn new(kind: TimelineEventKind, push_actions: Option<Vec<Action>>) -> Self {
534 let (thread_summary, latest_thread_event) = extract_bundled_thread_summary(kind.raw());
535 let bundled_latest_thread_event =
536 Self::from_bundled_latest_event(&kind, latest_thread_event);
537 Self { kind, push_actions, thread_summary, bundled_latest_thread_event }
538 }
539
540 fn from_bundled_latest_event(
544 this: &TimelineEventKind,
545 latest_event: Option<Raw<AnyMessageLikeEvent>>,
546 ) -> Option<Box<Self>> {
547 let latest_event = latest_event?;
548
549 match this {
550 TimelineEventKind::Decrypted(decrypted) => {
551 if let Some(unsigned_decryption_result) =
552 decrypted.unsigned_encryption_info.as_ref().and_then(|unsigned_map| {
553 unsigned_map.get(&UnsignedEventLocation::RelationsThreadLatestEvent)
554 })
555 {
556 match unsigned_decryption_result {
557 UnsignedDecryptionResult::Decrypted(encryption_info) => {
558 return Some(Box::new(TimelineEvent::from_decrypted(
561 DecryptedRoomEvent {
562 event: latest_event,
563 encryption_info: encryption_info.clone(),
564 unsigned_encryption_info: None,
568 },
569 None,
570 )));
571 }
572
573 UnsignedDecryptionResult::UnableToDecrypt(utd_info) => {
574 return Some(Box::new(TimelineEvent::from_utd(
576 latest_event.cast(),
577 utd_info.clone(),
578 )));
579 }
580 }
581 }
582 }
583
584 TimelineEventKind::UnableToDecrypt { .. } | TimelineEventKind::PlainText { .. } => {
585 }
587 }
588
589 match latest_event.get_field::<MessageLikeEventType>("type") {
590 Ok(None) => {
591 let event_id = latest_event.get_field::<OwnedEventId>("event_id").ok().flatten();
592 warn!(
593 ?event_id,
594 "couldn't deserialize bundled latest thread event: missing `type` field \
595 in bundled latest thread event"
596 );
597 None
598 }
599
600 Ok(Some(MessageLikeEventType::RoomEncrypted)) => {
601 Some(Box::new(TimelineEvent::from_utd(
605 latest_event.cast(),
606 UnableToDecryptInfo {
607 session_id: None,
608 reason: UnableToDecryptReason::Unknown,
609 },
610 )))
611 }
612
613 Ok(_) => Some(Box::new(TimelineEvent::from_plaintext(latest_event.cast()))),
614
615 Err(err) => {
616 let event_id = latest_event.get_field::<OwnedEventId>("event_id").ok().flatten();
617 warn!(?event_id, "couldn't deserialize bundled latest thread event's type: {err}");
618 None
619 }
620 }
621 }
622
623 pub fn push_actions(&self) -> Option<&[Action]> {
628 self.push_actions.as_deref()
629 }
630
631 pub fn set_push_actions(&mut self, push_actions: Vec<Action>) {
633 self.push_actions = Some(push_actions);
634 }
635
636 pub fn event_id(&self) -> Option<OwnedEventId> {
639 self.kind.event_id()
640 }
641
642 pub fn raw(&self) -> &Raw<AnySyncTimelineEvent> {
645 self.kind.raw()
646 }
647
648 pub fn replace_raw(&mut self, replacement: Raw<AnyMessageLikeEvent>) {
650 match &mut self.kind {
651 TimelineEventKind::Decrypted(decrypted) => decrypted.event = replacement,
652 TimelineEventKind::UnableToDecrypt { event, .. }
653 | TimelineEventKind::PlainText { event } => {
654 *event = replacement.cast();
657 }
658 }
659 }
660
661 pub fn encryption_info(&self) -> Option<&Arc<EncryptionInfo>> {
664 self.kind.encryption_info()
665 }
666
667 pub fn into_raw(self) -> Raw<AnySyncTimelineEvent> {
670 self.kind.into_raw()
671 }
672}
673
674impl<'de> Deserialize<'de> for TimelineEvent {
675 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
683 where
684 D: serde::Deserializer<'de>,
685 {
686 use serde_json::{Map, Value};
687
688 let value = Map::<String, Value>::deserialize(deserializer)?;
690
691 if value.contains_key("event") {
693 let v0: SyncTimelineEventDeserializationHelperV0 =
694 serde_json::from_value(Value::Object(value)).map_err(|e| {
695 serde::de::Error::custom(format!(
696 "Unable to deserialize V0-format TimelineEvent: {e}",
697 ))
698 })?;
699 Ok(v0.into())
700 }
701 else {
703 let v1: SyncTimelineEventDeserializationHelperV1 =
704 serde_json::from_value(Value::Object(value)).map_err(|e| {
705 serde::de::Error::custom(format!(
706 "Unable to deserialize V1-format TimelineEvent: {e}",
707 ))
708 })?;
709 Ok(v1.into())
710 }
711 }
712}
713
714#[derive(Clone, Serialize, Deserialize)]
716pub enum TimelineEventKind {
717 Decrypted(DecryptedRoomEvent),
719
720 UnableToDecrypt {
722 event: Raw<AnySyncTimelineEvent>,
726
727 utd_info: UnableToDecryptInfo,
729 },
730
731 PlainText {
733 event: Raw<AnySyncTimelineEvent>,
737 },
738}
739
740impl TimelineEventKind {
741 pub fn raw(&self) -> &Raw<AnySyncTimelineEvent> {
744 match self {
745 TimelineEventKind::Decrypted(d) => d.event.cast_ref(),
751 TimelineEventKind::UnableToDecrypt { event, .. } => event.cast_ref(),
752 TimelineEventKind::PlainText { event } => event,
753 }
754 }
755
756 pub fn event_id(&self) -> Option<OwnedEventId> {
759 self.raw().get_field::<OwnedEventId>("event_id").ok().flatten()
760 }
761
762 pub fn encryption_info(&self) -> Option<&Arc<EncryptionInfo>> {
765 match self {
766 TimelineEventKind::Decrypted(d) => Some(&d.encryption_info),
767 TimelineEventKind::UnableToDecrypt { .. } | TimelineEventKind::PlainText { .. } => None,
768 }
769 }
770
771 pub fn unsigned_encryption_map(
774 &self,
775 ) -> Option<&BTreeMap<UnsignedEventLocation, UnsignedDecryptionResult>> {
776 match self {
777 TimelineEventKind::Decrypted(d) => d.unsigned_encryption_info.as_ref(),
778 TimelineEventKind::UnableToDecrypt { .. } | TimelineEventKind::PlainText { .. } => None,
779 }
780 }
781
782 pub fn into_raw(self) -> Raw<AnySyncTimelineEvent> {
785 match self {
786 TimelineEventKind::Decrypted(d) => d.event.cast(),
792 TimelineEventKind::UnableToDecrypt { event, .. } => event.cast(),
793 TimelineEventKind::PlainText { event } => event,
794 }
795 }
796
797 pub fn session_id(&self) -> Option<&str> {
800 match self {
801 TimelineEventKind::Decrypted(decrypted_room_event) => {
802 decrypted_room_event.encryption_info.session_id()
803 }
804 TimelineEventKind::UnableToDecrypt { utd_info, .. } => utd_info.session_id.as_deref(),
805 TimelineEventKind::PlainText { .. } => None,
806 }
807 }
808}
809
810#[cfg(not(tarpaulin_include))]
811impl fmt::Debug for TimelineEventKind {
812 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
813 match &self {
814 Self::PlainText { event } => f
815 .debug_struct("TimelineEventDecryptionResult::PlainText")
816 .field("event", &DebugRawEvent(event))
817 .finish(),
818
819 Self::UnableToDecrypt { event, utd_info } => f
820 .debug_struct("TimelineEventDecryptionResult::UnableToDecrypt")
821 .field("event", &DebugRawEvent(event))
822 .field("utd_info", &utd_info)
823 .finish(),
824
825 Self::Decrypted(decrypted) => {
826 f.debug_tuple("TimelineEventDecryptionResult::Decrypted").field(decrypted).finish()
827 }
828 }
829 }
830}
831
832#[derive(Clone, Serialize, Deserialize)]
833pub struct DecryptedRoomEvent {
835 pub event: Raw<AnyMessageLikeEvent>,
842
843 pub encryption_info: Arc<EncryptionInfo>,
845
846 #[serde(skip_serializing_if = "Option::is_none")]
851 pub unsigned_encryption_info: Option<BTreeMap<UnsignedEventLocation, UnsignedDecryptionResult>>,
852}
853
854#[cfg(not(tarpaulin_include))]
855impl fmt::Debug for DecryptedRoomEvent {
856 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
857 let DecryptedRoomEvent { event, encryption_info, unsigned_encryption_info } = self;
858
859 f.debug_struct("DecryptedRoomEvent")
860 .field("event", &DebugRawEvent(event))
861 .field("encryption_info", encryption_info)
862 .maybe_field("unsigned_encryption_info", unsigned_encryption_info)
863 .finish()
864 }
865}
866
867#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
869pub enum UnsignedEventLocation {
870 RelationsReplace,
873 RelationsThreadLatestEvent,
876}
877
878impl UnsignedEventLocation {
879 pub fn find_mut<'a>(&self, unsigned: &'a mut JsonObject) -> Option<&'a mut serde_json::Value> {
886 let relations = unsigned.get_mut("m.relations")?.as_object_mut()?;
887
888 match self {
889 Self::RelationsReplace => relations.get_mut("m.replace"),
890 Self::RelationsThreadLatestEvent => {
891 relations.get_mut("m.thread")?.as_object_mut()?.get_mut("latest_event")
892 }
893 }
894 }
895}
896
897#[derive(Debug, Clone, Serialize, Deserialize)]
899pub enum UnsignedDecryptionResult {
900 Decrypted(Arc<EncryptionInfo>),
902 UnableToDecrypt(UnableToDecryptInfo),
904}
905
906impl UnsignedDecryptionResult {
907 pub fn encryption_info(&self) -> Option<&Arc<EncryptionInfo>> {
910 match self {
911 Self::Decrypted(info) => Some(info),
912 Self::UnableToDecrypt(_) => None,
913 }
914 }
915}
916
917#[derive(Debug, Clone, Serialize, Deserialize)]
919pub struct UnableToDecryptInfo {
920 #[serde(skip_serializing_if = "Option::is_none")]
923 pub session_id: Option<String>,
924
925 #[serde(default = "unknown_utd_reason", deserialize_with = "deserialize_utd_reason")]
927 pub reason: UnableToDecryptReason,
928}
929
930fn unknown_utd_reason() -> UnableToDecryptReason {
931 UnableToDecryptReason::Unknown
932}
933
934pub fn deserialize_utd_reason<'de, D>(d: D) -> Result<UnableToDecryptReason, D::Error>
937where
938 D: serde::Deserializer<'de>,
939{
940 let v: serde_json::Value = Deserialize::deserialize(d)?;
942 if v.as_str().is_some_and(|s| s == "MissingMegolmSession") {
945 return Ok(UnableToDecryptReason::MissingMegolmSession { withheld_code: None });
946 }
947 serde_json::from_value::<UnableToDecryptReason>(v).map_err(serde::de::Error::custom)
950}
951
952#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
954pub enum UnableToDecryptReason {
955 #[doc(hidden)]
958 Unknown,
959
960 MalformedEncryptedEvent,
964
965 MissingMegolmSession {
968 withheld_code: Option<WithheldCode>,
971 },
972
973 UnknownMegolmMessageIndex,
976
977 MegolmDecryptionFailure,
984
985 PayloadDeserializationFailure,
987
988 MismatchedIdentityKeys,
992
993 SenderIdentityNotTrusted(VerificationLevel),
997}
998
999impl UnableToDecryptReason {
1000 pub fn is_missing_room_key(&self) -> bool {
1003 matches!(
1006 self,
1007 Self::MissingMegolmSession { withheld_code: None } | Self::UnknownMegolmMessageIndex
1008 )
1009 }
1010}
1011
1012#[derive(
1016 Clone,
1017 PartialEq,
1018 Eq,
1019 Hash,
1020 AsStrAsRefStr,
1021 AsRefStr,
1022 FromString,
1023 DebugAsRefStr,
1024 SerializeAsRefStr,
1025 DeserializeFromCowStr,
1026)]
1027pub enum WithheldCode {
1028 #[ruma_enum(rename = "m.blacklisted")]
1030 Blacklisted,
1031
1032 #[ruma_enum(rename = "m.unverified")]
1034 Unverified,
1035
1036 #[ruma_enum(rename = "m.unauthorised")]
1040 Unauthorised,
1041
1042 #[ruma_enum(rename = "m.unavailable")]
1045 Unavailable,
1046
1047 #[ruma_enum(rename = "m.no_olm")]
1051 NoOlm,
1052
1053 #[doc(hidden)]
1054 _Custom(PrivOwnedStr),
1055}
1056
1057impl fmt::Display for WithheldCode {
1058 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
1059 let string = match self {
1060 WithheldCode::Blacklisted => "The sender has blocked you.",
1061 WithheldCode::Unverified => "The sender has disabled encrypting to unverified devices.",
1062 WithheldCode::Unauthorised => "You are not authorised to read the message.",
1063 WithheldCode::Unavailable => "The requested key was not found.",
1064 WithheldCode::NoOlm => "Unable to establish a secure channel.",
1065 _ => self.as_str(),
1066 };
1067
1068 f.write_str(string)
1069 }
1070}
1071
1072#[doc(hidden)]
1076#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
1077pub struct PrivOwnedStr(pub Box<str>);
1078
1079#[cfg(not(tarpaulin_include))]
1080impl fmt::Debug for PrivOwnedStr {
1081 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1082 self.0.fmt(f)
1083 }
1084}
1085
1086#[derive(Debug, Deserialize)]
1091struct SyncTimelineEventDeserializationHelperV1 {
1092 kind: TimelineEventKind,
1094
1095 #[serde(default)]
1097 push_actions: Vec<Action>,
1098
1099 #[serde(default)]
1101 thread_summary: ThreadSummaryStatus,
1102}
1103
1104impl From<SyncTimelineEventDeserializationHelperV1> for TimelineEvent {
1105 fn from(value: SyncTimelineEventDeserializationHelperV1) -> Self {
1106 let SyncTimelineEventDeserializationHelperV1 { kind, push_actions, thread_summary } = value;
1107 TimelineEvent {
1108 kind,
1109 push_actions: Some(push_actions),
1110 thread_summary,
1111 bundled_latest_thread_event: None,
1113 }
1114 }
1115}
1116
1117#[derive(Deserialize)]
1119struct SyncTimelineEventDeserializationHelperV0 {
1120 event: Raw<AnySyncTimelineEvent>,
1122
1123 encryption_info: Option<Arc<EncryptionInfo>>,
1127
1128 #[serde(default)]
1130 push_actions: Vec<Action>,
1131
1132 unsigned_encryption_info: Option<BTreeMap<UnsignedEventLocation, UnsignedDecryptionResult>>,
1137}
1138
1139impl From<SyncTimelineEventDeserializationHelperV0> for TimelineEvent {
1140 fn from(value: SyncTimelineEventDeserializationHelperV0) -> Self {
1141 let SyncTimelineEventDeserializationHelperV0 {
1142 event,
1143 encryption_info,
1144 push_actions,
1145 unsigned_encryption_info,
1146 } = value;
1147
1148 let kind = match encryption_info {
1149 Some(encryption_info) => {
1150 TimelineEventKind::Decrypted(DecryptedRoomEvent {
1151 event: event.cast(),
1158 encryption_info,
1159 unsigned_encryption_info,
1160 })
1161 }
1162
1163 None => TimelineEventKind::PlainText { event },
1164 };
1165
1166 TimelineEvent {
1167 kind,
1168 push_actions: Some(push_actions),
1169 thread_summary: ThreadSummaryStatus::Unknown,
1171 bundled_latest_thread_event: None,
1173 }
1174 }
1175}
1176
1177#[derive(Clone, Debug)]
1179pub enum ProcessedToDeviceEvent {
1180 Decrypted {
1183 raw: Raw<AnyToDeviceEvent>,
1185 encryption_info: EncryptionInfo,
1187 },
1188
1189 UnableToDecrypt(Raw<AnyToDeviceEvent>),
1191
1192 PlainText(Raw<AnyToDeviceEvent>),
1194
1195 Invalid(Raw<AnyToDeviceEvent>),
1199}
1200
1201impl ProcessedToDeviceEvent {
1202 pub fn to_raw(&self) -> Raw<AnyToDeviceEvent> {
1205 match self {
1206 ProcessedToDeviceEvent::Decrypted { raw, .. } => raw.clone(),
1207 ProcessedToDeviceEvent::UnableToDecrypt(event) => event.clone(),
1208 ProcessedToDeviceEvent::PlainText(event) => event.clone(),
1209 ProcessedToDeviceEvent::Invalid(event) => event.clone(),
1210 }
1211 }
1212
1213 pub fn as_raw(&self) -> &Raw<AnyToDeviceEvent> {
1215 match self {
1216 ProcessedToDeviceEvent::Decrypted { raw, .. } => raw,
1217 ProcessedToDeviceEvent::UnableToDecrypt(event) => event,
1218 ProcessedToDeviceEvent::PlainText(event) => event,
1219 ProcessedToDeviceEvent::Invalid(event) => event,
1220 }
1221 }
1222}
1223
1224#[cfg(test)]
1225mod tests {
1226 use std::{collections::BTreeMap, sync::Arc};
1227
1228 use assert_matches::assert_matches;
1229 use assert_matches2::assert_let;
1230 use insta::{assert_json_snapshot, with_settings};
1231 use ruma::{
1232 device_id, event_id, events::room::message::RoomMessageEventContent, serde::Raw, user_id,
1233 DeviceKeyAlgorithm,
1234 };
1235 use serde::Deserialize;
1236 use serde_json::json;
1237
1238 use super::{
1239 AlgorithmInfo, DecryptedRoomEvent, DeviceLinkProblem, EncryptionInfo, ShieldState,
1240 ShieldStateCode, TimelineEvent, TimelineEventKind, UnableToDecryptInfo,
1241 UnableToDecryptReason, UnsignedDecryptionResult, UnsignedEventLocation, VerificationLevel,
1242 VerificationState, WithheldCode,
1243 };
1244 use crate::deserialized_responses::{ThreadSummary, ThreadSummaryStatus};
1245
1246 fn example_event() -> serde_json::Value {
1247 json!({
1248 "content": RoomMessageEventContent::text_plain("secret"),
1249 "type": "m.room.message",
1250 "event_id": "$xxxxx:example.org",
1251 "room_id": "!someroom:example.com",
1252 "origin_server_ts": 2189,
1253 "sender": "@carl:example.com",
1254 })
1255 }
1256
1257 #[test]
1258 fn sync_timeline_debug_content() {
1259 let room_event = TimelineEvent::from_plaintext(Raw::new(&example_event()).unwrap().cast());
1260 let debug_s = format!("{room_event:?}");
1261 assert!(
1262 !debug_s.contains("secret"),
1263 "Debug representation contains event content!\n{debug_s}"
1264 );
1265 }
1266
1267 #[test]
1268 fn old_verification_state_to_new_migration() {
1269 #[derive(Deserialize)]
1270 struct State {
1271 state: VerificationState,
1272 }
1273
1274 let state = json!({
1275 "state": "Trusted",
1276 });
1277 let deserialized: State =
1278 serde_json::from_value(state).expect("We can deserialize the old trusted value");
1279 assert_eq!(deserialized.state, VerificationState::Verified);
1280
1281 let state = json!({
1282 "state": "UnknownDevice",
1283 });
1284
1285 let deserialized: State =
1286 serde_json::from_value(state).expect("We can deserialize the old unknown device value");
1287
1288 assert_eq!(
1289 deserialized.state,
1290 VerificationState::Unverified(VerificationLevel::None(
1291 DeviceLinkProblem::MissingDevice
1292 ))
1293 );
1294
1295 let state = json!({
1296 "state": "Untrusted",
1297 });
1298 let deserialized: State =
1299 serde_json::from_value(state).expect("We can deserialize the old trusted value");
1300
1301 assert_eq!(
1302 deserialized.state,
1303 VerificationState::Unverified(VerificationLevel::UnsignedDevice)
1304 );
1305 }
1306
1307 #[test]
1308 fn test_verification_level_deserializes() {
1309 #[derive(Deserialize)]
1311 struct Container {
1312 verification_level: VerificationLevel,
1313 }
1314 let container = json!({ "verification_level": "VerificationViolation" });
1315
1316 let deserialized: Container = serde_json::from_value(container)
1318 .expect("We can deserialize the old PreviouslyVerified value");
1319
1320 assert_eq!(deserialized.verification_level, VerificationLevel::VerificationViolation);
1322 }
1323
1324 #[test]
1325 fn test_verification_level_deserializes_from_old_previously_verified_value() {
1326 #[derive(Deserialize)]
1328 struct Container {
1329 verification_level: VerificationLevel,
1330 }
1331 let container = json!({ "verification_level": "PreviouslyVerified" });
1332
1333 let deserialized: Container = serde_json::from_value(container)
1335 .expect("We can deserialize the old PreviouslyVerified value");
1336
1337 assert_eq!(deserialized.verification_level, VerificationLevel::VerificationViolation);
1339 }
1340
1341 #[test]
1342 fn test_shield_state_code_deserializes() {
1343 #[derive(Deserialize)]
1345 struct Container {
1346 shield_state_code: ShieldStateCode,
1347 }
1348 let container = json!({ "shield_state_code": "VerificationViolation" });
1349
1350 let deserialized: Container = serde_json::from_value(container)
1352 .expect("We can deserialize the old PreviouslyVerified value");
1353
1354 assert_eq!(deserialized.shield_state_code, ShieldStateCode::VerificationViolation);
1356 }
1357
1358 #[test]
1359 fn test_shield_state_code_deserializes_from_old_previously_verified_value() {
1360 #[derive(Deserialize)]
1362 struct Container {
1363 shield_state_code: ShieldStateCode,
1364 }
1365 let container = json!({ "shield_state_code": "PreviouslyVerified" });
1366
1367 let deserialized: Container = serde_json::from_value(container)
1369 .expect("We can deserialize the old PreviouslyVerified value");
1370
1371 assert_eq!(deserialized.shield_state_code, ShieldStateCode::VerificationViolation);
1373 }
1374
1375 #[test]
1376 fn sync_timeline_event_serialisation() {
1377 let room_event = TimelineEvent {
1378 kind: TimelineEventKind::Decrypted(DecryptedRoomEvent {
1379 event: Raw::new(&example_event()).unwrap().cast(),
1380 encryption_info: Arc::new(EncryptionInfo {
1381 sender: user_id!("@sender:example.com").to_owned(),
1382 sender_device: None,
1383 algorithm_info: AlgorithmInfo::MegolmV1AesSha2 {
1384 curve25519_key: "xxx".to_owned(),
1385 sender_claimed_keys: Default::default(),
1386 session_id: Some("xyz".to_owned()),
1387 },
1388 verification_state: VerificationState::Verified,
1389 }),
1390 unsigned_encryption_info: Some(BTreeMap::from([(
1391 UnsignedEventLocation::RelationsReplace,
1392 UnsignedDecryptionResult::UnableToDecrypt(UnableToDecryptInfo {
1393 session_id: Some("xyz".to_owned()),
1394 reason: UnableToDecryptReason::MalformedEncryptedEvent,
1395 }),
1396 )])),
1397 }),
1398 push_actions: Default::default(),
1399 thread_summary: ThreadSummaryStatus::Unknown,
1400 bundled_latest_thread_event: None,
1401 };
1402
1403 let serialized = serde_json::to_value(&room_event).unwrap();
1404
1405 assert_eq!(
1407 serialized,
1408 json!({
1409 "kind": {
1410 "Decrypted": {
1411 "event": {
1412 "content": {"body": "secret", "msgtype": "m.text"},
1413 "event_id": "$xxxxx:example.org",
1414 "origin_server_ts": 2189,
1415 "room_id": "!someroom:example.com",
1416 "sender": "@carl:example.com",
1417 "type": "m.room.message",
1418 },
1419 "encryption_info": {
1420 "sender": "@sender:example.com",
1421 "sender_device": null,
1422 "algorithm_info": {
1423 "MegolmV1AesSha2": {
1424 "curve25519_key": "xxx",
1425 "sender_claimed_keys": {},
1426 "session_id": "xyz",
1427 }
1428 },
1429 "verification_state": "Verified",
1430 },
1431 "unsigned_encryption_info": {
1432 "RelationsReplace": {"UnableToDecrypt": {
1433 "session_id": "xyz",
1434 "reason": "MalformedEncryptedEvent",
1435 }}
1436 }
1437 }
1438 }
1439 })
1440 );
1441
1442 let event: TimelineEvent = serde_json::from_value(serialized).unwrap();
1444 assert_eq!(event.event_id(), Some(event_id!("$xxxxx:example.org").to_owned()));
1445 assert_matches!(
1446 event.encryption_info().unwrap().algorithm_info,
1447 AlgorithmInfo::MegolmV1AesSha2 { .. }
1448 );
1449
1450 let serialized = json!({
1452 "event": {
1453 "content": {"body": "secret", "msgtype": "m.text"},
1454 "event_id": "$xxxxx:example.org",
1455 "origin_server_ts": 2189,
1456 "room_id": "!someroom:example.com",
1457 "sender": "@carl:example.com",
1458 "type": "m.room.message",
1459 },
1460 "encryption_info": {
1461 "sender": "@sender:example.com",
1462 "sender_device": null,
1463 "algorithm_info": {
1464 "MegolmV1AesSha2": {
1465 "curve25519_key": "xxx",
1466 "sender_claimed_keys": {}
1467 }
1468 },
1469 "verification_state": "Verified",
1470 },
1471 });
1472 let event: TimelineEvent = serde_json::from_value(serialized).unwrap();
1473 assert_eq!(event.event_id(), Some(event_id!("$xxxxx:example.org").to_owned()));
1474 assert_matches!(
1475 event.encryption_info().unwrap().algorithm_info,
1476 AlgorithmInfo::MegolmV1AesSha2 { session_id: None, .. }
1477 );
1478
1479 let serialized = json!({
1482 "event": {
1483 "content": {"body": "secret", "msgtype": "m.text"},
1484 "event_id": "$xxxxx:example.org",
1485 "origin_server_ts": 2189,
1486 "room_id": "!someroom:example.com",
1487 "sender": "@carl:example.com",
1488 "type": "m.room.message",
1489 },
1490 "encryption_info": {
1491 "sender": "@sender:example.com",
1492 "sender_device": null,
1493 "algorithm_info": {
1494 "MegolmV1AesSha2": {
1495 "curve25519_key": "xxx",
1496 "sender_claimed_keys": {}
1497 }
1498 },
1499 "verification_state": "Verified",
1500 },
1501 "unsigned_encryption_info": {
1502 "RelationsReplace": {"UnableToDecrypt": {"session_id": "xyz"}}
1503 }
1504 });
1505 let event: TimelineEvent = serde_json::from_value(serialized).unwrap();
1506 assert_eq!(event.event_id(), Some(event_id!("$xxxxx:example.org").to_owned()));
1507 assert_matches!(
1508 event.encryption_info().unwrap().algorithm_info,
1509 AlgorithmInfo::MegolmV1AesSha2 { .. }
1510 );
1511 assert_matches!(event.kind, TimelineEventKind::Decrypted(decrypted) => {
1512 assert_matches!(decrypted.unsigned_encryption_info, Some(map) => {
1513 assert_eq!(map.len(), 1);
1514 let (location, result) = map.into_iter().next().unwrap();
1515 assert_eq!(location, UnsignedEventLocation::RelationsReplace);
1516 assert_matches!(result, UnsignedDecryptionResult::UnableToDecrypt(utd_info) => {
1517 assert_eq!(utd_info.session_id, Some("xyz".to_owned()));
1518 assert_eq!(utd_info.reason, UnableToDecryptReason::Unknown);
1519 })
1520 });
1521 });
1522 }
1523
1524 #[test]
1525 fn test_creating_or_deserializing_an_event_extracts_summary() {
1526 let event = json!({
1527 "event_id": "$eid:example.com",
1528 "type": "m.room.message",
1529 "sender": "@alice:example.com",
1530 "origin_server_ts": 42,
1531 "content": {
1532 "body": "Hello, world!",
1533 },
1534 "unsigned": {
1535 "m.relations": {
1536 "m.thread": {
1537 "latest_event": {
1538 "event_id": "$latest_event:example.com",
1539 "type": "m.room.message",
1540 "sender": "@bob:example.com",
1541 "origin_server_ts": 42,
1542 "content": {
1543 "body": "Hello to you too!",
1544 "msgtype": "m.text",
1545 }
1546 },
1547 "count": 2,
1548 "current_user_participated": true,
1549 }
1550 }
1551 }
1552 });
1553
1554 let raw = Raw::new(&event).unwrap().cast();
1555
1556 let timeline_event = TimelineEvent::from_plaintext(raw);
1559 assert_matches!(timeline_event.thread_summary, ThreadSummaryStatus::Some(ThreadSummary { num_replies, latest_reply }) => {
1560 assert_eq!(num_replies, 2);
1561 assert_eq!(latest_reply.as_deref(), Some(event_id!("$latest_event:example.com")));
1562 });
1563
1564 assert!(timeline_event.bundled_latest_thread_event.is_some());
1565
1566 let serialized_timeline_item = json!({
1569 "kind": {
1570 "PlainText": {
1571 "event": event
1572 }
1573 }
1574 });
1575
1576 let timeline_event: TimelineEvent =
1577 serde_json::from_value(serialized_timeline_item).unwrap();
1578 assert_matches!(timeline_event.thread_summary, ThreadSummaryStatus::Unknown);
1579
1580 assert!(timeline_event.bundled_latest_thread_event.is_none());
1583 }
1584
1585 #[test]
1586 fn sync_timeline_event_deserialisation_migration_for_withheld() {
1587 let serialized = json!({
1604 "kind": {
1605 "UnableToDecrypt": {
1606 "event": {
1607 "content": {
1608 "algorithm": "m.megolm.v1.aes-sha2",
1609 "ciphertext": "AwgAEoABzL1JYhqhjW9jXrlT3M6H8mJ4qffYtOQOnPuAPNxsuG20oiD/Fnpv6jnQGhU6YbV9pNM+1mRnTvxW3CbWOPjLKqCWTJTc7Q0vDEVtYePg38ncXNcwMmfhgnNAoW9S7vNs8C003x3yUl6NeZ8bH+ci870BZL+kWM/lMl10tn6U7snNmSjnE3ckvRdO+11/R4//5VzFQpZdf4j036lNSls/WIiI67Fk9iFpinz9xdRVWJFVdrAiPFwb8L5xRZ8aX+e2JDMlc1eW8gk",
1610 "device_id": "SKCGPNUWAU",
1611 "sender_key": "Gim/c7uQdSXyrrUbmUOrBT6sMC0gO7QSLmOK6B7NOm0",
1612 "session_id": "hgLyeSqXfb8vc5AjQLsg6TSHVu0HJ7HZ4B6jgMvxkrs"
1613 },
1614 "event_id": "$xxxxx:example.org",
1615 "origin_server_ts": 2189,
1616 "room_id": "!someroom:example.com",
1617 "sender": "@carl:example.com",
1618 "type": "m.room.message"
1619 },
1620 "utd_info": {
1621 "reason": "MissingMegolmSession",
1622 "session_id": "session000"
1623 }
1624 }
1625 }
1626 });
1627
1628 let result = serde_json::from_value(serialized);
1629 assert!(result.is_ok());
1630
1631 let event: TimelineEvent = result.unwrap();
1633 assert_matches!(
1634 event.kind,
1635 TimelineEventKind::UnableToDecrypt { utd_info, .. }=> {
1636 assert_matches!(
1637 utd_info.reason,
1638 UnableToDecryptReason::MissingMegolmSession { withheld_code: None }
1639 );
1640 }
1641 )
1642 }
1643
1644 #[test]
1645 fn unable_to_decrypt_info_migration_for_withheld() {
1646 let old_format = json!({
1647 "reason": "MissingMegolmSession",
1648 "session_id": "session000"
1649 });
1650
1651 let deserialized = serde_json::from_value::<UnableToDecryptInfo>(old_format).unwrap();
1652 let session_id = Some("session000".to_owned());
1653
1654 assert_eq!(deserialized.session_id, session_id);
1655 assert_eq!(
1656 deserialized.reason,
1657 UnableToDecryptReason::MissingMegolmSession { withheld_code: None },
1658 );
1659
1660 let new_format = json!({
1661 "session_id": "session000",
1662 "reason": {
1663 "MissingMegolmSession": {
1664 "withheld_code": null
1665 }
1666 }
1667 });
1668
1669 let deserialized = serde_json::from_value::<UnableToDecryptInfo>(new_format).unwrap();
1670
1671 assert_eq!(
1672 deserialized.reason,
1673 UnableToDecryptReason::MissingMegolmSession { withheld_code: None },
1674 );
1675 assert_eq!(deserialized.session_id, session_id);
1676 }
1677
1678 #[test]
1679 fn unable_to_decrypt_reason_is_missing_room_key() {
1680 let reason = UnableToDecryptReason::MissingMegolmSession { withheld_code: None };
1681 assert!(reason.is_missing_room_key());
1682
1683 let reason = UnableToDecryptReason::MissingMegolmSession {
1684 withheld_code: Some(WithheldCode::Blacklisted),
1685 };
1686 assert!(!reason.is_missing_room_key());
1687
1688 let reason = UnableToDecryptReason::UnknownMegolmMessageIndex;
1689 assert!(reason.is_missing_room_key());
1690 }
1691
1692 #[test]
1693 fn snapshot_test_verification_level() {
1694 with_settings!({ prepend_module_to_snapshot => false }, {
1695 assert_json_snapshot!(VerificationLevel::VerificationViolation);
1696 assert_json_snapshot!(VerificationLevel::UnsignedDevice);
1697 assert_json_snapshot!(VerificationLevel::None(DeviceLinkProblem::InsecureSource));
1698 assert_json_snapshot!(VerificationLevel::None(DeviceLinkProblem::MissingDevice));
1699 assert_json_snapshot!(VerificationLevel::UnverifiedIdentity);
1700 });
1701 }
1702
1703 #[test]
1704 fn snapshot_test_verification_states() {
1705 with_settings!({ prepend_module_to_snapshot => false }, {
1706 assert_json_snapshot!(VerificationState::Unverified(VerificationLevel::UnsignedDevice));
1707 assert_json_snapshot!(VerificationState::Unverified(
1708 VerificationLevel::VerificationViolation
1709 ));
1710 assert_json_snapshot!(VerificationState::Unverified(VerificationLevel::None(
1711 DeviceLinkProblem::InsecureSource,
1712 )));
1713 assert_json_snapshot!(VerificationState::Unverified(VerificationLevel::None(
1714 DeviceLinkProblem::MissingDevice,
1715 )));
1716 assert_json_snapshot!(VerificationState::Verified);
1717 });
1718 }
1719
1720 #[test]
1721 fn snapshot_test_shield_states() {
1722 with_settings!({ prepend_module_to_snapshot => false }, {
1723 assert_json_snapshot!(ShieldState::None);
1724 assert_json_snapshot!(ShieldState::Red {
1725 code: ShieldStateCode::UnverifiedIdentity,
1726 message: "a message"
1727 });
1728 assert_json_snapshot!(ShieldState::Grey {
1729 code: ShieldStateCode::AuthenticityNotGuaranteed,
1730 message: "authenticity of this message cannot be guaranteed",
1731 });
1732 });
1733 }
1734
1735 #[test]
1736 fn snapshot_test_shield_codes() {
1737 with_settings!({ prepend_module_to_snapshot => false }, {
1738 assert_json_snapshot!(ShieldStateCode::AuthenticityNotGuaranteed);
1739 assert_json_snapshot!(ShieldStateCode::UnknownDevice);
1740 assert_json_snapshot!(ShieldStateCode::UnsignedDevice);
1741 assert_json_snapshot!(ShieldStateCode::UnverifiedIdentity);
1742 assert_json_snapshot!(ShieldStateCode::SentInClear);
1743 assert_json_snapshot!(ShieldStateCode::VerificationViolation);
1744 });
1745 }
1746
1747 #[test]
1748 fn snapshot_test_algorithm_info() {
1749 let mut map = BTreeMap::new();
1750 map.insert(DeviceKeyAlgorithm::Curve25519, "claimedclaimedcurve25519".to_owned());
1751 map.insert(DeviceKeyAlgorithm::Ed25519, "claimedclaimeded25519".to_owned());
1752 let info = AlgorithmInfo::MegolmV1AesSha2 {
1753 curve25519_key: "curvecurvecurve".into(),
1754 sender_claimed_keys: BTreeMap::from([
1755 (DeviceKeyAlgorithm::Curve25519, "claimedclaimedcurve25519".to_owned()),
1756 (DeviceKeyAlgorithm::Ed25519, "claimedclaimeded25519".to_owned()),
1757 ]),
1758 session_id: None,
1759 };
1760
1761 with_settings!({ prepend_module_to_snapshot => false }, {
1762 assert_json_snapshot!(info)
1763 });
1764 }
1765
1766 #[test]
1767 fn test_encryption_info_migration() {
1768 let old_format = json!({
1771 "sender": "@alice:localhost",
1772 "sender_device": "ABCDEFGH",
1773 "algorithm_info": {
1774 "MegolmV1AesSha2": {
1775 "curve25519_key": "curvecurvecurve",
1776 "sender_claimed_keys": {}
1777 }
1778 },
1779 "verification_state": "Verified",
1780 "session_id": "mysessionid76"
1781 });
1782
1783 let deserialized = serde_json::from_value::<EncryptionInfo>(old_format).unwrap();
1784 let expected_session_id = Some("mysessionid76".to_owned());
1785
1786 assert_let!(
1787 AlgorithmInfo::MegolmV1AesSha2 { session_id, .. } = deserialized.algorithm_info.clone()
1788 );
1789 assert_eq!(session_id, expected_session_id);
1790
1791 assert_json_snapshot!(deserialized);
1792 }
1793
1794 #[test]
1795 fn snapshot_test_encryption_info() {
1796 let info = EncryptionInfo {
1797 sender: user_id!("@alice:localhost").to_owned(),
1798 sender_device: Some(device_id!("ABCDEFGH").to_owned()),
1799 algorithm_info: AlgorithmInfo::MegolmV1AesSha2 {
1800 curve25519_key: "curvecurvecurve".into(),
1801 sender_claimed_keys: Default::default(),
1802 session_id: Some("mysessionid76".to_owned()),
1803 },
1804 verification_state: VerificationState::Verified,
1805 };
1806
1807 with_settings!({ sort_maps => true, prepend_module_to_snapshot => false }, {
1808 assert_json_snapshot!(info)
1809 })
1810 }
1811
1812 #[test]
1813 fn snapshot_test_sync_timeline_event() {
1814 let room_event = TimelineEvent {
1815 kind: TimelineEventKind::Decrypted(DecryptedRoomEvent {
1816 event: Raw::new(&example_event()).unwrap().cast(),
1817 encryption_info: Arc::new(EncryptionInfo {
1818 sender: user_id!("@sender:example.com").to_owned(),
1819 sender_device: Some(device_id!("ABCDEFGHIJ").to_owned()),
1820 algorithm_info: AlgorithmInfo::MegolmV1AesSha2 {
1821 curve25519_key: "xxx".to_owned(),
1822 sender_claimed_keys: BTreeMap::from([
1823 (
1824 DeviceKeyAlgorithm::Ed25519,
1825 "I3YsPwqMZQXHkSQbjFNEs7b529uac2xBpI83eN3LUXo".to_owned(),
1826 ),
1827 (
1828 DeviceKeyAlgorithm::Curve25519,
1829 "qzdW3F5IMPFl0HQgz5w/L5Oi/npKUFn8Um84acIHfPY".to_owned(),
1830 ),
1831 ]),
1832 session_id: Some("mysessionid112".to_owned()),
1833 },
1834 verification_state: VerificationState::Verified,
1835 }),
1836 unsigned_encryption_info: Some(BTreeMap::from([(
1837 UnsignedEventLocation::RelationsThreadLatestEvent,
1838 UnsignedDecryptionResult::UnableToDecrypt(UnableToDecryptInfo {
1839 session_id: Some("xyz".to_owned()),
1840 reason: UnableToDecryptReason::MissingMegolmSession {
1841 withheld_code: Some(WithheldCode::Unverified),
1842 },
1843 }),
1844 )])),
1845 }),
1846 push_actions: Default::default(),
1847 thread_summary: ThreadSummaryStatus::Some(ThreadSummary {
1848 num_replies: 2,
1849 latest_reply: None,
1850 }),
1851 bundled_latest_thread_event: None,
1852 };
1853
1854 with_settings!({ sort_maps => true, prepend_module_to_snapshot => false }, {
1855 assert_json_snapshot! {
1858 serde_json::to_value(&room_event).unwrap(),
1859 }
1860 });
1861 }
1862}