1use std::{collections::BTreeMap, fmt, sync::Arc};
16
17use ruma::{
18 DeviceKeyAlgorithm, OwnedDeviceId, OwnedEventId, OwnedUserId,
19 events::{
20 AnySyncMessageLikeEvent, AnySyncTimelineEvent, AnyTimelineEvent, AnyToDeviceEvent,
21 MessageLikeEventType,
22 },
23 push::Action,
24 serde::{
25 AsRefStr, AsStrAsRefStr, DebugAsRefStr, DeserializeFromCowStr, FromString, JsonObject, Raw,
26 SerializeAsRefStr,
27 },
28};
29use serde::{Deserialize, Serialize};
30use tracing::warn;
31#[cfg(target_family = "wasm")]
32use wasm_bindgen::prelude::*;
33
34use crate::{
35 debug::{DebugRawEvent, DebugStructExt},
36 serde_helpers::extract_bundled_thread_summary,
37};
38
39const AUTHENTICITY_NOT_GUARANTEED: &str =
40 "The authenticity of this encrypted message can't be guaranteed on this device.";
41const UNVERIFIED_IDENTITY: &str = "Encrypted by an unverified user.";
42const VERIFICATION_VIOLATION: &str =
43 "Encrypted by a previously-verified user who is no longer verified.";
44const UNSIGNED_DEVICE: &str = "Encrypted by a device not verified by its owner.";
45const UNKNOWN_DEVICE: &str = "Encrypted by an unknown or deleted device.";
46const MISMATCHED_SENDER: &str = "\
47 The sender of the event does not match the owner of the device \
48 that created the Megolm session.";
49pub const SENT_IN_CLEAR: &str = "Not encrypted.";
50
51#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
54#[serde(from = "OldVerificationStateHelper")]
55pub enum VerificationState {
56 Verified,
61
62 Unverified(VerificationLevel),
67}
68
69#[derive(Clone, Debug, Deserialize)]
72enum OldVerificationStateHelper {
73 Untrusted,
74 UnknownDevice,
75 #[serde(alias = "Trusted")]
76 Verified,
77 Unverified(VerificationLevel),
78}
79
80impl From<OldVerificationStateHelper> for VerificationState {
81 fn from(value: OldVerificationStateHelper) -> Self {
82 match value {
83 OldVerificationStateHelper::Untrusted => {
86 VerificationState::Unverified(VerificationLevel::UnsignedDevice)
87 }
88 OldVerificationStateHelper::UnknownDevice => {
89 Self::Unverified(VerificationLevel::None(DeviceLinkProblem::MissingDevice))
90 }
91 OldVerificationStateHelper::Verified => Self::Verified,
92 OldVerificationStateHelper::Unverified(l) => Self::Unverified(l),
93 }
94 }
95}
96
97impl VerificationState {
98 pub fn to_shield_state_strict(&self) -> ShieldState {
105 match self {
106 VerificationState::Verified => ShieldState::None,
107 VerificationState::Unverified(level) => match level {
108 VerificationLevel::UnverifiedIdentity
109 | VerificationLevel::VerificationViolation
110 | VerificationLevel::UnsignedDevice => ShieldState::Red {
111 code: ShieldStateCode::UnverifiedIdentity,
112 message: UNVERIFIED_IDENTITY,
113 },
114 VerificationLevel::None(link) => match link {
115 DeviceLinkProblem::MissingDevice => ShieldState::Red {
116 code: ShieldStateCode::UnknownDevice,
117 message: UNKNOWN_DEVICE,
118 },
119 DeviceLinkProblem::InsecureSource => ShieldState::Red {
120 code: ShieldStateCode::AuthenticityNotGuaranteed,
121 message: AUTHENTICITY_NOT_GUARANTEED,
122 },
123 },
124 VerificationLevel::MismatchedSender => ShieldState::Red {
125 code: ShieldStateCode::MismatchedSender,
126 message: MISMATCHED_SENDER,
127 },
128 },
129 }
130 }
131
132 pub fn to_shield_state_lax(&self) -> ShieldState {
140 match self {
141 VerificationState::Verified => ShieldState::None,
142 VerificationState::Unverified(level) => match level {
143 VerificationLevel::UnverifiedIdentity => {
144 ShieldState::None
147 }
148 VerificationLevel::VerificationViolation => {
149 ShieldState::Red {
152 code: ShieldStateCode::VerificationViolation,
153 message: VERIFICATION_VIOLATION,
154 }
155 }
156 VerificationLevel::UnsignedDevice => {
157 ShieldState::Red {
159 code: ShieldStateCode::UnsignedDevice,
160 message: UNSIGNED_DEVICE,
161 }
162 }
163 VerificationLevel::None(link) => match link {
164 DeviceLinkProblem::MissingDevice => {
165 ShieldState::Red {
169 code: ShieldStateCode::UnknownDevice,
170 message: UNKNOWN_DEVICE,
171 }
172 }
173 DeviceLinkProblem::InsecureSource => {
174 ShieldState::Grey {
177 code: ShieldStateCode::AuthenticityNotGuaranteed,
178 message: AUTHENTICITY_NOT_GUARANTEED,
179 }
180 }
181 },
182 VerificationLevel::MismatchedSender => ShieldState::Red {
183 code: ShieldStateCode::MismatchedSender,
184 message: MISMATCHED_SENDER,
185 },
186 },
187 }
188 }
189}
190
191#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
194pub enum VerificationLevel {
195 UnverifiedIdentity,
197
198 #[serde(alias = "PreviouslyVerified")]
201 VerificationViolation,
202
203 UnsignedDevice,
206
207 None(DeviceLinkProblem),
213
214 MismatchedSender,
217}
218
219impl fmt::Display for VerificationLevel {
220 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
221 let display = match self {
222 VerificationLevel::UnverifiedIdentity => "The sender's identity was not verified",
223 VerificationLevel::VerificationViolation => {
224 "The sender's identity was previously verified but has changed"
225 }
226 VerificationLevel::UnsignedDevice => {
227 "The sending device was not signed by the user's identity"
228 }
229 VerificationLevel::None(..) => "The sending device is not known",
230 VerificationLevel::MismatchedSender => MISMATCHED_SENDER,
231 };
232 write!(f, "{display}")
233 }
234}
235
236#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
239pub enum DeviceLinkProblem {
240 MissingDevice,
244 InsecureSource,
247}
248
249#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq)]
252pub enum ShieldState {
253 Red {
256 code: ShieldStateCode,
258 message: &'static str,
260 },
261 Grey {
264 code: ShieldStateCode,
266 message: &'static str,
268 },
269 None,
271}
272
273#[derive(Clone, Copy, Debug, Deserialize, Serialize, Eq, PartialEq)]
275#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
276#[cfg_attr(target_family = "wasm", wasm_bindgen)]
277pub enum ShieldStateCode {
278 AuthenticityNotGuaranteed,
280 UnknownDevice,
282 UnsignedDevice,
284 UnverifiedIdentity,
286 SentInClear,
288 #[serde(alias = "PreviouslyVerified")]
290 VerificationViolation,
291 MismatchedSender,
294}
295
296#[derive(Clone, Debug, Deserialize, Serialize)]
298pub enum AlgorithmInfo {
299 MegolmV1AesSha2 {
301 curve25519_key: String,
304 sender_claimed_keys: BTreeMap<DeviceKeyAlgorithm, String>,
308
309 #[serde(default, skip_serializing_if = "Option::is_none")]
312 session_id: Option<String>,
313 },
314
315 OlmV1Curve25519AesSha2 {
317 curve25519_public_key_base64: String,
319 },
320}
321
322#[derive(Clone, Debug, Serialize)]
324pub struct EncryptionInfo {
325 pub sender: OwnedUserId,
328 pub sender_device: Option<OwnedDeviceId>,
331 pub algorithm_info: AlgorithmInfo,
333 pub verification_state: VerificationState,
340}
341
342impl EncryptionInfo {
343 pub fn session_id(&self) -> Option<&str> {
345 if let AlgorithmInfo::MegolmV1AesSha2 { session_id, .. } = &self.algorithm_info {
346 session_id.as_deref()
347 } else {
348 None
349 }
350 }
351}
352
353impl<'de> Deserialize<'de> for EncryptionInfo {
354 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
355 where
356 D: serde::Deserializer<'de>,
357 {
358 #[derive(Deserialize)]
361 struct Helper {
362 pub sender: OwnedUserId,
363 pub sender_device: Option<OwnedDeviceId>,
364 pub algorithm_info: AlgorithmInfo,
365 pub verification_state: VerificationState,
366 #[serde(rename = "session_id")]
367 pub old_session_id: Option<String>,
368 }
369
370 let Helper { sender, sender_device, algorithm_info, verification_state, old_session_id } =
371 Helper::deserialize(deserializer)?;
372
373 let algorithm_info = match algorithm_info {
374 AlgorithmInfo::MegolmV1AesSha2 { curve25519_key, sender_claimed_keys, session_id } => {
375 AlgorithmInfo::MegolmV1AesSha2 {
376 session_id: session_id.or(old_session_id),
378 curve25519_key,
379 sender_claimed_keys,
380 }
381 }
382 other => other,
383 };
384
385 Ok(EncryptionInfo { sender, sender_device, algorithm_info, verification_state })
386 }
387}
388
389#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
400pub struct ThreadSummary {
401 #[serde(skip_serializing_if = "Option::is_none")]
403 pub latest_reply: Option<OwnedEventId>,
404
405 pub num_replies: u32,
411}
412
413#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)]
415pub enum ThreadSummaryStatus {
416 #[default]
418 Unknown,
419 None,
421 Some(ThreadSummary),
423}
424
425impl ThreadSummaryStatus {
426 fn is_unknown(&self) -> bool {
428 matches!(self, ThreadSummaryStatus::Unknown)
429 }
430
431 pub fn summary(&self) -> Option<&ThreadSummary> {
434 match self {
435 ThreadSummaryStatus::Unknown | ThreadSummaryStatus::None => None,
436 ThreadSummaryStatus::Some(thread_summary) => Some(thread_summary),
437 }
438 }
439}
440
441#[derive(Clone, Debug, Serialize)]
464pub struct TimelineEvent {
465 pub kind: TimelineEventKind,
467
468 #[serde(skip_serializing_if = "skip_serialize_push_actions")]
473 push_actions: Option<Vec<Action>>,
474
475 #[serde(default, skip_serializing_if = "ThreadSummaryStatus::is_unknown")]
477 pub thread_summary: ThreadSummaryStatus,
478
479 #[serde(skip)]
484 pub bundled_latest_thread_event: Option<Box<TimelineEvent>>,
485}
486
487fn skip_serialize_push_actions(push_actions: &Option<Vec<Action>>) -> bool {
489 push_actions.as_ref().is_none_or(|v| v.is_empty())
490}
491
492#[cfg(not(feature = "test-send-sync"))]
494unsafe impl Send for TimelineEvent {}
495
496#[cfg(not(feature = "test-send-sync"))]
498unsafe impl Sync for TimelineEvent {}
499
500#[cfg(feature = "test-send-sync")]
501#[test]
502fn test_send_sync_for_sync_timeline_event() {
504 fn assert_send_sync<T: crate::SendOutsideWasm + crate::SyncOutsideWasm>() {}
505
506 assert_send_sync::<TimelineEvent>();
507}
508
509impl TimelineEvent {
510 pub fn from_plaintext(event: Raw<AnySyncTimelineEvent>) -> Self {
515 Self::new(TimelineEventKind::PlainText { event }, None)
516 }
517
518 pub fn from_decrypted(
520 decrypted: DecryptedRoomEvent,
521 push_actions: Option<Vec<Action>>,
522 ) -> Self {
523 Self::new(TimelineEventKind::Decrypted(decrypted), push_actions)
524 }
525
526 pub fn from_utd(event: Raw<AnySyncTimelineEvent>, utd_info: UnableToDecryptInfo) -> Self {
529 Self::new(TimelineEventKind::UnableToDecrypt { event, utd_info }, None)
530 }
531
532 fn new(kind: TimelineEventKind, push_actions: Option<Vec<Action>>) -> Self {
535 let (thread_summary, latest_thread_event) = extract_bundled_thread_summary(kind.raw());
536 let bundled_latest_thread_event =
537 Self::from_bundled_latest_event(&kind, latest_thread_event);
538 Self { kind, push_actions, thread_summary, bundled_latest_thread_event }
539 }
540
541 fn from_bundled_latest_event(
545 this: &TimelineEventKind,
546 latest_event: Option<Raw<AnySyncMessageLikeEvent>>,
547 ) -> Option<Box<Self>> {
548 let latest_event = latest_event?;
549
550 match this {
551 TimelineEventKind::Decrypted(decrypted) => {
552 if let Some(unsigned_decryption_result) =
553 decrypted.unsigned_encryption_info.as_ref().and_then(|unsigned_map| {
554 unsigned_map.get(&UnsignedEventLocation::RelationsThreadLatestEvent)
555 })
556 {
557 match unsigned_decryption_result {
558 UnsignedDecryptionResult::Decrypted(encryption_info) => {
559 return Some(Box::new(TimelineEvent::from_decrypted(
562 DecryptedRoomEvent {
563 event: latest_event.cast_unchecked(),
566 encryption_info: encryption_info.clone(),
567 unsigned_encryption_info: None,
571 },
572 None,
573 )));
574 }
575
576 UnsignedDecryptionResult::UnableToDecrypt(utd_info) => {
577 return Some(Box::new(TimelineEvent::from_utd(
579 latest_event.cast(),
580 utd_info.clone(),
581 )));
582 }
583 }
584 }
585 }
586
587 TimelineEventKind::UnableToDecrypt { .. } | TimelineEventKind::PlainText { .. } => {
588 }
590 }
591
592 match latest_event.get_field::<MessageLikeEventType>("type") {
593 Ok(None) => {
594 let event_id = latest_event.get_field::<OwnedEventId>("event_id").ok().flatten();
595 warn!(
596 ?event_id,
597 "couldn't deserialize bundled latest thread event: missing `type` field \
598 in bundled latest thread event"
599 );
600 None
601 }
602
603 Ok(Some(MessageLikeEventType::RoomEncrypted)) => {
604 Some(Box::new(TimelineEvent::from_utd(
608 latest_event.cast(),
609 UnableToDecryptInfo {
610 session_id: None,
611 reason: UnableToDecryptReason::Unknown,
612 },
613 )))
614 }
615
616 Ok(_) => Some(Box::new(TimelineEvent::from_plaintext(latest_event.cast()))),
617
618 Err(err) => {
619 let event_id = latest_event.get_field::<OwnedEventId>("event_id").ok().flatten();
620 warn!(?event_id, "couldn't deserialize bundled latest thread event's type: {err}");
621 None
622 }
623 }
624 }
625
626 pub fn push_actions(&self) -> Option<&[Action]> {
631 self.push_actions.as_deref()
632 }
633
634 pub fn set_push_actions(&mut self, push_actions: Vec<Action>) {
636 self.push_actions = Some(push_actions);
637 }
638
639 pub fn event_id(&self) -> Option<OwnedEventId> {
642 self.kind.event_id()
643 }
644
645 pub fn raw(&self) -> &Raw<AnySyncTimelineEvent> {
648 self.kind.raw()
649 }
650
651 pub fn replace_raw(&mut self, replacement: Raw<AnyTimelineEvent>) {
653 match &mut self.kind {
654 TimelineEventKind::Decrypted(decrypted) => decrypted.event = replacement,
655 TimelineEventKind::UnableToDecrypt { event, .. }
656 | TimelineEventKind::PlainText { event } => {
657 *event = replacement.cast();
660 }
661 }
662 }
663
664 pub fn encryption_info(&self) -> Option<&Arc<EncryptionInfo>> {
667 self.kind.encryption_info()
668 }
669
670 pub fn into_raw(self) -> Raw<AnySyncTimelineEvent> {
673 self.kind.into_raw()
674 }
675}
676
677impl<'de> Deserialize<'de> for TimelineEvent {
678 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
686 where
687 D: serde::Deserializer<'de>,
688 {
689 use serde_json::{Map, Value};
690
691 let value = Map::<String, Value>::deserialize(deserializer)?;
693
694 if value.contains_key("event") {
696 let v0: SyncTimelineEventDeserializationHelperV0 =
697 serde_json::from_value(Value::Object(value)).map_err(|e| {
698 serde::de::Error::custom(format!(
699 "Unable to deserialize V0-format TimelineEvent: {e}",
700 ))
701 })?;
702 Ok(v0.into())
703 }
704 else {
706 let v1: SyncTimelineEventDeserializationHelperV1 =
707 serde_json::from_value(Value::Object(value)).map_err(|e| {
708 serde::de::Error::custom(format!(
709 "Unable to deserialize V1-format TimelineEvent: {e}",
710 ))
711 })?;
712 Ok(v1.into())
713 }
714 }
715}
716
717#[derive(Clone, Serialize, Deserialize)]
719pub enum TimelineEventKind {
720 Decrypted(DecryptedRoomEvent),
722
723 UnableToDecrypt {
725 event: Raw<AnySyncTimelineEvent>,
729
730 utd_info: UnableToDecryptInfo,
732 },
733
734 PlainText {
736 event: Raw<AnySyncTimelineEvent>,
740 },
741}
742
743impl TimelineEventKind {
744 pub fn raw(&self) -> &Raw<AnySyncTimelineEvent> {
747 match self {
748 TimelineEventKind::Decrypted(d) => d.event.cast_ref(),
754 TimelineEventKind::UnableToDecrypt { event, .. } => event,
755 TimelineEventKind::PlainText { event } => event,
756 }
757 }
758
759 pub fn event_id(&self) -> Option<OwnedEventId> {
762 self.raw().get_field::<OwnedEventId>("event_id").ok().flatten()
763 }
764
765 pub fn is_utd(&self) -> bool {
767 matches!(self, TimelineEventKind::UnableToDecrypt { .. })
768 }
769
770 pub fn encryption_info(&self) -> Option<&Arc<EncryptionInfo>> {
773 match self {
774 TimelineEventKind::Decrypted(d) => Some(&d.encryption_info),
775 TimelineEventKind::UnableToDecrypt { .. } | TimelineEventKind::PlainText { .. } => None,
776 }
777 }
778
779 pub fn unsigned_encryption_map(
782 &self,
783 ) -> Option<&BTreeMap<UnsignedEventLocation, UnsignedDecryptionResult>> {
784 match self {
785 TimelineEventKind::Decrypted(d) => d.unsigned_encryption_info.as_ref(),
786 TimelineEventKind::UnableToDecrypt { .. } | TimelineEventKind::PlainText { .. } => None,
787 }
788 }
789
790 pub fn into_raw(self) -> Raw<AnySyncTimelineEvent> {
793 match self {
794 TimelineEventKind::Decrypted(d) => d.event.cast(),
800 TimelineEventKind::UnableToDecrypt { event, .. } => event,
801 TimelineEventKind::PlainText { event } => event,
802 }
803 }
804
805 pub fn session_id(&self) -> Option<&str> {
808 match self {
809 TimelineEventKind::Decrypted(decrypted_room_event) => {
810 decrypted_room_event.encryption_info.session_id()
811 }
812 TimelineEventKind::UnableToDecrypt { utd_info, .. } => utd_info.session_id.as_deref(),
813 TimelineEventKind::PlainText { .. } => None,
814 }
815 }
816}
817
818#[cfg(not(tarpaulin_include))]
819impl fmt::Debug for TimelineEventKind {
820 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
821 match &self {
822 Self::PlainText { event } => f
823 .debug_struct("TimelineEventDecryptionResult::PlainText")
824 .field("event", &DebugRawEvent(event))
825 .finish(),
826
827 Self::UnableToDecrypt { event, utd_info } => f
828 .debug_struct("TimelineEventDecryptionResult::UnableToDecrypt")
829 .field("event", &DebugRawEvent(event))
830 .field("utd_info", &utd_info)
831 .finish(),
832
833 Self::Decrypted(decrypted) => {
834 f.debug_tuple("TimelineEventDecryptionResult::Decrypted").field(decrypted).finish()
835 }
836 }
837 }
838}
839
840#[derive(Clone, Serialize, Deserialize)]
841pub struct DecryptedRoomEvent {
843 pub event: Raw<AnyTimelineEvent>,
851
852 pub encryption_info: Arc<EncryptionInfo>,
854
855 #[serde(skip_serializing_if = "Option::is_none")]
860 pub unsigned_encryption_info: Option<BTreeMap<UnsignedEventLocation, UnsignedDecryptionResult>>,
861}
862
863#[cfg(not(tarpaulin_include))]
864impl fmt::Debug for DecryptedRoomEvent {
865 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
866 let DecryptedRoomEvent { event, encryption_info, unsigned_encryption_info } = self;
867
868 f.debug_struct("DecryptedRoomEvent")
869 .field("event", &DebugRawEvent(event))
870 .field("encryption_info", encryption_info)
871 .maybe_field("unsigned_encryption_info", unsigned_encryption_info)
872 .finish()
873 }
874}
875
876#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
878pub enum UnsignedEventLocation {
879 RelationsReplace,
882 RelationsThreadLatestEvent,
885}
886
887impl UnsignedEventLocation {
888 pub fn find_mut<'a>(&self, unsigned: &'a mut JsonObject) -> Option<&'a mut serde_json::Value> {
895 let relations = unsigned.get_mut("m.relations")?.as_object_mut()?;
896
897 match self {
898 Self::RelationsReplace => relations.get_mut("m.replace"),
899 Self::RelationsThreadLatestEvent => {
900 relations.get_mut("m.thread")?.as_object_mut()?.get_mut("latest_event")
901 }
902 }
903 }
904}
905
906#[derive(Debug, Clone, Serialize, Deserialize)]
908pub enum UnsignedDecryptionResult {
909 Decrypted(Arc<EncryptionInfo>),
911 UnableToDecrypt(UnableToDecryptInfo),
913}
914
915impl UnsignedDecryptionResult {
916 pub fn encryption_info(&self) -> Option<&Arc<EncryptionInfo>> {
919 match self {
920 Self::Decrypted(info) => Some(info),
921 Self::UnableToDecrypt(_) => None,
922 }
923 }
924}
925
926#[derive(Debug, Clone, Serialize, Deserialize)]
928pub struct UnableToDecryptInfo {
929 #[serde(skip_serializing_if = "Option::is_none")]
932 pub session_id: Option<String>,
933
934 #[serde(default = "unknown_utd_reason", deserialize_with = "deserialize_utd_reason")]
936 pub reason: UnableToDecryptReason,
937}
938
939fn unknown_utd_reason() -> UnableToDecryptReason {
940 UnableToDecryptReason::Unknown
941}
942
943pub fn deserialize_utd_reason<'de, D>(d: D) -> Result<UnableToDecryptReason, D::Error>
946where
947 D: serde::Deserializer<'de>,
948{
949 let v: serde_json::Value = Deserialize::deserialize(d)?;
951 if v.as_str().is_some_and(|s| s == "MissingMegolmSession") {
954 return Ok(UnableToDecryptReason::MissingMegolmSession { withheld_code: None });
955 }
956 serde_json::from_value::<UnableToDecryptReason>(v).map_err(serde::de::Error::custom)
959}
960
961#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
963pub enum UnableToDecryptReason {
964 #[doc(hidden)]
967 Unknown,
968
969 MalformedEncryptedEvent,
973
974 MissingMegolmSession {
977 withheld_code: Option<WithheldCode>,
980 },
981
982 UnknownMegolmMessageIndex,
985
986 MegolmDecryptionFailure,
993
994 PayloadDeserializationFailure,
996
997 MismatchedIdentityKeys,
1001
1002 SenderIdentityNotTrusted(VerificationLevel),
1006
1007 #[cfg(feature = "experimental-encrypted-state-events")]
1010 StateKeyVerificationFailed,
1011}
1012
1013impl UnableToDecryptReason {
1014 pub fn is_missing_room_key(&self) -> bool {
1017 matches!(
1020 self,
1021 Self::MissingMegolmSession { withheld_code: None } | Self::UnknownMegolmMessageIndex
1022 )
1023 }
1024}
1025
1026#[derive(
1030 Clone,
1031 PartialEq,
1032 Eq,
1033 Hash,
1034 AsStrAsRefStr,
1035 AsRefStr,
1036 FromString,
1037 DebugAsRefStr,
1038 SerializeAsRefStr,
1039 DeserializeFromCowStr,
1040)]
1041pub enum WithheldCode {
1042 #[ruma_enum(rename = "m.blacklisted")]
1044 Blacklisted,
1045
1046 #[ruma_enum(rename = "m.unverified")]
1048 Unverified,
1049
1050 #[ruma_enum(rename = "m.unauthorised")]
1054 Unauthorised,
1055
1056 #[ruma_enum(rename = "m.unavailable")]
1059 Unavailable,
1060
1061 #[ruma_enum(rename = "m.no_olm")]
1065 NoOlm,
1066
1067 #[doc(hidden)]
1068 _Custom(PrivOwnedStr),
1069}
1070
1071impl fmt::Display for WithheldCode {
1072 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
1073 let string = match self {
1074 WithheldCode::Blacklisted => "The sender has blocked you.",
1075 WithheldCode::Unverified => "The sender has disabled encrypting to unverified devices.",
1076 WithheldCode::Unauthorised => "You are not authorised to read the message.",
1077 WithheldCode::Unavailable => "The requested key was not found.",
1078 WithheldCode::NoOlm => "Unable to establish a secure channel.",
1079 _ => self.as_str(),
1080 };
1081
1082 f.write_str(string)
1083 }
1084}
1085
1086#[doc(hidden)]
1090#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
1091pub struct PrivOwnedStr(pub Box<str>);
1092
1093#[cfg(not(tarpaulin_include))]
1094impl fmt::Debug for PrivOwnedStr {
1095 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1096 self.0.fmt(f)
1097 }
1098}
1099
1100#[derive(Debug, Deserialize)]
1105struct SyncTimelineEventDeserializationHelperV1 {
1106 kind: TimelineEventKind,
1108
1109 #[serde(default)]
1111 push_actions: Vec<Action>,
1112
1113 #[serde(default)]
1115 thread_summary: ThreadSummaryStatus,
1116}
1117
1118impl From<SyncTimelineEventDeserializationHelperV1> for TimelineEvent {
1119 fn from(value: SyncTimelineEventDeserializationHelperV1) -> Self {
1120 let SyncTimelineEventDeserializationHelperV1 { kind, push_actions, thread_summary } = value;
1121 TimelineEvent {
1122 kind,
1123 push_actions: Some(push_actions),
1124 thread_summary,
1125 bundled_latest_thread_event: None,
1127 }
1128 }
1129}
1130
1131#[derive(Deserialize)]
1133struct SyncTimelineEventDeserializationHelperV0 {
1134 event: Raw<AnySyncTimelineEvent>,
1136
1137 encryption_info: Option<Arc<EncryptionInfo>>,
1141
1142 #[serde(default)]
1144 push_actions: Vec<Action>,
1145
1146 unsigned_encryption_info: Option<BTreeMap<UnsignedEventLocation, UnsignedDecryptionResult>>,
1151}
1152
1153impl From<SyncTimelineEventDeserializationHelperV0> for TimelineEvent {
1154 fn from(value: SyncTimelineEventDeserializationHelperV0) -> Self {
1155 let SyncTimelineEventDeserializationHelperV0 {
1156 event,
1157 encryption_info,
1158 push_actions,
1159 unsigned_encryption_info,
1160 } = value;
1161
1162 let kind = match encryption_info {
1163 Some(encryption_info) => {
1164 TimelineEventKind::Decrypted(DecryptedRoomEvent {
1165 event: event.cast_unchecked(),
1172 encryption_info,
1173 unsigned_encryption_info,
1174 })
1175 }
1176
1177 None => TimelineEventKind::PlainText { event },
1178 };
1179
1180 TimelineEvent {
1181 kind,
1182 push_actions: Some(push_actions),
1183 thread_summary: ThreadSummaryStatus::Unknown,
1185 bundled_latest_thread_event: None,
1187 }
1188 }
1189}
1190
1191#[derive(Debug, Clone, PartialEq)]
1193pub enum ToDeviceUnableToDecryptReason {
1194 DecryptionFailure,
1197
1198 UnverifiedSenderDevice,
1202
1203 NoOlmMachine,
1206
1207 EncryptionIsDisabled,
1209}
1210
1211#[derive(Clone, Debug)]
1213pub struct ToDeviceUnableToDecryptInfo {
1214 pub reason: ToDeviceUnableToDecryptReason,
1216}
1217
1218#[derive(Clone, Debug)]
1220pub enum ProcessedToDeviceEvent {
1221 Decrypted {
1224 raw: Raw<AnyToDeviceEvent>,
1226 encryption_info: EncryptionInfo,
1228 },
1229
1230 UnableToDecrypt {
1232 encrypted_event: Raw<AnyToDeviceEvent>,
1233 utd_info: ToDeviceUnableToDecryptInfo,
1234 },
1235
1236 PlainText(Raw<AnyToDeviceEvent>),
1238
1239 Invalid(Raw<AnyToDeviceEvent>),
1243}
1244
1245impl ProcessedToDeviceEvent {
1246 pub fn to_raw(&self) -> Raw<AnyToDeviceEvent> {
1249 match self {
1250 ProcessedToDeviceEvent::Decrypted { raw, .. } => raw.clone(),
1251 ProcessedToDeviceEvent::UnableToDecrypt { encrypted_event, .. } => {
1252 encrypted_event.clone()
1253 }
1254 ProcessedToDeviceEvent::PlainText(event) => event.clone(),
1255 ProcessedToDeviceEvent::Invalid(event) => event.clone(),
1256 }
1257 }
1258
1259 pub fn as_raw(&self) -> &Raw<AnyToDeviceEvent> {
1261 match self {
1262 ProcessedToDeviceEvent::Decrypted { raw, .. } => raw,
1263 ProcessedToDeviceEvent::UnableToDecrypt { encrypted_event, .. } => encrypted_event,
1264 ProcessedToDeviceEvent::PlainText(event) => event,
1265 ProcessedToDeviceEvent::Invalid(event) => event,
1266 }
1267 }
1268}
1269
1270#[cfg(test)]
1271mod tests {
1272 use std::{collections::BTreeMap, sync::Arc};
1273
1274 use assert_matches::assert_matches;
1275 use assert_matches2::assert_let;
1276 use insta::{assert_json_snapshot, with_settings};
1277 use ruma::{
1278 DeviceKeyAlgorithm, device_id, event_id, events::room::message::RoomMessageEventContent,
1279 serde::Raw, user_id,
1280 };
1281 use serde::Deserialize;
1282 use serde_json::json;
1283
1284 use super::{
1285 AlgorithmInfo, DecryptedRoomEvent, DeviceLinkProblem, EncryptionInfo, ShieldState,
1286 ShieldStateCode, TimelineEvent, TimelineEventKind, UnableToDecryptInfo,
1287 UnableToDecryptReason, UnsignedDecryptionResult, UnsignedEventLocation, VerificationLevel,
1288 VerificationState, WithheldCode,
1289 };
1290 use crate::deserialized_responses::{ThreadSummary, ThreadSummaryStatus};
1291
1292 fn example_event() -> serde_json::Value {
1293 json!({
1294 "content": RoomMessageEventContent::text_plain("secret"),
1295 "type": "m.room.message",
1296 "event_id": "$xxxxx:example.org",
1297 "room_id": "!someroom:example.com",
1298 "origin_server_ts": 2189,
1299 "sender": "@carl:example.com",
1300 })
1301 }
1302
1303 #[test]
1304 fn sync_timeline_debug_content() {
1305 let room_event =
1306 TimelineEvent::from_plaintext(Raw::new(&example_event()).unwrap().cast_unchecked());
1307 let debug_s = format!("{room_event:?}");
1308 assert!(
1309 !debug_s.contains("secret"),
1310 "Debug representation contains event content!\n{debug_s}"
1311 );
1312 }
1313
1314 #[test]
1315 fn old_verification_state_to_new_migration() {
1316 #[derive(Deserialize)]
1317 struct State {
1318 state: VerificationState,
1319 }
1320
1321 let state = json!({
1322 "state": "Trusted",
1323 });
1324 let deserialized: State =
1325 serde_json::from_value(state).expect("We can deserialize the old trusted value");
1326 assert_eq!(deserialized.state, VerificationState::Verified);
1327
1328 let state = json!({
1329 "state": "UnknownDevice",
1330 });
1331
1332 let deserialized: State =
1333 serde_json::from_value(state).expect("We can deserialize the old unknown device value");
1334
1335 assert_eq!(
1336 deserialized.state,
1337 VerificationState::Unverified(VerificationLevel::None(
1338 DeviceLinkProblem::MissingDevice
1339 ))
1340 );
1341
1342 let state = json!({
1343 "state": "Untrusted",
1344 });
1345 let deserialized: State =
1346 serde_json::from_value(state).expect("We can deserialize the old trusted value");
1347
1348 assert_eq!(
1349 deserialized.state,
1350 VerificationState::Unverified(VerificationLevel::UnsignedDevice)
1351 );
1352 }
1353
1354 #[test]
1355 fn test_verification_level_deserializes() {
1356 #[derive(Deserialize)]
1358 struct Container {
1359 verification_level: VerificationLevel,
1360 }
1361 let container = json!({ "verification_level": "VerificationViolation" });
1362
1363 let deserialized: Container = serde_json::from_value(container)
1365 .expect("We can deserialize the old PreviouslyVerified value");
1366
1367 assert_eq!(deserialized.verification_level, VerificationLevel::VerificationViolation);
1369 }
1370
1371 #[test]
1372 fn test_verification_level_deserializes_from_old_previously_verified_value() {
1373 #[derive(Deserialize)]
1375 struct Container {
1376 verification_level: VerificationLevel,
1377 }
1378 let container = json!({ "verification_level": "PreviouslyVerified" });
1379
1380 let deserialized: Container = serde_json::from_value(container)
1382 .expect("We can deserialize the old PreviouslyVerified value");
1383
1384 assert_eq!(deserialized.verification_level, VerificationLevel::VerificationViolation);
1386 }
1387
1388 #[test]
1389 fn test_shield_state_code_deserializes() {
1390 #[derive(Deserialize)]
1392 struct Container {
1393 shield_state_code: ShieldStateCode,
1394 }
1395 let container = json!({ "shield_state_code": "VerificationViolation" });
1396
1397 let deserialized: Container = serde_json::from_value(container)
1399 .expect("We can deserialize the old PreviouslyVerified value");
1400
1401 assert_eq!(deserialized.shield_state_code, ShieldStateCode::VerificationViolation);
1403 }
1404
1405 #[test]
1406 fn test_shield_state_code_deserializes_from_old_previously_verified_value() {
1407 #[derive(Deserialize)]
1409 struct Container {
1410 shield_state_code: ShieldStateCode,
1411 }
1412 let container = json!({ "shield_state_code": "PreviouslyVerified" });
1413
1414 let deserialized: Container = serde_json::from_value(container)
1416 .expect("We can deserialize the old PreviouslyVerified value");
1417
1418 assert_eq!(deserialized.shield_state_code, ShieldStateCode::VerificationViolation);
1420 }
1421
1422 #[test]
1423 fn sync_timeline_event_serialisation() {
1424 let room_event = TimelineEvent {
1425 kind: TimelineEventKind::Decrypted(DecryptedRoomEvent {
1426 event: Raw::new(&example_event()).unwrap().cast_unchecked(),
1427 encryption_info: Arc::new(EncryptionInfo {
1428 sender: user_id!("@sender:example.com").to_owned(),
1429 sender_device: None,
1430 algorithm_info: AlgorithmInfo::MegolmV1AesSha2 {
1431 curve25519_key: "xxx".to_owned(),
1432 sender_claimed_keys: Default::default(),
1433 session_id: Some("xyz".to_owned()),
1434 },
1435 verification_state: VerificationState::Verified,
1436 }),
1437 unsigned_encryption_info: Some(BTreeMap::from([(
1438 UnsignedEventLocation::RelationsReplace,
1439 UnsignedDecryptionResult::UnableToDecrypt(UnableToDecryptInfo {
1440 session_id: Some("xyz".to_owned()),
1441 reason: UnableToDecryptReason::MalformedEncryptedEvent,
1442 }),
1443 )])),
1444 }),
1445 push_actions: Default::default(),
1446 thread_summary: ThreadSummaryStatus::Unknown,
1447 bundled_latest_thread_event: None,
1448 };
1449
1450 let serialized = serde_json::to_value(&room_event).unwrap();
1451
1452 assert_eq!(
1454 serialized,
1455 json!({
1456 "kind": {
1457 "Decrypted": {
1458 "event": {
1459 "content": {"body": "secret", "msgtype": "m.text"},
1460 "event_id": "$xxxxx:example.org",
1461 "origin_server_ts": 2189,
1462 "room_id": "!someroom:example.com",
1463 "sender": "@carl:example.com",
1464 "type": "m.room.message",
1465 },
1466 "encryption_info": {
1467 "sender": "@sender:example.com",
1468 "sender_device": null,
1469 "algorithm_info": {
1470 "MegolmV1AesSha2": {
1471 "curve25519_key": "xxx",
1472 "sender_claimed_keys": {},
1473 "session_id": "xyz",
1474 }
1475 },
1476 "verification_state": "Verified",
1477 },
1478 "unsigned_encryption_info": {
1479 "RelationsReplace": {"UnableToDecrypt": {
1480 "session_id": "xyz",
1481 "reason": "MalformedEncryptedEvent",
1482 }}
1483 }
1484 }
1485 }
1486 })
1487 );
1488
1489 let event: TimelineEvent = serde_json::from_value(serialized).unwrap();
1491 assert_eq!(event.event_id(), Some(event_id!("$xxxxx:example.org").to_owned()));
1492 assert_matches!(
1493 event.encryption_info().unwrap().algorithm_info,
1494 AlgorithmInfo::MegolmV1AesSha2 { .. }
1495 );
1496
1497 let serialized = json!({
1499 "event": {
1500 "content": {"body": "secret", "msgtype": "m.text"},
1501 "event_id": "$xxxxx:example.org",
1502 "origin_server_ts": 2189,
1503 "room_id": "!someroom:example.com",
1504 "sender": "@carl:example.com",
1505 "type": "m.room.message",
1506 },
1507 "encryption_info": {
1508 "sender": "@sender:example.com",
1509 "sender_device": null,
1510 "algorithm_info": {
1511 "MegolmV1AesSha2": {
1512 "curve25519_key": "xxx",
1513 "sender_claimed_keys": {}
1514 }
1515 },
1516 "verification_state": "Verified",
1517 },
1518 });
1519 let event: TimelineEvent = serde_json::from_value(serialized).unwrap();
1520 assert_eq!(event.event_id(), Some(event_id!("$xxxxx:example.org").to_owned()));
1521 assert_matches!(
1522 event.encryption_info().unwrap().algorithm_info,
1523 AlgorithmInfo::MegolmV1AesSha2 { session_id: None, .. }
1524 );
1525
1526 let serialized = json!({
1529 "event": {
1530 "content": {"body": "secret", "msgtype": "m.text"},
1531 "event_id": "$xxxxx:example.org",
1532 "origin_server_ts": 2189,
1533 "room_id": "!someroom:example.com",
1534 "sender": "@carl:example.com",
1535 "type": "m.room.message",
1536 },
1537 "encryption_info": {
1538 "sender": "@sender:example.com",
1539 "sender_device": null,
1540 "algorithm_info": {
1541 "MegolmV1AesSha2": {
1542 "curve25519_key": "xxx",
1543 "sender_claimed_keys": {}
1544 }
1545 },
1546 "verification_state": "Verified",
1547 },
1548 "unsigned_encryption_info": {
1549 "RelationsReplace": {"UnableToDecrypt": {"session_id": "xyz"}}
1550 }
1551 });
1552 let event: TimelineEvent = serde_json::from_value(serialized).unwrap();
1553 assert_eq!(event.event_id(), Some(event_id!("$xxxxx:example.org").to_owned()));
1554 assert_matches!(
1555 event.encryption_info().unwrap().algorithm_info,
1556 AlgorithmInfo::MegolmV1AesSha2 { .. }
1557 );
1558 assert_matches!(event.kind, TimelineEventKind::Decrypted(decrypted) => {
1559 assert_matches!(decrypted.unsigned_encryption_info, Some(map) => {
1560 assert_eq!(map.len(), 1);
1561 let (location, result) = map.into_iter().next().unwrap();
1562 assert_eq!(location, UnsignedEventLocation::RelationsReplace);
1563 assert_matches!(result, UnsignedDecryptionResult::UnableToDecrypt(utd_info) => {
1564 assert_eq!(utd_info.session_id, Some("xyz".to_owned()));
1565 assert_eq!(utd_info.reason, UnableToDecryptReason::Unknown);
1566 })
1567 });
1568 });
1569 }
1570
1571 #[test]
1572 fn test_creating_or_deserializing_an_event_extracts_summary() {
1573 let event = json!({
1574 "event_id": "$eid:example.com",
1575 "type": "m.room.message",
1576 "sender": "@alice:example.com",
1577 "origin_server_ts": 42,
1578 "content": {
1579 "body": "Hello, world!",
1580 },
1581 "unsigned": {
1582 "m.relations": {
1583 "m.thread": {
1584 "latest_event": {
1585 "event_id": "$latest_event:example.com",
1586 "type": "m.room.message",
1587 "sender": "@bob:example.com",
1588 "origin_server_ts": 42,
1589 "content": {
1590 "body": "Hello to you too!",
1591 "msgtype": "m.text",
1592 }
1593 },
1594 "count": 2,
1595 "current_user_participated": true,
1596 }
1597 }
1598 }
1599 });
1600
1601 let raw = Raw::new(&event).unwrap().cast_unchecked();
1602
1603 let timeline_event = TimelineEvent::from_plaintext(raw);
1606 assert_matches!(timeline_event.thread_summary, ThreadSummaryStatus::Some(ThreadSummary { num_replies, latest_reply }) => {
1607 assert_eq!(num_replies, 2);
1608 assert_eq!(latest_reply.as_deref(), Some(event_id!("$latest_event:example.com")));
1609 });
1610
1611 assert!(timeline_event.bundled_latest_thread_event.is_some());
1612
1613 let serialized_timeline_item = json!({
1616 "kind": {
1617 "PlainText": {
1618 "event": event
1619 }
1620 }
1621 });
1622
1623 let timeline_event: TimelineEvent =
1624 serde_json::from_value(serialized_timeline_item).unwrap();
1625 assert_matches!(timeline_event.thread_summary, ThreadSummaryStatus::Unknown);
1626
1627 assert!(timeline_event.bundled_latest_thread_event.is_none());
1630 }
1631
1632 #[test]
1633 fn sync_timeline_event_deserialisation_migration_for_withheld() {
1634 let serialized = json!({
1651 "kind": {
1652 "UnableToDecrypt": {
1653 "event": {
1654 "content": {
1655 "algorithm": "m.megolm.v1.aes-sha2",
1656 "ciphertext": "AwgAEoABzL1JYhqhjW9jXrlT3M6H8mJ4qffYtOQOnPuAPNxsuG20oiD/Fnpv6jnQGhU6YbV9pNM+1mRnTvxW3CbWOPjLKqCWTJTc7Q0vDEVtYePg38ncXNcwMmfhgnNAoW9S7vNs8C003x3yUl6NeZ8bH+ci870BZL+kWM/lMl10tn6U7snNmSjnE3ckvRdO+11/R4//5VzFQpZdf4j036lNSls/WIiI67Fk9iFpinz9xdRVWJFVdrAiPFwb8L5xRZ8aX+e2JDMlc1eW8gk",
1657 "device_id": "SKCGPNUWAU",
1658 "sender_key": "Gim/c7uQdSXyrrUbmUOrBT6sMC0gO7QSLmOK6B7NOm0",
1659 "session_id": "hgLyeSqXfb8vc5AjQLsg6TSHVu0HJ7HZ4B6jgMvxkrs"
1660 },
1661 "event_id": "$xxxxx:example.org",
1662 "origin_server_ts": 2189,
1663 "room_id": "!someroom:example.com",
1664 "sender": "@carl:example.com",
1665 "type": "m.room.message"
1666 },
1667 "utd_info": {
1668 "reason": "MissingMegolmSession",
1669 "session_id": "session000"
1670 }
1671 }
1672 }
1673 });
1674
1675 let result = serde_json::from_value(serialized);
1676 assert!(result.is_ok());
1677
1678 let event: TimelineEvent = result.unwrap();
1680 assert_matches!(
1681 event.kind,
1682 TimelineEventKind::UnableToDecrypt { utd_info, .. }=> {
1683 assert_matches!(
1684 utd_info.reason,
1685 UnableToDecryptReason::MissingMegolmSession { withheld_code: None }
1686 );
1687 }
1688 )
1689 }
1690
1691 #[test]
1692 fn unable_to_decrypt_info_migration_for_withheld() {
1693 let old_format = json!({
1694 "reason": "MissingMegolmSession",
1695 "session_id": "session000"
1696 });
1697
1698 let deserialized = serde_json::from_value::<UnableToDecryptInfo>(old_format).unwrap();
1699 let session_id = Some("session000".to_owned());
1700
1701 assert_eq!(deserialized.session_id, session_id);
1702 assert_eq!(
1703 deserialized.reason,
1704 UnableToDecryptReason::MissingMegolmSession { withheld_code: None },
1705 );
1706
1707 let new_format = json!({
1708 "session_id": "session000",
1709 "reason": {
1710 "MissingMegolmSession": {
1711 "withheld_code": null
1712 }
1713 }
1714 });
1715
1716 let deserialized = serde_json::from_value::<UnableToDecryptInfo>(new_format).unwrap();
1717
1718 assert_eq!(
1719 deserialized.reason,
1720 UnableToDecryptReason::MissingMegolmSession { withheld_code: None },
1721 );
1722 assert_eq!(deserialized.session_id, session_id);
1723 }
1724
1725 #[test]
1726 fn unable_to_decrypt_reason_is_missing_room_key() {
1727 let reason = UnableToDecryptReason::MissingMegolmSession { withheld_code: None };
1728 assert!(reason.is_missing_room_key());
1729
1730 let reason = UnableToDecryptReason::MissingMegolmSession {
1731 withheld_code: Some(WithheldCode::Blacklisted),
1732 };
1733 assert!(!reason.is_missing_room_key());
1734
1735 let reason = UnableToDecryptReason::UnknownMegolmMessageIndex;
1736 assert!(reason.is_missing_room_key());
1737 }
1738
1739 #[test]
1740 fn snapshot_test_verification_level() {
1741 with_settings!({ prepend_module_to_snapshot => false }, {
1742 assert_json_snapshot!(VerificationLevel::VerificationViolation);
1743 assert_json_snapshot!(VerificationLevel::UnsignedDevice);
1744 assert_json_snapshot!(VerificationLevel::None(DeviceLinkProblem::InsecureSource));
1745 assert_json_snapshot!(VerificationLevel::None(DeviceLinkProblem::MissingDevice));
1746 assert_json_snapshot!(VerificationLevel::UnverifiedIdentity);
1747 });
1748 }
1749
1750 #[test]
1751 fn snapshot_test_verification_states() {
1752 with_settings!({ prepend_module_to_snapshot => false }, {
1753 assert_json_snapshot!(VerificationState::Unverified(VerificationLevel::UnsignedDevice));
1754 assert_json_snapshot!(VerificationState::Unverified(
1755 VerificationLevel::VerificationViolation
1756 ));
1757 assert_json_snapshot!(VerificationState::Unverified(VerificationLevel::None(
1758 DeviceLinkProblem::InsecureSource,
1759 )));
1760 assert_json_snapshot!(VerificationState::Unverified(VerificationLevel::None(
1761 DeviceLinkProblem::MissingDevice,
1762 )));
1763 assert_json_snapshot!(VerificationState::Verified);
1764 });
1765 }
1766
1767 #[test]
1768 fn snapshot_test_shield_states() {
1769 with_settings!({ prepend_module_to_snapshot => false }, {
1770 assert_json_snapshot!(ShieldState::None);
1771 assert_json_snapshot!(ShieldState::Red {
1772 code: ShieldStateCode::UnverifiedIdentity,
1773 message: "a message"
1774 });
1775 assert_json_snapshot!(ShieldState::Grey {
1776 code: ShieldStateCode::AuthenticityNotGuaranteed,
1777 message: "authenticity of this message cannot be guaranteed",
1778 });
1779 });
1780 }
1781
1782 #[test]
1783 fn snapshot_test_shield_codes() {
1784 with_settings!({ prepend_module_to_snapshot => false }, {
1785 assert_json_snapshot!(ShieldStateCode::AuthenticityNotGuaranteed);
1786 assert_json_snapshot!(ShieldStateCode::UnknownDevice);
1787 assert_json_snapshot!(ShieldStateCode::UnsignedDevice);
1788 assert_json_snapshot!(ShieldStateCode::UnverifiedIdentity);
1789 assert_json_snapshot!(ShieldStateCode::SentInClear);
1790 assert_json_snapshot!(ShieldStateCode::VerificationViolation);
1791 });
1792 }
1793
1794 #[test]
1795 fn snapshot_test_algorithm_info() {
1796 let mut map = BTreeMap::new();
1797 map.insert(DeviceKeyAlgorithm::Curve25519, "claimedclaimedcurve25519".to_owned());
1798 map.insert(DeviceKeyAlgorithm::Ed25519, "claimedclaimeded25519".to_owned());
1799 let info = AlgorithmInfo::MegolmV1AesSha2 {
1800 curve25519_key: "curvecurvecurve".into(),
1801 sender_claimed_keys: BTreeMap::from([
1802 (DeviceKeyAlgorithm::Curve25519, "claimedclaimedcurve25519".to_owned()),
1803 (DeviceKeyAlgorithm::Ed25519, "claimedclaimeded25519".to_owned()),
1804 ]),
1805 session_id: None,
1806 };
1807
1808 with_settings!({ prepend_module_to_snapshot => false }, {
1809 assert_json_snapshot!(info)
1810 });
1811 }
1812
1813 #[test]
1814 fn test_encryption_info_migration() {
1815 let old_format = json!({
1818 "sender": "@alice:localhost",
1819 "sender_device": "ABCDEFGH",
1820 "algorithm_info": {
1821 "MegolmV1AesSha2": {
1822 "curve25519_key": "curvecurvecurve",
1823 "sender_claimed_keys": {}
1824 }
1825 },
1826 "verification_state": "Verified",
1827 "session_id": "mysessionid76"
1828 });
1829
1830 let deserialized = serde_json::from_value::<EncryptionInfo>(old_format).unwrap();
1831 let expected_session_id = Some("mysessionid76".to_owned());
1832
1833 assert_let!(
1834 AlgorithmInfo::MegolmV1AesSha2 { session_id, .. } = deserialized.algorithm_info.clone()
1835 );
1836 assert_eq!(session_id, expected_session_id);
1837
1838 assert_json_snapshot!(deserialized);
1839 }
1840
1841 #[test]
1842 fn snapshot_test_encryption_info() {
1843 let info = EncryptionInfo {
1844 sender: user_id!("@alice:localhost").to_owned(),
1845 sender_device: Some(device_id!("ABCDEFGH").to_owned()),
1846 algorithm_info: AlgorithmInfo::MegolmV1AesSha2 {
1847 curve25519_key: "curvecurvecurve".into(),
1848 sender_claimed_keys: Default::default(),
1849 session_id: Some("mysessionid76".to_owned()),
1850 },
1851 verification_state: VerificationState::Verified,
1852 };
1853
1854 with_settings!({ sort_maps => true, prepend_module_to_snapshot => false }, {
1855 assert_json_snapshot!(info)
1856 })
1857 }
1858
1859 #[test]
1860 fn snapshot_test_sync_timeline_event() {
1861 let room_event = TimelineEvent {
1862 kind: TimelineEventKind::Decrypted(DecryptedRoomEvent {
1863 event: Raw::new(&example_event()).unwrap().cast_unchecked(),
1864 encryption_info: Arc::new(EncryptionInfo {
1865 sender: user_id!("@sender:example.com").to_owned(),
1866 sender_device: Some(device_id!("ABCDEFGHIJ").to_owned()),
1867 algorithm_info: AlgorithmInfo::MegolmV1AesSha2 {
1868 curve25519_key: "xxx".to_owned(),
1869 sender_claimed_keys: BTreeMap::from([
1870 (
1871 DeviceKeyAlgorithm::Ed25519,
1872 "I3YsPwqMZQXHkSQbjFNEs7b529uac2xBpI83eN3LUXo".to_owned(),
1873 ),
1874 (
1875 DeviceKeyAlgorithm::Curve25519,
1876 "qzdW3F5IMPFl0HQgz5w/L5Oi/npKUFn8Um84acIHfPY".to_owned(),
1877 ),
1878 ]),
1879 session_id: Some("mysessionid112".to_owned()),
1880 },
1881 verification_state: VerificationState::Verified,
1882 }),
1883 unsigned_encryption_info: Some(BTreeMap::from([(
1884 UnsignedEventLocation::RelationsThreadLatestEvent,
1885 UnsignedDecryptionResult::UnableToDecrypt(UnableToDecryptInfo {
1886 session_id: Some("xyz".to_owned()),
1887 reason: UnableToDecryptReason::MissingMegolmSession {
1888 withheld_code: Some(WithheldCode::Unverified),
1889 },
1890 }),
1891 )])),
1892 }),
1893 push_actions: Default::default(),
1894 thread_summary: ThreadSummaryStatus::Some(ThreadSummary {
1895 num_replies: 2,
1896 latest_reply: None,
1897 }),
1898 bundled_latest_thread_event: None,
1899 };
1900
1901 with_settings!({ sort_maps => true, prepend_module_to_snapshot => false }, {
1902 assert_json_snapshot! {
1905 serde_json::to_value(&room_event).unwrap(),
1906 }
1907 });
1908 }
1909}