matrix_sdk_common/
deserialized_responses.rs

1// Copyright 2023 The Matrix.org Foundation C.I.C.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::{collections::BTreeMap, fmt};
16
17#[cfg(doc)]
18use ruma::events::AnyTimelineEvent;
19use ruma::{
20    events::{AnyMessageLikeEvent, AnySyncTimelineEvent},
21    push::Action,
22    serde::{
23        AsRefStr, AsStrAsRefStr, DebugAsRefStr, DeserializeFromCowStr, FromString, JsonObject, Raw,
24        SerializeAsRefStr,
25    },
26    DeviceKeyAlgorithm, OwnedDeviceId, OwnedEventId, OwnedUserId,
27};
28use serde::{Deserialize, Serialize};
29#[cfg(target_arch = "wasm32")]
30use wasm_bindgen::prelude::*;
31
32use crate::debug::{DebugRawEvent, DebugStructExt};
33
34const AUTHENTICITY_NOT_GUARANTEED: &str =
35    "The authenticity of this encrypted message can't be guaranteed on this device.";
36const UNVERIFIED_IDENTITY: &str = "Encrypted by an unverified user.";
37const VERIFICATION_VIOLATION: &str =
38    "Encrypted by a previously-verified user who is no longer verified.";
39const UNSIGNED_DEVICE: &str = "Encrypted by a device not verified by its owner.";
40const UNKNOWN_DEVICE: &str = "Encrypted by an unknown or deleted device.";
41pub const SENT_IN_CLEAR: &str = "Not encrypted.";
42
43/// Represents the state of verification for a decrypted message sent by a
44/// device.
45#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
46#[serde(from = "OldVerificationStateHelper")]
47pub enum VerificationState {
48    /// This message is guaranteed to be authentic as it is coming from a device
49    /// belonging to a user that we have verified.
50    ///
51    /// This is the only state where authenticity can be guaranteed.
52    Verified,
53
54    /// The message could not be linked to a verified device.
55    ///
56    /// For more detailed information on why the message is considered
57    /// unverified, refer to the VerificationLevel sub-enum.
58    Unverified(VerificationLevel),
59}
60
61// TODO: Remove this once we're confident that everybody that serialized these
62// states uses the new enum.
63#[derive(Clone, Debug, Deserialize)]
64enum OldVerificationStateHelper {
65    Untrusted,
66    UnknownDevice,
67    #[serde(alias = "Trusted")]
68    Verified,
69    Unverified(VerificationLevel),
70}
71
72impl From<OldVerificationStateHelper> for VerificationState {
73    fn from(value: OldVerificationStateHelper) -> Self {
74        match value {
75            // This mapping isn't strictly correct but we don't know which part in the old
76            // `VerificationState` enum was unverified.
77            OldVerificationStateHelper::Untrusted => {
78                VerificationState::Unverified(VerificationLevel::UnsignedDevice)
79            }
80            OldVerificationStateHelper::UnknownDevice => {
81                Self::Unverified(VerificationLevel::None(DeviceLinkProblem::MissingDevice))
82            }
83            OldVerificationStateHelper::Verified => Self::Verified,
84            OldVerificationStateHelper::Unverified(l) => Self::Unverified(l),
85        }
86    }
87}
88
89impl VerificationState {
90    /// Convert the `VerificationState` into a `ShieldState` which can be
91    /// directly used to decorate messages in the recommended way.
92    ///
93    /// This method decorates messages using a strict ruleset, for a more lax
94    /// variant of this method take a look at
95    /// [`VerificationState::to_shield_state_lax()`].
96    pub fn to_shield_state_strict(&self) -> ShieldState {
97        match self {
98            VerificationState::Verified => ShieldState::None,
99            VerificationState::Unverified(level) => match level {
100                VerificationLevel::UnverifiedIdentity
101                | VerificationLevel::VerificationViolation
102                | VerificationLevel::UnsignedDevice => ShieldState::Red {
103                    code: ShieldStateCode::UnverifiedIdentity,
104                    message: UNVERIFIED_IDENTITY,
105                },
106                VerificationLevel::None(link) => match link {
107                    DeviceLinkProblem::MissingDevice => ShieldState::Red {
108                        code: ShieldStateCode::UnknownDevice,
109                        message: UNKNOWN_DEVICE,
110                    },
111                    DeviceLinkProblem::InsecureSource => ShieldState::Red {
112                        code: ShieldStateCode::AuthenticityNotGuaranteed,
113                        message: AUTHENTICITY_NOT_GUARANTEED,
114                    },
115                },
116            },
117        }
118    }
119
120    /// Convert the `VerificationState` into a `ShieldState` which can be used
121    /// to decorate messages in the recommended way.
122    ///
123    /// This implements a legacy, lax decoration mode.
124    ///
125    /// For a more strict variant of this method take a look at
126    /// [`VerificationState::to_shield_state_strict()`].
127    pub fn to_shield_state_lax(&self) -> ShieldState {
128        match self {
129            VerificationState::Verified => ShieldState::None,
130            VerificationState::Unverified(level) => match level {
131                VerificationLevel::UnverifiedIdentity => {
132                    // If you didn't show interest in verifying that user we don't
133                    // nag you with an error message.
134                    ShieldState::None
135                }
136                VerificationLevel::VerificationViolation => {
137                    // This is a high warning. The sender was previously
138                    // verified, but changed their identity.
139                    ShieldState::Red {
140                        code: ShieldStateCode::VerificationViolation,
141                        message: VERIFICATION_VIOLATION,
142                    }
143                }
144                VerificationLevel::UnsignedDevice => {
145                    // This is a high warning. The sender hasn't verified his own device.
146                    ShieldState::Red {
147                        code: ShieldStateCode::UnsignedDevice,
148                        message: UNSIGNED_DEVICE,
149                    }
150                }
151                VerificationLevel::None(link) => match link {
152                    DeviceLinkProblem::MissingDevice => {
153                        // Have to warn as it could have been a temporary injected device.
154                        // Notice that the device might just not be known at this time, so callers
155                        // should retry when there is a device change for that user.
156                        ShieldState::Red {
157                            code: ShieldStateCode::UnknownDevice,
158                            message: UNKNOWN_DEVICE,
159                        }
160                    }
161                    DeviceLinkProblem::InsecureSource => {
162                        // In legacy mode, we tone down this warning as it is quite common and
163                        // mostly noise (due to legacy backup and lack of trusted forwards).
164                        ShieldState::Grey {
165                            code: ShieldStateCode::AuthenticityNotGuaranteed,
166                            message: AUTHENTICITY_NOT_GUARANTEED,
167                        }
168                    }
169                },
170            },
171        }
172    }
173}
174
175/// The sub-enum containing detailed information on why a message is considered
176/// to be unverified.
177#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
178pub enum VerificationLevel {
179    /// The message was sent by a user identity we have not verified.
180    UnverifiedIdentity,
181
182    /// The message was sent by a user identity we have not verified, but the
183    /// user was previously verified.
184    #[serde(alias = "PreviouslyVerified")]
185    VerificationViolation,
186
187    /// The message was sent by a device not linked to (signed by) any user
188    /// identity.
189    UnsignedDevice,
190
191    /// We weren't able to link the message back to any device. This might be
192    /// because the message claims to have been sent by a device which we have
193    /// not been able to obtain (for example, because the device was since
194    /// deleted) or because the key to decrypt the message was obtained from
195    /// an insecure source.
196    None(DeviceLinkProblem),
197}
198
199impl fmt::Display for VerificationLevel {
200    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
201        let display = match self {
202            VerificationLevel::UnverifiedIdentity => "The sender's identity was not verified",
203            VerificationLevel::VerificationViolation => {
204                "The sender's identity was previously verified but has changed"
205            }
206            VerificationLevel::UnsignedDevice => {
207                "The sending device was not signed by the user's identity"
208            }
209            VerificationLevel::None(..) => "The sending device is not known",
210        };
211        write!(f, "{}", display)
212    }
213}
214
215/// The sub-enum containing detailed information on why we were not able to link
216/// a message back to a device.
217#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
218pub enum DeviceLinkProblem {
219    /// The device is missing, either because it was deleted, or you haven't
220    /// yet downoaled it or the server is erroneously omitting it (federation
221    /// lag).
222    MissingDevice,
223    /// The key was obtained from an insecure source: imported from a file,
224    /// obtained from a legacy (asymmetric) backup, unsafe key forward, etc.
225    InsecureSource,
226}
227
228/// Recommended decorations for decrypted messages, representing the message's
229/// authenticity properties.
230#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq)]
231pub enum ShieldState {
232    /// A red shield with a tooltip containing the associated message should be
233    /// presented.
234    Red {
235        /// A machine-readable representation.
236        code: ShieldStateCode,
237        /// A human readable description.
238        message: &'static str,
239    },
240    /// A grey shield with a tooltip containing the associated message should be
241    /// presented.
242    Grey {
243        /// A machine-readable representation.
244        code: ShieldStateCode,
245        /// A human readable description.
246        message: &'static str,
247    },
248    /// No shield should be presented.
249    None,
250}
251
252/// A machine-readable representation of the authenticity for a `ShieldState`.
253#[derive(Clone, Copy, Debug, Deserialize, Serialize, Eq, PartialEq)]
254#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
255#[cfg_attr(target_arch = "wasm32", wasm_bindgen)]
256pub enum ShieldStateCode {
257    /// Not enough information available to check the authenticity.
258    AuthenticityNotGuaranteed,
259    /// The sending device isn't yet known by the Client.
260    UnknownDevice,
261    /// The sending device hasn't been verified by the sender.
262    UnsignedDevice,
263    /// The sender hasn't been verified by the Client's user.
264    UnverifiedIdentity,
265    /// An unencrypted event in an encrypted room.
266    SentInClear,
267    /// The sender was previously verified but changed their identity.
268    #[serde(alias = "PreviouslyVerified")]
269    VerificationViolation,
270}
271
272/// The algorithm specific information of a decrypted event.
273#[derive(Clone, Debug, Deserialize, Serialize)]
274pub enum AlgorithmInfo {
275    /// The info if the event was encrypted using m.megolm.v1.aes-sha2
276    MegolmV1AesSha2 {
277        /// The curve25519 key of the device that created the megolm decryption
278        /// key originally.
279        curve25519_key: String,
280        /// The signing keys that have created the megolm key that was used to
281        /// decrypt this session. This map will usually contain a single ed25519
282        /// key.
283        sender_claimed_keys: BTreeMap<DeviceKeyAlgorithm, String>,
284    },
285}
286
287/// Struct containing information on how an event was decrypted.
288#[derive(Clone, Debug, Deserialize, Serialize)]
289pub struct EncryptionInfo {
290    /// The user ID of the event sender, note this is untrusted data unless the
291    /// `verification_state` is `Verified` as well.
292    pub sender: OwnedUserId,
293    /// The device ID of the device that sent us the event, note this is
294    /// untrusted data unless `verification_state` is `Verified` as well.
295    pub sender_device: Option<OwnedDeviceId>,
296    /// Information about the algorithm that was used to encrypt the event.
297    pub algorithm_info: AlgorithmInfo,
298    /// The verification state of the device that sent us the event, note this
299    /// is the state of the device at the time of decryption. It may change in
300    /// the future if a device gets verified or deleted.
301    ///
302    /// Callers that persist this should mark the state as dirty when a device
303    /// change is received down the sync.
304    pub verification_state: VerificationState,
305}
306
307/// Represents a matrix room event that has been returned from `/sync`,
308/// after initial processing.
309///
310/// Previously, this differed from [`TimelineEvent`] by wrapping an
311/// [`AnySyncTimelineEvent`] instead of an [`AnyTimelineEvent`], but nowadays
312/// they are essentially identical, and one of them should probably be removed.
313//
314// 🚨 Note about this type, please read! 🚨
315//
316// `TimelineEvent` is heavily used across the SDK crates. In some cases, we
317// are reaching a [`recursion_limit`] when the compiler is trying to figure out
318// if `TimelineEvent` implements `Sync` when it's embedded in other types.
319//
320// We want to help the compiler so that one doesn't need to increase the
321// `recursion_limit`. We stop the recursive check by (un)safely implement `Sync`
322// and `Send` on `TimelineEvent` directly.
323//
324// See
325// https://github.com/matrix-org/matrix-rust-sdk/pull/3749#issuecomment-2312939823
326// which has addressed this issue first
327//
328// [`recursion_limit`]: https://doc.rust-lang.org/reference/attributes/limits.html#the-recursion_limit-attribute
329#[derive(Clone, Debug, Serialize)]
330pub struct TimelineEvent {
331    /// The event itself, together with any information on decryption.
332    pub kind: TimelineEventKind,
333
334    /// The push actions associated with this event.
335    ///
336    /// If it's set to `None`, then it means we couldn't compute those actions.
337    #[serde(skip_serializing_if = "Option::is_none")]
338    pub push_actions: Option<Vec<Action>>,
339}
340
341// See https://github.com/matrix-org/matrix-rust-sdk/pull/3749#issuecomment-2312939823.
342#[cfg(not(feature = "test-send-sync"))]
343unsafe impl Send for TimelineEvent {}
344
345// See https://github.com/matrix-org/matrix-rust-sdk/pull/3749#issuecomment-2312939823.
346#[cfg(not(feature = "test-send-sync"))]
347unsafe impl Sync for TimelineEvent {}
348
349#[cfg(feature = "test-send-sync")]
350#[test]
351// See https://github.com/matrix-org/matrix-rust-sdk/pull/3749#issuecomment-2312939823.
352fn test_send_sync_for_sync_timeline_event() {
353    fn assert_send_sync<T: Send + Sync>() {}
354
355    assert_send_sync::<TimelineEvent>();
356}
357
358impl TimelineEvent {
359    /// Create a new [`TimelineEvent`] from the given raw event.
360    ///
361    /// This is a convenience constructor for a plaintext event when you don't
362    /// need to set `push_action`, for example inside a test.
363    pub fn new(event: Raw<AnySyncTimelineEvent>) -> Self {
364        Self { kind: TimelineEventKind::PlainText { event }, push_actions: None }
365    }
366
367    /// Create a new [`TimelineEvent`] from the given raw event and push
368    /// actions.
369    ///
370    /// This is a convenience constructor for a plaintext event, for example
371    /// inside a test.
372    pub fn new_with_push_actions(
373        event: Raw<AnySyncTimelineEvent>,
374        push_actions: Vec<Action>,
375    ) -> Self {
376        Self { kind: TimelineEventKind::PlainText { event }, push_actions: Some(push_actions) }
377    }
378
379    /// Create a new [`TimelineEvent`] to represent the given decryption
380    /// failure.
381    pub fn new_utd_event(event: Raw<AnySyncTimelineEvent>, utd_info: UnableToDecryptInfo) -> Self {
382        Self { kind: TimelineEventKind::UnableToDecrypt { event, utd_info }, push_actions: None }
383    }
384
385    /// Get the event id of this [`TimelineEvent`] if the event has any valid
386    /// id.
387    pub fn event_id(&self) -> Option<OwnedEventId> {
388        self.kind.event_id()
389    }
390
391    /// Returns a reference to the (potentially decrypted) Matrix event inside
392    /// this [`TimelineEvent`].
393    pub fn raw(&self) -> &Raw<AnySyncTimelineEvent> {
394        self.kind.raw()
395    }
396
397    /// Replace the raw event included in this item by another one.
398    pub fn replace_raw(&mut self, replacement: Raw<AnyMessageLikeEvent>) {
399        match &mut self.kind {
400            TimelineEventKind::Decrypted(decrypted) => decrypted.event = replacement,
401            TimelineEventKind::UnableToDecrypt { event, .. }
402            | TimelineEventKind::PlainText { event } => {
403                // It's safe to cast `AnyMessageLikeEvent` into `AnySyncMessageLikeEvent`,
404                // because the former contains a superset of the fields included in the latter.
405                *event = replacement.cast();
406            }
407        }
408    }
409
410    /// If the event was a decrypted event that was successfully decrypted, get
411    /// its encryption info. Otherwise, `None`.
412    pub fn encryption_info(&self) -> Option<&EncryptionInfo> {
413        self.kind.encryption_info()
414    }
415
416    /// Takes ownership of this `TimelineEvent`, returning the (potentially
417    /// decrypted) Matrix event within.
418    pub fn into_raw(self) -> Raw<AnySyncTimelineEvent> {
419        self.kind.into_raw()
420    }
421}
422
423impl From<DecryptedRoomEvent> for TimelineEvent {
424    fn from(decrypted: DecryptedRoomEvent) -> Self {
425        Self { kind: TimelineEventKind::Decrypted(decrypted), push_actions: None }
426    }
427}
428
429impl<'de> Deserialize<'de> for TimelineEvent {
430    /// Custom deserializer for [`TimelineEvent`], to support older formats.
431    ///
432    /// Ideally we might use an untagged enum and then convert from that;
433    /// however, that doesn't work due to a [serde bug](https://github.com/serde-rs/json/issues/497).
434    ///
435    /// Instead, we first deserialize into an unstructured JSON map, and then
436    /// inspect the json to figure out which format we have.
437    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
438    where
439        D: serde::Deserializer<'de>,
440    {
441        use serde_json::{Map, Value};
442
443        // First, deserialize to an unstructured JSON map
444        let value = Map::<String, Value>::deserialize(deserializer)?;
445
446        // If we have a top-level `event`, it's V0
447        if value.contains_key("event") {
448            let v0: SyncTimelineEventDeserializationHelperV0 =
449                serde_json::from_value(Value::Object(value)).map_err(|e| {
450                    serde::de::Error::custom(format!(
451                        "Unable to deserialize V0-format TimelineEvent: {}",
452                        e
453                    ))
454                })?;
455            Ok(v0.into())
456        }
457        // Otherwise, it's V1
458        else {
459            let v1: SyncTimelineEventDeserializationHelperV1 =
460                serde_json::from_value(Value::Object(value)).map_err(|e| {
461                    serde::de::Error::custom(format!(
462                        "Unable to deserialize V1-format TimelineEvent: {}",
463                        e
464                    ))
465                })?;
466            Ok(v1.into())
467        }
468    }
469}
470
471/// The event within a [`TimelineEvent`], together with encryption data.
472#[derive(Clone, Serialize, Deserialize)]
473pub enum TimelineEventKind {
474    /// A successfully-decrypted encrypted event.
475    Decrypted(DecryptedRoomEvent),
476
477    /// An encrypted event which could not be decrypted.
478    UnableToDecrypt {
479        /// The `m.room.encrypted` event. Depending on the source of the event,
480        /// it could actually be an [`AnyTimelineEvent`] (i.e., it may
481        /// have a `room_id` property).
482        event: Raw<AnySyncTimelineEvent>,
483
484        /// Information on the reason we failed to decrypt
485        utd_info: UnableToDecryptInfo,
486    },
487
488    /// An unencrypted event.
489    PlainText {
490        /// The actual event. Depending on the source of the event, it could
491        /// actually be a [`AnyTimelineEvent`] (which differs from
492        /// [`AnySyncTimelineEvent`] by the addition of a `room_id` property).
493        event: Raw<AnySyncTimelineEvent>,
494    },
495}
496
497impl TimelineEventKind {
498    /// Returns a reference to the (potentially decrypted) Matrix event inside
499    /// this `TimelineEvent`.
500    pub fn raw(&self) -> &Raw<AnySyncTimelineEvent> {
501        match self {
502            // It is safe to cast from an `AnyMessageLikeEvent` (i.e. JSON which does
503            // *not* contain a `state_key` and *does* contain a `room_id`) into an
504            // `AnySyncTimelineEvent` (i.e. JSON which *may* contain a `state_key` and is *not*
505            // expected to contain a `room_id`). It just means that the `room_id` will be ignored
506            // in a future deserialization.
507            TimelineEventKind::Decrypted(d) => d.event.cast_ref(),
508            TimelineEventKind::UnableToDecrypt { event, .. } => event.cast_ref(),
509            TimelineEventKind::PlainText { event } => event,
510        }
511    }
512
513    /// Get the event id of this `TimelineEventKind` if the event has any valid
514    /// id.
515    pub fn event_id(&self) -> Option<OwnedEventId> {
516        self.raw().get_field::<OwnedEventId>("event_id").ok().flatten()
517    }
518
519    /// If the event was a decrypted event that was successfully decrypted, get
520    /// its encryption info. Otherwise, `None`.
521    pub fn encryption_info(&self) -> Option<&EncryptionInfo> {
522        match self {
523            TimelineEventKind::Decrypted(d) => Some(&d.encryption_info),
524            TimelineEventKind::UnableToDecrypt { .. } => None,
525            TimelineEventKind::PlainText { .. } => None,
526        }
527    }
528
529    /// Takes ownership of this `TimelineEvent`, returning the (potentially
530    /// decrypted) Matrix event within.
531    pub fn into_raw(self) -> Raw<AnySyncTimelineEvent> {
532        match self {
533            // It is safe to cast from an `AnyMessageLikeEvent` (i.e. JSON which does
534            // *not* contain a `state_key` and *does* contain a `room_id`) into an
535            // `AnySyncTimelineEvent` (i.e. JSON which *may* contain a `state_key` and is *not*
536            // expected to contain a `room_id`). It just means that the `room_id` will be ignored
537            // in a future deserialization.
538            TimelineEventKind::Decrypted(d) => d.event.cast(),
539            TimelineEventKind::UnableToDecrypt { event, .. } => event.cast(),
540            TimelineEventKind::PlainText { event } => event,
541        }
542    }
543}
544
545#[cfg(not(tarpaulin_include))]
546impl fmt::Debug for TimelineEventKind {
547    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
548        match &self {
549            Self::PlainText { event } => f
550                .debug_struct("TimelineEventDecryptionResult::PlainText")
551                .field("event", &DebugRawEvent(event))
552                .finish(),
553
554            Self::UnableToDecrypt { event, utd_info } => f
555                .debug_struct("TimelineEventDecryptionResult::UnableToDecrypt")
556                .field("event", &DebugRawEvent(event))
557                .field("utd_info", &utd_info)
558                .finish(),
559
560            Self::Decrypted(decrypted) => {
561                f.debug_tuple("TimelineEventDecryptionResult::Decrypted").field(decrypted).finish()
562            }
563        }
564    }
565}
566
567#[derive(Clone, Serialize, Deserialize)]
568/// A successfully-decrypted encrypted event.
569pub struct DecryptedRoomEvent {
570    /// The decrypted event.
571    ///
572    /// Note: it's not an error that this contains an `AnyMessageLikeEvent`: an
573    /// encrypted payload *always contains* a room id, by the [spec].
574    ///
575    /// [spec]: https://spec.matrix.org/v1.12/client-server-api/#mmegolmv1aes-sha2
576    pub event: Raw<AnyMessageLikeEvent>,
577
578    /// The encryption info about the event.
579    pub encryption_info: EncryptionInfo,
580
581    /// The encryption info about the events bundled in the `unsigned`
582    /// object.
583    ///
584    /// Will be `None` if no bundled event was encrypted.
585    #[serde(skip_serializing_if = "Option::is_none")]
586    pub unsigned_encryption_info: Option<BTreeMap<UnsignedEventLocation, UnsignedDecryptionResult>>,
587}
588
589#[cfg(not(tarpaulin_include))]
590impl fmt::Debug for DecryptedRoomEvent {
591    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
592        let DecryptedRoomEvent { event, encryption_info, unsigned_encryption_info } = self;
593
594        f.debug_struct("DecryptedRoomEvent")
595            .field("event", &DebugRawEvent(event))
596            .field("encryption_info", encryption_info)
597            .maybe_field("unsigned_encryption_info", unsigned_encryption_info)
598            .finish()
599    }
600}
601
602/// The location of an event bundled in an `unsigned` object.
603#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
604pub enum UnsignedEventLocation {
605    /// An event at the `m.replace` key of the `m.relations` object, that is a
606    /// bundled replacement.
607    RelationsReplace,
608    /// An event at the `latest_event` key of the `m.thread` object of the
609    /// `m.relations` object, that is the latest event of a thread.
610    RelationsThreadLatestEvent,
611}
612
613impl UnsignedEventLocation {
614    /// Find the mutable JSON value at this location in the given unsigned
615    /// object.
616    ///
617    /// # Arguments
618    ///
619    /// * `unsigned` - The `unsigned` property of an event as a JSON object.
620    pub fn find_mut<'a>(&self, unsigned: &'a mut JsonObject) -> Option<&'a mut serde_json::Value> {
621        let relations = unsigned.get_mut("m.relations")?.as_object_mut()?;
622
623        match self {
624            Self::RelationsReplace => relations.get_mut("m.replace"),
625            Self::RelationsThreadLatestEvent => {
626                relations.get_mut("m.thread")?.as_object_mut()?.get_mut("latest_event")
627            }
628        }
629    }
630}
631
632/// The result of the decryption of an event bundled in an `unsigned` object.
633#[derive(Debug, Clone, Serialize, Deserialize)]
634pub enum UnsignedDecryptionResult {
635    /// The event was successfully decrypted.
636    Decrypted(EncryptionInfo),
637    /// The event failed to be decrypted.
638    UnableToDecrypt(UnableToDecryptInfo),
639}
640
641/// Metadata about an event that could not be decrypted.
642#[derive(Debug, Clone, Serialize, Deserialize)]
643pub struct UnableToDecryptInfo {
644    /// The ID of the session used to encrypt the message, if it used the
645    /// `m.megolm.v1.aes-sha2` algorithm.
646    #[serde(skip_serializing_if = "Option::is_none")]
647    pub session_id: Option<String>,
648
649    /// Reason code for the decryption failure
650    #[serde(default = "unknown_utd_reason", deserialize_with = "deserialize_utd_reason")]
651    pub reason: UnableToDecryptReason,
652}
653
654fn unknown_utd_reason() -> UnableToDecryptReason {
655    UnableToDecryptReason::Unknown
656}
657
658/// Provides basic backward compatibility for deserializing older serialized
659/// `UnableToDecryptReason` values.
660pub fn deserialize_utd_reason<'de, D>(d: D) -> Result<UnableToDecryptReason, D::Error>
661where
662    D: serde::Deserializer<'de>,
663{
664    // Start by deserializing as to an untyped JSON value.
665    let v: serde_json::Value = Deserialize::deserialize(d)?;
666    // Backwards compatibility: `MissingMegolmSession` used to be stored without the
667    // withheld code.
668    if v.as_str().is_some_and(|s| s == "MissingMegolmSession") {
669        return Ok(UnableToDecryptReason::MissingMegolmSession { withheld_code: None });
670    }
671    // Otherwise, use the derived deserialize impl to turn the JSON into a
672    // UnableToDecryptReason
673    serde_json::from_value::<UnableToDecryptReason>(v).map_err(serde::de::Error::custom)
674}
675
676/// Reason code for a decryption failure
677#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
678pub enum UnableToDecryptReason {
679    /// The reason for the decryption failure is unknown. This is only intended
680    /// for use when deserializing old UnableToDecryptInfo instances.
681    #[doc(hidden)]
682    Unknown,
683
684    /// The `m.room.encrypted` event that should have been decrypted is
685    /// malformed in some way (e.g. unsupported algorithm, missing fields,
686    /// unknown megolm message type).
687    MalformedEncryptedEvent,
688
689    /// Decryption failed because we're missing the megolm session that was used
690    /// to encrypt the event.
691    MissingMegolmSession {
692        /// If the key was withheld on purpose, the associated code. `None`
693        /// means no withheld code was received.
694        withheld_code: Option<WithheldCode>,
695    },
696
697    /// Decryption failed because, while we have the megolm session that was
698    /// used to encrypt the message, it is ratcheted too far forward.
699    UnknownMegolmMessageIndex,
700
701    /// We found the Megolm session, but were unable to decrypt the event using
702    /// that session for some reason (e.g. incorrect MAC).
703    ///
704    /// This represents all `vodozemac::megolm::DecryptionError`s, except
705    /// `UnknownMessageIndex`, which is represented as
706    /// `UnknownMegolmMessageIndex`.
707    MegolmDecryptionFailure,
708
709    /// The event could not be deserialized after decryption.
710    PayloadDeserializationFailure,
711
712    /// Decryption failed because of a mismatch between the identity keys of the
713    /// device we received the room key from and the identity keys recorded in
714    /// the plaintext of the room key to-device message.
715    MismatchedIdentityKeys,
716
717    /// An encrypted message wasn't decrypted, because the sender's
718    /// cross-signing identity did not satisfy the requested
719    /// `TrustRequirement`.
720    SenderIdentityNotTrusted(VerificationLevel),
721}
722
723impl UnableToDecryptReason {
724    /// Returns true if this UTD is due to a missing room key (and hence might
725    /// resolve itself if we wait a bit.)
726    pub fn is_missing_room_key(&self) -> bool {
727        // In case of MissingMegolmSession with a withheld code we return false here
728        // given that this API is used to decide if waiting a bit will help.
729        matches!(
730            self,
731            Self::MissingMegolmSession { withheld_code: None } | Self::UnknownMegolmMessageIndex
732        )
733    }
734}
735
736/// A machine-readable code for why a Megolm key was not sent.
737///
738/// Normally sent as the payload of an [`m.room_key.withheld`](https://spec.matrix.org/v1.12/client-server-api/#mroom_keywithheld) to-device message.
739#[derive(
740    Clone,
741    PartialEq,
742    Eq,
743    Hash,
744    AsStrAsRefStr,
745    AsRefStr,
746    FromString,
747    DebugAsRefStr,
748    SerializeAsRefStr,
749    DeserializeFromCowStr,
750)]
751pub enum WithheldCode {
752    /// the user/device was blacklisted.
753    #[ruma_enum(rename = "m.blacklisted")]
754    Blacklisted,
755
756    /// the user/devices is unverified.
757    #[ruma_enum(rename = "m.unverified")]
758    Unverified,
759
760    /// The user/device is not allowed have the key. For example, this would
761    /// usually be sent in response to a key request if the user was not in
762    /// the room when the message was sent.
763    #[ruma_enum(rename = "m.unauthorised")]
764    Unauthorised,
765
766    /// Sent in reply to a key request if the device that the key is requested
767    /// from does not have the requested key.
768    #[ruma_enum(rename = "m.unavailable")]
769    Unavailable,
770
771    /// An olm session could not be established.
772    /// This may happen, for example, if the sender was unable to obtain a
773    /// one-time key from the recipient.
774    #[ruma_enum(rename = "m.no_olm")]
775    NoOlm,
776
777    #[doc(hidden)]
778    _Custom(PrivOwnedStr),
779}
780
781impl fmt::Display for WithheldCode {
782    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
783        let string = match self {
784            WithheldCode::Blacklisted => "The sender has blocked you.",
785            WithheldCode::Unverified => "The sender has disabled encrypting to unverified devices.",
786            WithheldCode::Unauthorised => "You are not authorised to read the message.",
787            WithheldCode::Unavailable => "The requested key was not found.",
788            WithheldCode::NoOlm => "Unable to establish a secure channel.",
789            _ => self.as_str(),
790        };
791
792        f.write_str(string)
793    }
794}
795
796// The Ruma macro expects the type to have this name.
797// The payload is counter intuitively made public in order to avoid having
798// multiple copies of this struct.
799#[doc(hidden)]
800#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
801pub struct PrivOwnedStr(pub Box<str>);
802
803#[cfg(not(tarpaulin_include))]
804impl fmt::Debug for PrivOwnedStr {
805    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
806        self.0.fmt(f)
807    }
808}
809
810/// Deserialization helper for [`TimelineEvent`], for the modern format.
811///
812/// This has the exact same fields as [`TimelineEvent`] itself, but has a
813/// regular `Deserialize` implementation.
814#[derive(Debug, Deserialize)]
815struct SyncTimelineEventDeserializationHelperV1 {
816    /// The event itself, together with any information on decryption.
817    kind: TimelineEventKind,
818
819    /// The push actions associated with this event.
820    #[serde(default)]
821    push_actions: Vec<Action>,
822}
823
824impl From<SyncTimelineEventDeserializationHelperV1> for TimelineEvent {
825    fn from(value: SyncTimelineEventDeserializationHelperV1) -> Self {
826        let SyncTimelineEventDeserializationHelperV1 { kind, push_actions } = value;
827        TimelineEvent { kind, push_actions: Some(push_actions) }
828    }
829}
830
831/// Deserialization helper for [`TimelineEvent`], for an older format.
832#[derive(Deserialize)]
833struct SyncTimelineEventDeserializationHelperV0 {
834    /// The actual event.
835    event: Raw<AnySyncTimelineEvent>,
836
837    /// The encryption info about the event. Will be `None` if the event
838    /// was not encrypted.
839    encryption_info: Option<EncryptionInfo>,
840
841    /// The push actions associated with this event.
842    #[serde(default)]
843    push_actions: Vec<Action>,
844
845    /// The encryption info about the events bundled in the `unsigned`
846    /// object.
847    ///
848    /// Will be `None` if no bundled event was encrypted.
849    unsigned_encryption_info: Option<BTreeMap<UnsignedEventLocation, UnsignedDecryptionResult>>,
850}
851
852impl From<SyncTimelineEventDeserializationHelperV0> for TimelineEvent {
853    fn from(value: SyncTimelineEventDeserializationHelperV0) -> Self {
854        let SyncTimelineEventDeserializationHelperV0 {
855            event,
856            encryption_info,
857            push_actions,
858            unsigned_encryption_info,
859        } = value;
860
861        let kind = match encryption_info {
862            Some(encryption_info) => {
863                TimelineEventKind::Decrypted(DecryptedRoomEvent {
864                    // We cast from `Raw<AnySyncTimelineEvent>` to
865                    // `Raw<AnyMessageLikeEvent>`, which means
866                    // we are asserting that it contains a room_id.
867                    // That *should* be ok, because if this is genuinely a decrypted
868                    // room event (as the encryption_info indicates), then it will have
869                    // a room_id.
870                    event: event.cast(),
871                    encryption_info,
872                    unsigned_encryption_info,
873                })
874            }
875
876            None => TimelineEventKind::PlainText { event },
877        };
878
879        TimelineEvent { kind, push_actions: Some(push_actions) }
880    }
881}
882
883#[cfg(test)]
884mod tests {
885    use std::collections::BTreeMap;
886
887    use assert_matches::assert_matches;
888    use insta::{assert_json_snapshot, with_settings};
889    use ruma::{
890        device_id, event_id, events::room::message::RoomMessageEventContent, serde::Raw, user_id,
891        DeviceKeyAlgorithm,
892    };
893    use serde::Deserialize;
894    use serde_json::json;
895
896    use super::{
897        AlgorithmInfo, DecryptedRoomEvent, DeviceLinkProblem, EncryptionInfo, ShieldState,
898        ShieldStateCode, TimelineEvent, TimelineEventKind, UnableToDecryptInfo,
899        UnableToDecryptReason, UnsignedDecryptionResult, UnsignedEventLocation, VerificationLevel,
900        VerificationState, WithheldCode,
901    };
902
903    fn example_event() -> serde_json::Value {
904        json!({
905            "content": RoomMessageEventContent::text_plain("secret"),
906            "type": "m.room.message",
907            "event_id": "$xxxxx:example.org",
908            "room_id": "!someroom:example.com",
909            "origin_server_ts": 2189,
910            "sender": "@carl:example.com",
911        })
912    }
913
914    #[test]
915    fn sync_timeline_debug_content() {
916        let room_event = TimelineEvent::new(Raw::new(&example_event()).unwrap().cast());
917        let debug_s = format!("{room_event:?}");
918        assert!(
919            !debug_s.contains("secret"),
920            "Debug representation contains event content!\n{debug_s}"
921        );
922    }
923
924    #[test]
925    fn old_verification_state_to_new_migration() {
926        #[derive(Deserialize)]
927        struct State {
928            state: VerificationState,
929        }
930
931        let state = json!({
932            "state": "Trusted",
933        });
934        let deserialized: State =
935            serde_json::from_value(state).expect("We can deserialize the old trusted value");
936        assert_eq!(deserialized.state, VerificationState::Verified);
937
938        let state = json!({
939            "state": "UnknownDevice",
940        });
941
942        let deserialized: State =
943            serde_json::from_value(state).expect("We can deserialize the old unknown device value");
944
945        assert_eq!(
946            deserialized.state,
947            VerificationState::Unverified(VerificationLevel::None(
948                DeviceLinkProblem::MissingDevice
949            ))
950        );
951
952        let state = json!({
953            "state": "Untrusted",
954        });
955        let deserialized: State =
956            serde_json::from_value(state).expect("We can deserialize the old trusted value");
957
958        assert_eq!(
959            deserialized.state,
960            VerificationState::Unverified(VerificationLevel::UnsignedDevice)
961        );
962    }
963
964    #[test]
965    fn test_verification_level_deserializes() {
966        // Given a JSON VerificationLevel
967        #[derive(Deserialize)]
968        struct Container {
969            verification_level: VerificationLevel,
970        }
971        let container = json!({ "verification_level": "VerificationViolation" });
972
973        // When we deserialize it
974        let deserialized: Container = serde_json::from_value(container)
975            .expect("We can deserialize the old PreviouslyVerified value");
976
977        // Then it is populated correctly
978        assert_eq!(deserialized.verification_level, VerificationLevel::VerificationViolation);
979    }
980
981    #[test]
982    fn test_verification_level_deserializes_from_old_previously_verified_value() {
983        // Given a JSON VerificationLevel with the old value PreviouslyVerified
984        #[derive(Deserialize)]
985        struct Container {
986            verification_level: VerificationLevel,
987        }
988        let container = json!({ "verification_level": "PreviouslyVerified" });
989
990        // When we deserialize it
991        let deserialized: Container = serde_json::from_value(container)
992            .expect("We can deserialize the old PreviouslyVerified value");
993
994        // Then it is migrated to the new value
995        assert_eq!(deserialized.verification_level, VerificationLevel::VerificationViolation);
996    }
997
998    #[test]
999    fn test_shield_state_code_deserializes() {
1000        // Given a JSON ShieldStateCode with value VerificationViolation
1001        #[derive(Deserialize)]
1002        struct Container {
1003            shield_state_code: ShieldStateCode,
1004        }
1005        let container = json!({ "shield_state_code": "VerificationViolation" });
1006
1007        // When we deserialize it
1008        let deserialized: Container = serde_json::from_value(container)
1009            .expect("We can deserialize the old PreviouslyVerified value");
1010
1011        // Then it is populated correctly
1012        assert_eq!(deserialized.shield_state_code, ShieldStateCode::VerificationViolation);
1013    }
1014
1015    #[test]
1016    fn test_shield_state_code_deserializes_from_old_previously_verified_value() {
1017        // Given a JSON ShieldStateCode with the old value PreviouslyVerified
1018        #[derive(Deserialize)]
1019        struct Container {
1020            shield_state_code: ShieldStateCode,
1021        }
1022        let container = json!({ "shield_state_code": "PreviouslyVerified" });
1023
1024        // When we deserialize it
1025        let deserialized: Container = serde_json::from_value(container)
1026            .expect("We can deserialize the old PreviouslyVerified value");
1027
1028        // Then it is migrated to the new value
1029        assert_eq!(deserialized.shield_state_code, ShieldStateCode::VerificationViolation);
1030    }
1031
1032    #[test]
1033    fn sync_timeline_event_serialisation() {
1034        let room_event = TimelineEvent {
1035            kind: TimelineEventKind::Decrypted(DecryptedRoomEvent {
1036                event: Raw::new(&example_event()).unwrap().cast(),
1037                encryption_info: EncryptionInfo {
1038                    sender: user_id!("@sender:example.com").to_owned(),
1039                    sender_device: None,
1040                    algorithm_info: AlgorithmInfo::MegolmV1AesSha2 {
1041                        curve25519_key: "xxx".to_owned(),
1042                        sender_claimed_keys: Default::default(),
1043                    },
1044                    verification_state: VerificationState::Verified,
1045                },
1046                unsigned_encryption_info: Some(BTreeMap::from([(
1047                    UnsignedEventLocation::RelationsReplace,
1048                    UnsignedDecryptionResult::UnableToDecrypt(UnableToDecryptInfo {
1049                        session_id: Some("xyz".to_owned()),
1050                        reason: UnableToDecryptReason::MalformedEncryptedEvent,
1051                    }),
1052                )])),
1053            }),
1054            push_actions: Default::default(),
1055        };
1056
1057        let serialized = serde_json::to_value(&room_event).unwrap();
1058
1059        // Test that the serialization is as expected
1060        assert_eq!(
1061            serialized,
1062            json!({
1063                "kind": {
1064                    "Decrypted": {
1065                        "event": {
1066                            "content": {"body": "secret", "msgtype": "m.text"},
1067                            "event_id": "$xxxxx:example.org",
1068                            "origin_server_ts": 2189,
1069                            "room_id": "!someroom:example.com",
1070                            "sender": "@carl:example.com",
1071                            "type": "m.room.message",
1072                        },
1073                        "encryption_info": {
1074                            "sender": "@sender:example.com",
1075                            "sender_device": null,
1076                            "algorithm_info": {
1077                                "MegolmV1AesSha2": {
1078                                    "curve25519_key": "xxx",
1079                                    "sender_claimed_keys": {}
1080                                }
1081                            },
1082                            "verification_state": "Verified",
1083                        },
1084                        "unsigned_encryption_info": {
1085                            "RelationsReplace": {"UnableToDecrypt": {
1086                                "session_id": "xyz",
1087                                "reason": "MalformedEncryptedEvent",
1088                            }}
1089                        }
1090                    }
1091                }
1092            })
1093        );
1094
1095        // And it can be properly deserialized from the new format.
1096        let event: TimelineEvent = serde_json::from_value(serialized).unwrap();
1097        assert_eq!(event.event_id(), Some(event_id!("$xxxxx:example.org").to_owned()));
1098        assert_matches!(
1099            event.encryption_info().unwrap().algorithm_info,
1100            AlgorithmInfo::MegolmV1AesSha2 { .. }
1101        );
1102
1103        // Test that the previous format can also be deserialized.
1104        let serialized = json!({
1105            "event": {
1106                "content": {"body": "secret", "msgtype": "m.text"},
1107                "event_id": "$xxxxx:example.org",
1108                "origin_server_ts": 2189,
1109                "room_id": "!someroom:example.com",
1110                "sender": "@carl:example.com",
1111                "type": "m.room.message",
1112            },
1113            "encryption_info": {
1114                "sender": "@sender:example.com",
1115                "sender_device": null,
1116                "algorithm_info": {
1117                    "MegolmV1AesSha2": {
1118                        "curve25519_key": "xxx",
1119                        "sender_claimed_keys": {}
1120                    }
1121                },
1122                "verification_state": "Verified",
1123            },
1124        });
1125        let event: TimelineEvent = serde_json::from_value(serialized).unwrap();
1126        assert_eq!(event.event_id(), Some(event_id!("$xxxxx:example.org").to_owned()));
1127        assert_matches!(
1128            event.encryption_info().unwrap().algorithm_info,
1129            AlgorithmInfo::MegolmV1AesSha2 { .. }
1130        );
1131
1132        // Test that the previous format, with an undecryptable unsigned event, can also
1133        // be deserialized.
1134        let serialized = json!({
1135            "event": {
1136                "content": {"body": "secret", "msgtype": "m.text"},
1137                "event_id": "$xxxxx:example.org",
1138                "origin_server_ts": 2189,
1139                "room_id": "!someroom:example.com",
1140                "sender": "@carl:example.com",
1141                "type": "m.room.message",
1142            },
1143            "encryption_info": {
1144                "sender": "@sender:example.com",
1145                "sender_device": null,
1146                "algorithm_info": {
1147                    "MegolmV1AesSha2": {
1148                        "curve25519_key": "xxx",
1149                        "sender_claimed_keys": {}
1150                    }
1151                },
1152                "verification_state": "Verified",
1153            },
1154            "unsigned_encryption_info": {
1155                "RelationsReplace": {"UnableToDecrypt": {"session_id": "xyz"}}
1156            }
1157        });
1158        let event: TimelineEvent = serde_json::from_value(serialized).unwrap();
1159        assert_eq!(event.event_id(), Some(event_id!("$xxxxx:example.org").to_owned()));
1160        assert_matches!(
1161            event.encryption_info().unwrap().algorithm_info,
1162            AlgorithmInfo::MegolmV1AesSha2 { .. }
1163        );
1164        assert_matches!(event.kind, TimelineEventKind::Decrypted(decrypted) => {
1165            assert_matches!(decrypted.unsigned_encryption_info, Some(map) => {
1166                assert_eq!(map.len(), 1);
1167                let (location, result) = map.into_iter().next().unwrap();
1168                assert_eq!(location, UnsignedEventLocation::RelationsReplace);
1169                assert_matches!(result, UnsignedDecryptionResult::UnableToDecrypt(utd_info) => {
1170                    assert_eq!(utd_info.session_id, Some("xyz".to_owned()));
1171                    assert_eq!(utd_info.reason, UnableToDecryptReason::Unknown);
1172                })
1173            });
1174        });
1175    }
1176
1177    #[test]
1178    fn sync_timeline_event_deserialisation_migration_for_withheld() {
1179        // Old serialized version was
1180        //    "utd_info": {
1181        //         "reason": "MissingMegolmSession",
1182        //         "session_id": "session000"
1183        //       }
1184
1185        // The new version would be
1186        //      "utd_info": {
1187        //         "reason": {
1188        //           "MissingMegolmSession": {
1189        //              "withheld_code": null
1190        //           }
1191        //         },
1192        //         "session_id": "session000"
1193        //       }
1194
1195        let serialized = json!({
1196             "kind": {
1197                "UnableToDecrypt": {
1198                  "event": {
1199                    "content": {
1200                      "algorithm": "m.megolm.v1.aes-sha2",
1201                      "ciphertext": "AwgAEoABzL1JYhqhjW9jXrlT3M6H8mJ4qffYtOQOnPuAPNxsuG20oiD/Fnpv6jnQGhU6YbV9pNM+1mRnTvxW3CbWOPjLKqCWTJTc7Q0vDEVtYePg38ncXNcwMmfhgnNAoW9S7vNs8C003x3yUl6NeZ8bH+ci870BZL+kWM/lMl10tn6U7snNmSjnE3ckvRdO+11/R4//5VzFQpZdf4j036lNSls/WIiI67Fk9iFpinz9xdRVWJFVdrAiPFwb8L5xRZ8aX+e2JDMlc1eW8gk",
1202                      "device_id": "SKCGPNUWAU",
1203                      "sender_key": "Gim/c7uQdSXyrrUbmUOrBT6sMC0gO7QSLmOK6B7NOm0",
1204                      "session_id": "hgLyeSqXfb8vc5AjQLsg6TSHVu0HJ7HZ4B6jgMvxkrs"
1205                    },
1206                    "event_id": "$xxxxx:example.org",
1207                    "origin_server_ts": 2189,
1208                    "room_id": "!someroom:example.com",
1209                    "sender": "@carl:example.com",
1210                    "type": "m.room.message"
1211                  },
1212                  "utd_info": {
1213                    "reason": "MissingMegolmSession",
1214                    "session_id": "session000"
1215                  }
1216                }
1217              }
1218        });
1219
1220        let result = serde_json::from_value(serialized);
1221        assert!(result.is_ok());
1222
1223        // should have migrated to the new format
1224        let event: TimelineEvent = result.unwrap();
1225        assert_matches!(
1226            event.kind,
1227            TimelineEventKind::UnableToDecrypt { utd_info, .. }=> {
1228                assert_matches!(
1229                    utd_info.reason,
1230                    UnableToDecryptReason::MissingMegolmSession { withheld_code: None }
1231                );
1232            }
1233        )
1234    }
1235
1236    #[test]
1237    fn unable_to_decrypt_info_migration_for_withheld() {
1238        let old_format = json!({
1239            "reason": "MissingMegolmSession",
1240            "session_id": "session000"
1241        });
1242
1243        let deserialized = serde_json::from_value::<UnableToDecryptInfo>(old_format).unwrap();
1244        let session_id = Some("session000".to_owned());
1245
1246        assert_eq!(deserialized.session_id, session_id);
1247        assert_eq!(
1248            deserialized.reason,
1249            UnableToDecryptReason::MissingMegolmSession { withheld_code: None },
1250        );
1251
1252        let new_format = json!({
1253             "session_id": "session000",
1254              "reason": {
1255                "MissingMegolmSession": {
1256                  "withheld_code": null
1257                }
1258              }
1259        });
1260
1261        let deserialized = serde_json::from_value::<UnableToDecryptInfo>(new_format).unwrap();
1262
1263        assert_eq!(
1264            deserialized.reason,
1265            UnableToDecryptReason::MissingMegolmSession { withheld_code: None },
1266        );
1267        assert_eq!(deserialized.session_id, session_id);
1268    }
1269
1270    #[test]
1271    fn unable_to_decrypt_reason_is_missing_room_key() {
1272        let reason = UnableToDecryptReason::MissingMegolmSession { withheld_code: None };
1273        assert!(reason.is_missing_room_key());
1274
1275        let reason = UnableToDecryptReason::MissingMegolmSession {
1276            withheld_code: Some(WithheldCode::Blacklisted),
1277        };
1278        assert!(!reason.is_missing_room_key());
1279
1280        let reason = UnableToDecryptReason::UnknownMegolmMessageIndex;
1281        assert!(reason.is_missing_room_key());
1282    }
1283
1284    #[test]
1285    fn snapshot_test_verification_level() {
1286        assert_json_snapshot!(VerificationLevel::VerificationViolation);
1287        assert_json_snapshot!(VerificationLevel::UnsignedDevice);
1288        assert_json_snapshot!(VerificationLevel::None(DeviceLinkProblem::InsecureSource));
1289        assert_json_snapshot!(VerificationLevel::None(DeviceLinkProblem::MissingDevice));
1290        assert_json_snapshot!(VerificationLevel::UnverifiedIdentity);
1291    }
1292
1293    #[test]
1294    fn snapshot_test_verification_states() {
1295        assert_json_snapshot!(VerificationState::Unverified(VerificationLevel::UnsignedDevice));
1296        assert_json_snapshot!(VerificationState::Unverified(
1297            VerificationLevel::VerificationViolation
1298        ));
1299        assert_json_snapshot!(VerificationState::Unverified(VerificationLevel::None(
1300            DeviceLinkProblem::InsecureSource,
1301        )));
1302        assert_json_snapshot!(VerificationState::Unverified(VerificationLevel::None(
1303            DeviceLinkProblem::MissingDevice,
1304        )));
1305        assert_json_snapshot!(VerificationState::Verified);
1306    }
1307
1308    #[test]
1309    fn snapshot_test_shield_states() {
1310        assert_json_snapshot!(ShieldState::None);
1311        assert_json_snapshot!(ShieldState::Red {
1312            code: ShieldStateCode::UnverifiedIdentity,
1313            message: "a message"
1314        });
1315        assert_json_snapshot!(ShieldState::Grey {
1316            code: ShieldStateCode::AuthenticityNotGuaranteed,
1317            message: "authenticity of this message cannot be guaranteed",
1318        });
1319    }
1320
1321    #[test]
1322    fn snapshot_test_shield_codes() {
1323        assert_json_snapshot!(ShieldStateCode::AuthenticityNotGuaranteed);
1324        assert_json_snapshot!(ShieldStateCode::UnknownDevice);
1325        assert_json_snapshot!(ShieldStateCode::UnsignedDevice);
1326        assert_json_snapshot!(ShieldStateCode::UnverifiedIdentity);
1327        assert_json_snapshot!(ShieldStateCode::SentInClear);
1328        assert_json_snapshot!(ShieldStateCode::VerificationViolation);
1329    }
1330
1331    #[test]
1332    fn snapshot_test_algorithm_info() {
1333        let mut map = BTreeMap::new();
1334        map.insert(DeviceKeyAlgorithm::Curve25519, "claimedclaimedcurve25519".to_owned());
1335        map.insert(DeviceKeyAlgorithm::Ed25519, "claimedclaimeded25519".to_owned());
1336        let info = AlgorithmInfo::MegolmV1AesSha2 {
1337            curve25519_key: "curvecurvecurve".into(),
1338            sender_claimed_keys: BTreeMap::from([
1339                (DeviceKeyAlgorithm::Curve25519, "claimedclaimedcurve25519".to_owned()),
1340                (DeviceKeyAlgorithm::Ed25519, "claimedclaimeded25519".to_owned()),
1341            ]),
1342        };
1343
1344        assert_json_snapshot!(info)
1345    }
1346
1347    #[test]
1348    fn snapshot_test_encryption_info() {
1349        let info = EncryptionInfo {
1350            sender: user_id!("@alice:localhost").to_owned(),
1351            sender_device: Some(device_id!("ABCDEFGH").to_owned()),
1352            algorithm_info: AlgorithmInfo::MegolmV1AesSha2 {
1353                curve25519_key: "curvecurvecurve".into(),
1354                sender_claimed_keys: Default::default(),
1355            },
1356            verification_state: VerificationState::Verified,
1357        };
1358
1359        with_settings!({sort_maps =>true}, {
1360            assert_json_snapshot!(info)
1361        })
1362    }
1363
1364    #[test]
1365    fn snapshot_test_sync_timeline_event() {
1366        let room_event = TimelineEvent {
1367            kind: TimelineEventKind::Decrypted(DecryptedRoomEvent {
1368                event: Raw::new(&example_event()).unwrap().cast(),
1369                encryption_info: EncryptionInfo {
1370                    sender: user_id!("@sender:example.com").to_owned(),
1371                    sender_device: Some(device_id!("ABCDEFGHIJ").to_owned()),
1372                    algorithm_info: AlgorithmInfo::MegolmV1AesSha2 {
1373                        curve25519_key: "xxx".to_owned(),
1374                        sender_claimed_keys: BTreeMap::from([
1375                            (
1376                                DeviceKeyAlgorithm::Ed25519,
1377                                "I3YsPwqMZQXHkSQbjFNEs7b529uac2xBpI83eN3LUXo".to_owned(),
1378                            ),
1379                            (
1380                                DeviceKeyAlgorithm::Curve25519,
1381                                "qzdW3F5IMPFl0HQgz5w/L5Oi/npKUFn8Um84acIHfPY".to_owned(),
1382                            ),
1383                        ]),
1384                    },
1385                    verification_state: VerificationState::Verified,
1386                },
1387                unsigned_encryption_info: Some(BTreeMap::from([(
1388                    UnsignedEventLocation::RelationsThreadLatestEvent,
1389                    UnsignedDecryptionResult::UnableToDecrypt(UnableToDecryptInfo {
1390                        session_id: Some("xyz".to_owned()),
1391                        reason: UnableToDecryptReason::MissingMegolmSession {
1392                            withheld_code: Some(WithheldCode::Unverified),
1393                        },
1394                    }),
1395                )])),
1396            }),
1397            push_actions: Default::default(),
1398        };
1399
1400        with_settings!({sort_maps =>true}, {
1401            // We use directly the serde_json formatter here, because of a bug in insta
1402            // not serializing custom BTreeMap key enum https://github.com/mitsuhiko/insta/issues/689
1403            assert_json_snapshot! {
1404                serde_json::to_value(&room_event).unwrap(),
1405            }
1406        });
1407    }
1408}