matrix_sdk_crypto/olm/
account.rs

1// Copyright 2020 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::{
16    collections::{BTreeMap, HashMap},
17    fmt,
18    ops::{Deref, Not as _},
19    sync::Arc,
20    time::Duration,
21};
22
23use hkdf::Hkdf;
24use js_option::JsOption;
25use matrix_sdk_common::deserialized_responses::{
26    AlgorithmInfo, DeviceLinkProblem, EncryptionInfo, VerificationLevel, VerificationState,
27};
28#[cfg(test)]
29use ruma::api::client::dehydrated_device::DehydratedDeviceV1;
30use ruma::{
31    CanonicalJsonValue, DeviceId, DeviceKeyAlgorithm, DeviceKeyId, MilliSecondsSinceUnixEpoch,
32    OneTimeKeyAlgorithm, OneTimeKeyId, OwnedDeviceId, OwnedDeviceKeyId, OwnedOneTimeKeyId,
33    OwnedUserId, RoomId, SecondsSinceUnixEpoch, UInt, UserId,
34    api::client::{
35        dehydrated_device::{DehydratedDeviceData, DehydratedDeviceV2},
36        keys::{
37            upload_keys,
38            upload_signatures::v3::{Request as SignatureUploadRequest, SignedKeys},
39        },
40    },
41    canonical_json::to_canonical_value,
42    events::{AnyToDeviceEvent, room::history_visibility::HistoryVisibility},
43    serde::Raw,
44};
45use serde::{Deserialize, Serialize, de::Error};
46use serde_json::value::{RawValue as RawJsonValue, to_raw_value};
47use sha2::{Digest, Sha256};
48use tokio::sync::Mutex;
49use tracing::{Span, debug, field::debug, info, instrument, trace, warn};
50use vodozemac::{
51    Curve25519PublicKey, Ed25519Signature, KeyId, PickleError, base64_encode,
52    olm::{
53        Account as InnerAccount, AccountPickle, IdentityKeys, OlmMessage,
54        OneTimeKeyGenerationResult, PreKeyMessage, SessionConfig,
55    },
56};
57
58use super::{
59    EncryptionSettings, InboundGroupSession, OutboundGroupSession, PrivateCrossSigningIdentity,
60    Session, SessionCreationError as MegolmSessionCreationError, utility::SignJson,
61};
62#[cfg(feature = "experimental-algorithms")]
63use crate::types::events::room::encrypted::OlmV2Curve25519AesSha2Content;
64use crate::{
65    DecryptionSettings, Device, OlmError, SignatureError, TrustRequirement,
66    dehydrated_devices::DehydrationError,
67    error::{EventError, OlmResult, SessionCreationError},
68    identities::DeviceData,
69    olm::SenderData,
70    store::{
71        Store,
72        types::{Changes, DeviceChanges},
73    },
74    types::{
75        CrossSigningKey, DeviceKeys, EventEncryptionAlgorithm, MasterPubkey, OneTimeKey, SignedKey,
76        events::{
77            olm_v1::AnyDecryptedOlmEvent,
78            room::encrypted::{
79                EncryptedToDeviceEvent, OlmV1Curve25519AesSha2Content,
80                ToDeviceEncryptedEventContent,
81            },
82        },
83        requests::UploadSigningKeysRequest,
84    },
85};
86
87#[derive(Debug)]
88enum PrekeyBundle {
89    Olm3DH { key: SignedKey },
90}
91
92#[derive(Debug, Clone)]
93pub(crate) enum SessionType {
94    New(Session),
95    Existing(Session),
96}
97
98#[derive(Debug)]
99pub struct InboundCreationResult {
100    pub session: Session,
101    pub plaintext: String,
102}
103
104impl SessionType {
105    #[cfg(test)]
106    pub fn session(self) -> Session {
107        match self {
108            SessionType::New(s) => s,
109            SessionType::Existing(s) => s,
110        }
111    }
112}
113
114/// A struct witnessing a successful decryption of an Olm-encrypted to-device
115/// event.
116///
117/// Contains the decrypted event plaintext along with some associated metadata,
118/// such as the identity (Curve25519) key of the to-device event sender.
119#[derive(Debug)]
120pub(crate) struct OlmDecryptionInfo {
121    pub session: SessionType,
122    pub message_hash: OlmMessageHash,
123    pub inbound_group_session: Option<InboundGroupSession>,
124    pub result: DecryptionResult,
125}
126
127#[derive(Debug)]
128pub(crate) struct DecryptionResult {
129    // AnyDecryptedOlmEvent is pretty big at 512 bytes, box it to reduce stack size
130    pub event: Box<AnyDecryptedOlmEvent>,
131    pub raw_event: Raw<AnyToDeviceEvent>,
132    pub sender_key: Curve25519PublicKey,
133    pub encryption_info: EncryptionInfo,
134}
135
136/// A hash of a successfully decrypted Olm message.
137///
138/// Can be used to check if a message has been replayed to us.
139#[derive(Debug, Clone, Serialize, Deserialize)]
140pub struct OlmMessageHash {
141    /// The curve25519 key of the sender that sent us the Olm message.
142    pub sender_key: String,
143    /// The hash of the message.
144    pub hash: String,
145}
146
147impl OlmMessageHash {
148    fn new(sender_key: Curve25519PublicKey, ciphertext: &OlmMessage) -> Self {
149        let (message_type, ciphertext) = ciphertext.clone().to_parts();
150        let sender_key = sender_key.to_base64();
151
152        let sha = Sha256::new()
153            .chain_update(sender_key.as_bytes())
154            .chain_update([message_type as u8])
155            .chain_update(ciphertext)
156            .finalize();
157
158        Self { sender_key, hash: base64_encode(sha.as_slice()) }
159    }
160}
161
162/// Account data that's static for the lifetime of a Client.
163///
164/// This data never changes once it's set, so it can be freely passed and cloned
165/// everywhere.
166#[derive(Clone)]
167#[cfg_attr(not(tarpaulin_include), derive(Debug))]
168pub struct StaticAccountData {
169    /// The user_id this account belongs to.
170    pub user_id: OwnedUserId,
171    /// The device_id of this entry.
172    pub device_id: OwnedDeviceId,
173    /// The associated identity keys.
174    pub identity_keys: Arc<IdentityKeys>,
175    /// Whether the account is for a dehydrated device.
176    pub dehydrated: bool,
177    // The creation time of the account in milliseconds since epoch.
178    creation_local_time: MilliSecondsSinceUnixEpoch,
179}
180
181impl StaticAccountData {
182    const ALGORITHMS: &'static [&'static EventEncryptionAlgorithm] = &[
183        &EventEncryptionAlgorithm::OlmV1Curve25519AesSha2,
184        #[cfg(feature = "experimental-algorithms")]
185        &EventEncryptionAlgorithm::OlmV2Curve25519AesSha2,
186        &EventEncryptionAlgorithm::MegolmV1AesSha2,
187        #[cfg(feature = "experimental-algorithms")]
188        &EventEncryptionAlgorithm::MegolmV2AesSha2,
189    ];
190
191    /// Create a group session pair.
192    ///
193    /// This session pair can be used to encrypt and decrypt messages meant for
194    /// a large group of participants.
195    ///
196    /// The outbound session is used to encrypt messages while the inbound one
197    /// is used to decrypt messages encrypted by the outbound one.
198    ///
199    /// # Arguments
200    ///
201    /// * `room_id` - The ID of the room where the group session will be used.
202    ///
203    /// * `settings` - Settings determining the algorithm and rotation period of
204    ///   the outbound group session.
205    pub async fn create_group_session_pair(
206        &self,
207        room_id: &RoomId,
208        settings: EncryptionSettings,
209        own_sender_data: SenderData,
210    ) -> Result<(OutboundGroupSession, InboundGroupSession), MegolmSessionCreationError> {
211        trace!(?room_id, algorithm = settings.algorithm.as_str(), "Creating a new room key");
212
213        let visibility = settings.history_visibility.clone();
214        let algorithm = settings.algorithm.to_owned();
215
216        let outbound = OutboundGroupSession::new(
217            self.device_id.clone(),
218            self.identity_keys.clone(),
219            room_id,
220            settings,
221        )?;
222
223        let identity_keys = &self.identity_keys;
224
225        let sender_key = identity_keys.curve25519;
226        let signing_key = identity_keys.ed25519;
227        let shared_history = shared_history_from_history_visibility(&visibility);
228
229        let inbound = InboundGroupSession::new(
230            sender_key,
231            signing_key,
232            room_id,
233            &outbound.session_key().await,
234            own_sender_data,
235            None,
236            algorithm,
237            Some(visibility),
238            shared_history,
239        )?;
240
241        Ok((outbound, inbound))
242    }
243
244    #[cfg(any(test, feature = "testing"))]
245    #[allow(dead_code)]
246    /// Testing only facility to create a group session pair with default
247    /// settings.
248    pub async fn create_group_session_pair_with_defaults(
249        &self,
250        room_id: &RoomId,
251    ) -> (OutboundGroupSession, InboundGroupSession) {
252        self.create_group_session_pair(
253            room_id,
254            EncryptionSettings::default(),
255            SenderData::unknown(),
256        )
257        .await
258        .expect("Can't create default group session pair")
259    }
260
261    /// Get the key ID of our Ed25519 signing key.
262    pub fn signing_key_id(&self) -> OwnedDeviceKeyId {
263        DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, self.device_id())
264    }
265
266    /// Check if the given JSON is signed by this Account key.
267    ///
268    /// This method should only be used if an object's signature needs to be
269    /// checked multiple times, and you'd like to avoid performing the
270    /// canonicalization step each time.
271    ///
272    /// **Note**: Use this method with caution, the `canonical_json` needs to be
273    /// correctly canonicalized and make sure that the object you are checking
274    /// the signature for is allowed to be signed by our own device.
275    pub fn has_signed_raw(
276        &self,
277        signatures: &crate::types::Signatures,
278        canonical_json: &str,
279    ) -> Result<(), SignatureError> {
280        use crate::olm::utility::VerifyJson;
281
282        let signing_key = self.identity_keys.ed25519;
283
284        signing_key.verify_canonicalized_json(
285            &self.user_id,
286            &DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, self.device_id()),
287            signatures,
288            canonical_json,
289        )
290    }
291
292    /// Generate the unsigned `DeviceKeys` from this `StaticAccountData`.
293    pub fn unsigned_device_keys(&self) -> DeviceKeys {
294        let identity_keys = self.identity_keys();
295        let keys = BTreeMap::from([
296            (
297                DeviceKeyId::from_parts(DeviceKeyAlgorithm::Curve25519, &self.device_id),
298                identity_keys.curve25519.into(),
299            ),
300            (
301                DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, &self.device_id),
302                identity_keys.ed25519.into(),
303            ),
304        ]);
305
306        let mut ret = DeviceKeys::new(
307            (*self.user_id).to_owned(),
308            (*self.device_id).to_owned(),
309            Self::ALGORITHMS.iter().map(|a| (**a).clone()).collect(),
310            keys,
311            Default::default(),
312        );
313        if self.dehydrated {
314            ret.dehydrated = JsOption::Some(true);
315        }
316        ret
317    }
318
319    /// Get the user id of the owner of the account.
320    pub fn user_id(&self) -> &UserId {
321        &self.user_id
322    }
323
324    /// Get the device ID that owns this account.
325    pub fn device_id(&self) -> &DeviceId {
326        &self.device_id
327    }
328
329    /// Get the public parts of the identity keys for the account.
330    pub fn identity_keys(&self) -> IdentityKeys {
331        *self.identity_keys
332    }
333
334    /// Get the local timestamp creation of the account in secs since epoch.
335    pub fn creation_local_time(&self) -> MilliSecondsSinceUnixEpoch {
336        self.creation_local_time
337    }
338}
339
340/// Account holding identity keys for which sessions can be created.
341///
342/// An account is the central identity for encrypted communication between two
343/// devices.
344pub struct Account {
345    pub(crate) static_data: StaticAccountData,
346    /// `vodozemac` account.
347    inner: Box<InnerAccount>,
348    /// Is this account ready to encrypt messages? (i.e. has it shared keys with
349    /// a homeserver)
350    shared: bool,
351    /// The number of signed one-time keys we have uploaded to the server. If
352    /// this is None, no action will be taken. After a sync request the client
353    /// needs to set this for us, depending on the count we will suggest the
354    /// client to upload new keys.
355    uploaded_signed_key_count: u64,
356    /// The timestamp of the last time we generated a fallback key. Fallback
357    /// keys are rotated in a time-based manner. This field records when we
358    /// either generated our first fallback key or rotated one.
359    ///
360    /// Will be `None` if we never created a fallback key, or if we're migrating
361    /// from a `AccountPickle` that didn't use time-based fallback key
362    /// rotation.
363    fallback_creation_timestamp: Option<MilliSecondsSinceUnixEpoch>,
364}
365
366impl Deref for Account {
367    type Target = StaticAccountData;
368
369    fn deref(&self) -> &Self::Target {
370        &self.static_data
371    }
372}
373
374/// A pickled version of an `Account`.
375///
376/// Holds all the information that needs to be stored in a database to restore
377/// an account.
378#[derive(Serialize, Deserialize)]
379#[allow(missing_debug_implementations)]
380pub struct PickledAccount {
381    /// The user id of the account owner.
382    pub user_id: OwnedUserId,
383    /// The device ID of the account owner.
384    pub device_id: OwnedDeviceId,
385    /// The pickled version of the Olm account.
386    pub pickle: AccountPickle,
387    /// Was the account shared.
388    pub shared: bool,
389    /// Whether this is for a dehydrated device
390    #[serde(default)]
391    pub dehydrated: bool,
392    /// The number of uploaded one-time keys we have on the server.
393    pub uploaded_signed_key_count: u64,
394    /// The local time creation of this account (milliseconds since epoch), used
395    /// as creation time of own device
396    #[serde(default = "default_account_creation_time")]
397    pub creation_local_time: MilliSecondsSinceUnixEpoch,
398    /// The timestamp of the last time we generated a fallback key.
399    #[serde(default)]
400    pub fallback_key_creation_timestamp: Option<MilliSecondsSinceUnixEpoch>,
401}
402
403fn default_account_creation_time() -> MilliSecondsSinceUnixEpoch {
404    MilliSecondsSinceUnixEpoch(UInt::default())
405}
406
407#[cfg(not(tarpaulin_include))]
408impl fmt::Debug for Account {
409    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
410        f.debug_struct("Account")
411            .field("identity_keys", &self.identity_keys())
412            .field("shared", &self.shared())
413            .finish()
414    }
415}
416
417pub type OneTimeKeys = BTreeMap<OwnedOneTimeKeyId, Raw<ruma::encryption::OneTimeKey>>;
418pub type FallbackKeys = OneTimeKeys;
419
420impl Account {
421    pub(crate) fn new_helper(
422        mut account: InnerAccount,
423        user_id: &UserId,
424        device_id: &DeviceId,
425    ) -> Self {
426        let identity_keys = account.identity_keys();
427
428        // Let's generate some initial one-time keys while we're here. Since we know
429        // that this is a completely new [`Account`] we're certain that the
430        // server does not yet have any one-time keys of ours.
431        //
432        // This ensures we upload one-time keys along with our device keys right
433        // away, rather than waiting for the key counts to be echoed back to us
434        // from the server.
435        //
436        // It would be nice to do this for the fallback key as well but we can't assume
437        // that the server supports fallback keys. Maybe one of these days we
438        // will be able to do so.
439        account.generate_one_time_keys(account.max_number_of_one_time_keys());
440
441        Self {
442            static_data: StaticAccountData {
443                user_id: user_id.into(),
444                device_id: device_id.into(),
445                identity_keys: Arc::new(identity_keys),
446                dehydrated: false,
447                creation_local_time: MilliSecondsSinceUnixEpoch::now(),
448            },
449            inner: Box::new(account),
450            shared: false,
451            uploaded_signed_key_count: 0,
452            fallback_creation_timestamp: None,
453        }
454    }
455
456    /// Create a fresh new account, this will generate the identity key-pair.
457    pub fn with_device_id(user_id: &UserId, device_id: &DeviceId) -> Self {
458        let account = InnerAccount::new();
459
460        Self::new_helper(account, user_id, device_id)
461    }
462
463    /// Create a new random Olm Account, the long-term Curve25519 identity key
464    /// encoded as base64 will be used for the device ID.
465    pub fn new(user_id: &UserId) -> Self {
466        let account = InnerAccount::new();
467        let device_id: OwnedDeviceId =
468            base64_encode(account.identity_keys().curve25519.as_bytes()).into();
469
470        Self::new_helper(account, user_id, &device_id)
471    }
472
473    /// Create a new random Olm Account for a dehydrated device
474    pub fn new_dehydrated(user_id: &UserId) -> Self {
475        let account = InnerAccount::new();
476        let device_id: OwnedDeviceId =
477            base64_encode(account.identity_keys().curve25519.as_bytes()).into();
478
479        let mut ret = Self::new_helper(account, user_id, &device_id);
480        ret.static_data.dehydrated = true;
481        ret
482    }
483
484    /// Get the immutable data for this account.
485    pub fn static_data(&self) -> &StaticAccountData {
486        &self.static_data
487    }
488
489    /// Update the uploaded key count.
490    ///
491    /// # Arguments
492    ///
493    /// * `new_count` - The new count that was reported by the server.
494    pub fn update_uploaded_key_count(&mut self, new_count: u64) {
495        self.uploaded_signed_key_count = new_count;
496    }
497
498    /// Get the currently known uploaded key count.
499    pub fn uploaded_key_count(&self) -> u64 {
500        self.uploaded_signed_key_count
501    }
502
503    /// Has the account been shared with the server.
504    pub fn shared(&self) -> bool {
505        self.shared
506    }
507
508    /// Mark the account as shared.
509    ///
510    /// Messages shouldn't be encrypted with the session before it has been
511    /// shared.
512    pub fn mark_as_shared(&mut self) {
513        self.shared = true;
514    }
515
516    /// Get the one-time keys of the account.
517    ///
518    /// This can be empty, keys need to be generated first.
519    pub fn one_time_keys(&self) -> HashMap<KeyId, Curve25519PublicKey> {
520        self.inner.one_time_keys()
521    }
522
523    /// Generate count number of one-time keys.
524    pub fn generate_one_time_keys(&mut self, count: usize) -> OneTimeKeyGenerationResult {
525        self.inner.generate_one_time_keys(count)
526    }
527
528    /// Get the maximum number of one-time keys the account can hold.
529    pub fn max_one_time_keys(&self) -> usize {
530        self.inner.max_number_of_one_time_keys()
531    }
532
533    pub(crate) fn update_key_counts(
534        &mut self,
535        one_time_key_counts: &BTreeMap<OneTimeKeyAlgorithm, UInt>,
536        unused_fallback_keys: Option<&[OneTimeKeyAlgorithm]>,
537    ) {
538        if let Some(count) = one_time_key_counts.get(&OneTimeKeyAlgorithm::SignedCurve25519) {
539            let count: u64 = (*count).into();
540            let old_count = self.uploaded_key_count();
541
542            // Some servers might always return the key counts in the sync
543            // response, we don't want to the logs with noop changes if they do
544            // so.
545            if count != old_count {
546                debug!(
547                    "Updated uploaded one-time key count {} -> {count}.",
548                    self.uploaded_key_count(),
549                );
550            }
551
552            self.update_uploaded_key_count(count);
553            self.generate_one_time_keys_if_needed();
554        }
555
556        // If the server supports fallback keys or if it did so in the past, shown by
557        // the existence of a fallback creation timestamp, generate a new one if
558        // we don't have one, or if the current fallback key expired.
559        if unused_fallback_keys.is_some() || self.fallback_creation_timestamp.is_some() {
560            self.generate_fallback_key_if_needed();
561        }
562    }
563
564    /// Generate new one-time keys that need to be uploaded to the server.
565    ///
566    /// Returns None if no keys need to be uploaded, otherwise the number of
567    /// newly generated one-time keys. May return 0 if some one-time keys are
568    /// already generated but weren't uploaded.
569    ///
570    /// Generally `Some` means that keys should be uploaded, while `None` means
571    /// that keys should not be uploaded.
572    #[instrument(skip_all)]
573    pub fn generate_one_time_keys_if_needed(&mut self) -> Option<u64> {
574        // Only generate one-time keys if there aren't any, otherwise the caller
575        // might have failed to upload them the last time this method was
576        // called.
577        if !self.one_time_keys().is_empty() {
578            return Some(0);
579        }
580
581        let count = self.uploaded_key_count();
582        let max_keys = self.max_one_time_keys();
583
584        if count >= max_keys as u64 {
585            return None;
586        }
587
588        let key_count = (max_keys as u64) - count;
589        let key_count: usize = key_count.try_into().unwrap_or(max_keys);
590
591        let result = self.generate_one_time_keys(key_count);
592
593        debug!(
594            count = key_count,
595            discarded_keys = ?result.removed,
596            created_keys = ?result.created,
597            "Generated new one-time keys"
598        );
599
600        Some(key_count as u64)
601    }
602
603    /// Generate a new fallback key iff a unpublished one isn't already inside
604    /// of vodozemac and if the currently active one expired.
605    ///
606    /// The former is checked using [`Account::fallback_key().is_empty()`],
607    /// which is a hashmap that gets cleared by the
608    /// [`Account::mark_keys_as_published()`] call.
609    pub(crate) fn generate_fallback_key_if_needed(&mut self) {
610        if self.inner.fallback_key().is_empty() && self.fallback_key_expired() {
611            let removed_fallback_key = self.inner.generate_fallback_key();
612            self.fallback_creation_timestamp = Some(MilliSecondsSinceUnixEpoch::now());
613
614            debug!(
615                ?removed_fallback_key,
616                "The fallback key either expired or we didn't have one: generated a new fallback key.",
617            );
618        }
619    }
620
621    /// Check if our most recent fallback key has expired.
622    ///
623    /// We consider the fallback key to be expired if it's older than a week.
624    /// This is the lower bound for the recommended signed pre-key bundle
625    /// rotation interval in the X3DH spec[1].
626    ///
627    /// [1]: https://signal.org/docs/specifications/x3dh/#publishing-keys
628    fn fallback_key_expired(&self) -> bool {
629        const FALLBACK_KEY_MAX_AGE: Duration = Duration::from_secs(3600 * 24 * 7);
630
631        if let Some(time) = self.fallback_creation_timestamp {
632            // `to_system_time()` returns `None` if the the UNIX_EPOCH + `time` doesn't fit
633            // into a i64. This will likely never happen, but let's rotate the
634            // key in case the values are messed up for some other reason.
635            let Some(system_time) = time.to_system_time() else {
636                return true;
637            };
638
639            // `elapsed()` errors if the `system_time` is in the future, this should mean
640            // that our clock has changed to the past, let's rotate just in case
641            // and then we'll get to a normal time.
642            let Ok(elapsed) = system_time.elapsed() else {
643                return true;
644            };
645
646            // Alright, our times are normal and we know how much time elapsed since the
647            // last time we created/rotated a fallback key.
648            //
649            // If the key is older than a week, then we rotate it.
650            elapsed > FALLBACK_KEY_MAX_AGE
651        } else {
652            // We never created a fallback key, or we're migrating to the time-based
653            // fallback key rotation, so let's generate a new fallback key.
654            true
655        }
656    }
657
658    fn fallback_key(&self) -> HashMap<KeyId, Curve25519PublicKey> {
659        self.inner.fallback_key()
660    }
661
662    /// Get a tuple of device, one-time, and fallback keys that need to be
663    /// uploaded.
664    ///
665    /// If no keys need to be uploaded the `DeviceKeys` will be `None` and the
666    /// one-time and fallback keys maps will be empty.
667    pub fn keys_for_upload(&self) -> (Option<DeviceKeys>, OneTimeKeys, FallbackKeys) {
668        let device_keys = self.shared().not().then(|| self.device_keys());
669
670        let one_time_keys = self.signed_one_time_keys();
671        let fallback_keys = self.signed_fallback_keys();
672
673        (device_keys, one_time_keys, fallback_keys)
674    }
675
676    /// Mark the current set of one-time keys as being published.
677    pub fn mark_keys_as_published(&mut self) {
678        self.inner.mark_keys_as_published();
679    }
680
681    /// Sign the given string using the accounts signing key.
682    ///
683    /// Returns the signature as a base64 encoded string.
684    pub fn sign(&self, string: &str) -> Ed25519Signature {
685        self.inner.sign(string)
686    }
687
688    /// Get a serializable version of the `Account` so it can be persisted.
689    pub fn pickle(&self) -> PickledAccount {
690        let pickle = self.inner.pickle();
691
692        PickledAccount {
693            user_id: self.user_id().to_owned(),
694            device_id: self.device_id().to_owned(),
695            pickle,
696            shared: self.shared(),
697            dehydrated: self.static_data.dehydrated,
698            uploaded_signed_key_count: self.uploaded_key_count(),
699            creation_local_time: self.static_data.creation_local_time,
700            fallback_key_creation_timestamp: self.fallback_creation_timestamp,
701        }
702    }
703
704    pub(crate) fn dehydrate(&self, pickle_key: &[u8; 32]) -> Raw<DehydratedDeviceData> {
705        let dehydration_result = self
706            .inner
707            .to_dehydrated_device(pickle_key)
708            .expect("We should be able to convert a freshly created Account into a libolm pickle");
709
710        let data = DehydratedDeviceData::V2(DehydratedDeviceV2::new(
711            dehydration_result.ciphertext,
712            dehydration_result.nonce,
713        ));
714        Raw::from_json(to_raw_value(&data).expect("Couldn't serialize our dehydrated device data"))
715    }
716
717    pub(crate) fn rehydrate(
718        pickle_key: &[u8; 32],
719        user_id: &UserId,
720        device_id: &DeviceId,
721        device_data: Raw<DehydratedDeviceData>,
722    ) -> Result<Self, DehydrationError> {
723        let data = device_data.deserialize()?;
724
725        match data {
726            DehydratedDeviceData::V1(d) => {
727                let pickle_key = expand_legacy_pickle_key(pickle_key, device_id);
728                let account =
729                    InnerAccount::from_libolm_pickle(&d.device_pickle, pickle_key.as_ref())?;
730                Ok(Self::new_helper(account, user_id, device_id))
731            }
732            DehydratedDeviceData::V2(d) => {
733                let account =
734                    InnerAccount::from_dehydrated_device(&d.device_pickle, &d.nonce, pickle_key)?;
735                Ok(Self::new_helper(account, user_id, device_id))
736            }
737            _ => Err(DehydrationError::Json(serde_json::Error::custom(format!(
738                "Unsupported dehydrated device algorithm {:?}",
739                data.algorithm()
740            )))),
741        }
742    }
743
744    /// Produce a dehydrated device using a format described in an older version
745    /// of MSC3814.
746    #[cfg(test)]
747    pub(crate) fn legacy_dehydrate(&self, pickle_key: &[u8; 32]) -> Raw<DehydratedDeviceData> {
748        let pickle_key = expand_legacy_pickle_key(pickle_key, &self.device_id);
749        let device_pickle = self
750            .inner
751            .to_libolm_pickle(pickle_key.as_ref())
752            .expect("We should be able to convert a freshly created Account into a libolm pickle");
753
754        let data = DehydratedDeviceData::V1(DehydratedDeviceV1::new(device_pickle));
755        Raw::from_json(to_raw_value(&data).expect("Couldn't serialize our dehydrated device data"))
756    }
757
758    /// Restore an account from a previously pickled one.
759    ///
760    /// # Arguments
761    ///
762    /// * `pickle` - The pickled version of the Account.
763    ///
764    /// * `pickle_mode` - The mode that was used to pickle the account, either
765    ///   an unencrypted mode or an encrypted using passphrase.
766    pub fn from_pickle(pickle: PickledAccount) -> Result<Self, PickleError> {
767        let account: vodozemac::olm::Account = pickle.pickle.into();
768        let identity_keys = account.identity_keys();
769
770        Ok(Self {
771            static_data: StaticAccountData {
772                user_id: (*pickle.user_id).into(),
773                device_id: (*pickle.device_id).into(),
774                identity_keys: Arc::new(identity_keys),
775                dehydrated: pickle.dehydrated,
776                creation_local_time: pickle.creation_local_time,
777            },
778            inner: Box::new(account),
779            shared: pickle.shared,
780            uploaded_signed_key_count: pickle.uploaded_signed_key_count,
781            fallback_creation_timestamp: pickle.fallback_key_creation_timestamp,
782        })
783    }
784
785    /// Sign the device keys of the account and return them so they can be
786    /// uploaded.
787    pub fn device_keys(&self) -> DeviceKeys {
788        let mut device_keys = self.unsigned_device_keys();
789
790        // Create a copy of the device keys containing only fields that will
791        // get signed.
792        let json_device_keys =
793            to_canonical_value(&device_keys).expect("device key is always safe to serialize");
794        let signature = self
795            .sign_json(json_device_keys)
796            .expect("Newly created device keys can always be signed");
797
798        device_keys.signatures.add_signature(
799            self.user_id().to_owned(),
800            DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, &self.static_data.device_id),
801            signature,
802        );
803
804        device_keys
805    }
806
807    /// Bootstraps cross-signing, generating new cross-signing keys and creating
808    /// the necessary upload and signature requests.
809    ///
810    /// # Returns
811    /// A tuple containing:
812    /// - [`PrivateCrossSigningIdentity`]: The newly-generated cross-signing
813    ///   identity (including a signature from this device).
814    /// - [`UploadSigningKeysRequest`]: The request to upload the
815    ///   newly-generated cross-signing keys to the server.
816    /// - [`SignatureUploadRequest`]: The request to upload the signature of
817    ///   this device to the server.
818    pub async fn bootstrap_cross_signing(
819        &self,
820    ) -> (PrivateCrossSigningIdentity, UploadSigningKeysRequest, SignatureUploadRequest) {
821        let identity = PrivateCrossSigningIdentity::for_account(self);
822
823        let signature_request = identity
824            .sign_account(self.static_data())
825            .await
826            .expect("Can't sign own device with new cross signing keys");
827
828        let upload_request = identity.as_upload_request().await;
829
830        (identity, upload_request, signature_request)
831    }
832
833    /// Sign the given CrossSigning Key in place
834    pub fn sign_cross_signing_key(
835        &self,
836        cross_signing_key: &mut CrossSigningKey,
837    ) -> Result<(), SignatureError> {
838        let signature = self.sign_json(to_canonical_value(&cross_signing_key)?)?;
839
840        cross_signing_key.signatures.add_signature(
841            self.user_id().to_owned(),
842            DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, self.device_id()),
843            signature,
844        );
845
846        Ok(())
847    }
848
849    /// Sign the given Master Key
850    pub fn sign_master_key(
851        &self,
852        master_key: &MasterPubkey,
853    ) -> Result<SignatureUploadRequest, SignatureError> {
854        let public_key =
855            master_key.get_first_key().ok_or(SignatureError::MissingSigningKey)?.to_base64().into();
856
857        let mut cross_signing_key: CrossSigningKey = master_key.as_ref().clone();
858        cross_signing_key.signatures.clear();
859        self.sign_cross_signing_key(&mut cross_signing_key)?;
860
861        let mut user_signed_keys = SignedKeys::new();
862        user_signed_keys.add_cross_signing_keys(public_key, cross_signing_key.to_raw());
863
864        let signed_keys = [(self.user_id().to_owned(), user_signed_keys)].into();
865        Ok(SignatureUploadRequest::new(signed_keys))
866    }
867
868    /// Convert a JSON value to the canonical representation and sign the JSON
869    /// string.
870    ///
871    /// # Arguments
872    ///
873    /// * `json` - The value that should be converted into a canonical JSON
874    ///   string.
875    pub fn sign_json(&self, json: CanonicalJsonValue) -> Result<Ed25519Signature, SignatureError> {
876        self.inner.sign_json(json)
877    }
878
879    /// Sign and prepare one-time keys to be uploaded.
880    ///
881    /// If no one-time keys need to be uploaded, returns an empty `BTreeMap`.
882    pub fn signed_one_time_keys(&self) -> OneTimeKeys {
883        let one_time_keys = self.one_time_keys();
884
885        if one_time_keys.is_empty() {
886            BTreeMap::new()
887        } else {
888            self.signed_keys(one_time_keys, false)
889        }
890    }
891
892    /// Sign and prepare fallback keys to be uploaded.
893    ///
894    /// If no fallback keys need to be uploaded returns an empty BTreeMap.
895    pub fn signed_fallback_keys(&self) -> FallbackKeys {
896        let fallback_key = self.fallback_key();
897
898        if fallback_key.is_empty() { BTreeMap::new() } else { self.signed_keys(fallback_key, true) }
899    }
900
901    fn signed_keys(
902        &self,
903        keys: HashMap<KeyId, Curve25519PublicKey>,
904        fallback: bool,
905    ) -> OneTimeKeys {
906        let mut keys_map = BTreeMap::new();
907
908        for (key_id, key) in keys {
909            let signed_key = self.sign_key(key, fallback);
910
911            keys_map.insert(
912                OneTimeKeyId::from_parts(
913                    OneTimeKeyAlgorithm::SignedCurve25519,
914                    key_id.to_base64().as_str().into(),
915                ),
916                signed_key.into_raw(),
917            );
918        }
919
920        keys_map
921    }
922
923    fn sign_key(&self, key: Curve25519PublicKey, fallback: bool) -> SignedKey {
924        let mut key = if fallback {
925            SignedKey::new_fallback(key.to_owned())
926        } else {
927            SignedKey::new(key.to_owned())
928        };
929
930        let signature = self
931            .sign_json(to_canonical_value(&key).expect("Can't serialize a signed key"))
932            .expect("Newly created one-time keys can always be signed");
933
934        key.signatures_mut().add_signature(
935            self.user_id().to_owned(),
936            DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, self.device_id()),
937            signature,
938        );
939
940        key
941    }
942
943    /// Create a new session with another account given a one-time key.
944    ///
945    /// Returns the newly created session or a `OlmSessionError` if creating a
946    /// session failed.
947    ///
948    /// # Arguments
949    ///
950    /// * `config` - The session config that should be used when creating the
951    ///   Session.
952    ///
953    /// * `identity_key` - The other account's identity/curve25519 key.
954    ///
955    /// * `one_time_key` - A signed one-time key that the other account created
956    ///   and shared with us.
957    ///
958    /// * `fallback_used` - Was the one-time key a fallback key.
959    ///
960    /// * `our_device_keys` - Our own `DeviceKeys`, including cross-signing
961    ///   signatures if applicable, for embedding in encrypted messages.
962    pub fn create_outbound_session_helper(
963        &self,
964        config: SessionConfig,
965        identity_key: Curve25519PublicKey,
966        one_time_key: Curve25519PublicKey,
967        fallback_used: bool,
968        our_device_keys: DeviceKeys,
969    ) -> Session {
970        let session = self.inner.create_outbound_session(config, identity_key, one_time_key);
971
972        let now = SecondsSinceUnixEpoch::now();
973        let session_id = session.session_id();
974
975        Session {
976            inner: Arc::new(Mutex::new(session)),
977            session_id: session_id.into(),
978            sender_key: identity_key,
979            our_device_keys,
980            created_using_fallback_key: fallback_used,
981            creation_time: now,
982            last_use_time: now,
983        }
984    }
985
986    #[instrument(
987        skip_all,
988        fields(
989            user_id = ?device.user_id(),
990            device_id = ?device.device_id(),
991            algorithms = ?device.algorithms()
992        )
993    )]
994    fn find_pre_key_bundle(
995        device: &DeviceData,
996        key_map: &OneTimeKeys,
997    ) -> Result<PrekeyBundle, SessionCreationError> {
998        let mut keys = key_map.iter();
999
1000        let first_key = keys.next().ok_or_else(|| {
1001            SessionCreationError::OneTimeKeyMissing(
1002                device.user_id().to_owned(),
1003                device.device_id().into(),
1004            )
1005        })?;
1006
1007        let first_key_id = first_key.0.to_owned();
1008        let first_key = OneTimeKey::deserialize(first_key_id.algorithm(), first_key.1)?;
1009
1010        let result = match first_key {
1011            OneTimeKey::SignedKey(key) => Ok(PrekeyBundle::Olm3DH { key }),
1012        };
1013
1014        trace!(?result, "Finished searching for a valid pre-key bundle");
1015
1016        result
1017    }
1018
1019    /// Create a new session with another account given a one-time key and a
1020    /// device.
1021    ///
1022    /// Returns the newly created session or a `OlmSessionError` if creating a
1023    /// session failed.
1024    ///
1025    /// # Arguments
1026    /// * `device` - The other account's device.
1027    ///
1028    /// * `key_map` - A map from the algorithm and device ID to the one-time key
1029    ///   that the other account created and shared with us.
1030    ///
1031    /// * `our_device_keys` - Our own `DeviceKeys`, including cross-signing
1032    ///   signatures if applicable, for embedding in encrypted messages.
1033    #[allow(clippy::result_large_err)]
1034    pub fn create_outbound_session(
1035        &self,
1036        device: &DeviceData,
1037        key_map: &OneTimeKeys,
1038        our_device_keys: DeviceKeys,
1039    ) -> Result<Session, SessionCreationError> {
1040        let pre_key_bundle = Self::find_pre_key_bundle(device, key_map)?;
1041
1042        match pre_key_bundle {
1043            PrekeyBundle::Olm3DH { key } => {
1044                device.verify_one_time_key(&key).map_err(|error| {
1045                    SessionCreationError::InvalidSignature {
1046                        signing_key: device.ed25519_key().map(Box::new),
1047                        one_time_key: key.clone().into(),
1048                        error: error.into(),
1049                    }
1050                })?;
1051
1052                let identity_key = device.curve25519_key().ok_or_else(|| {
1053                    SessionCreationError::DeviceMissingCurveKey(
1054                        device.user_id().to_owned(),
1055                        device.device_id().into(),
1056                    )
1057                })?;
1058
1059                let is_fallback = key.fallback();
1060                let one_time_key = key.key();
1061                let config = device.olm_session_config();
1062
1063                Ok(self.create_outbound_session_helper(
1064                    config,
1065                    identity_key,
1066                    one_time_key,
1067                    is_fallback,
1068                    our_device_keys,
1069                ))
1070            }
1071        }
1072    }
1073
1074    /// Create a new session with another account given a pre-key Olm message.
1075    ///
1076    /// Returns the newly created session or a `OlmSessionError` if creating a
1077    /// session failed.
1078    ///
1079    /// # Arguments
1080    ///
1081    /// * `their_identity_key` - The other account's identity/curve25519 key.
1082    ///
1083    /// * `our_device_keys` - Our own `DeviceKeys`, including cross-signing
1084    ///   signatures if applicable, for embedding in encrypted messages.
1085    ///
1086    /// * `message` - A pre-key Olm message that was sent to us by the other
1087    ///   account.
1088    pub fn create_inbound_session(
1089        &mut self,
1090        their_identity_key: Curve25519PublicKey,
1091        our_device_keys: DeviceKeys,
1092        message: &PreKeyMessage,
1093    ) -> Result<InboundCreationResult, SessionCreationError> {
1094        Span::current().record("session_id", debug(message.session_id()));
1095        trace!("Creating a new Olm session from a pre-key message");
1096
1097        let result = self.inner.create_inbound_session(their_identity_key, message)?;
1098        let now = SecondsSinceUnixEpoch::now();
1099        let session_id = result.session.session_id();
1100
1101        debug!(session=?result.session, "Decrypted an Olm message from a new Olm session");
1102
1103        let session = Session {
1104            inner: Arc::new(Mutex::new(result.session)),
1105            session_id: session_id.into(),
1106            sender_key: their_identity_key,
1107            our_device_keys,
1108            created_using_fallback_key: false,
1109            creation_time: now,
1110            last_use_time: now,
1111        };
1112
1113        let plaintext = String::from_utf8_lossy(&result.plaintext).to_string();
1114
1115        Ok(InboundCreationResult { session, plaintext })
1116    }
1117
1118    #[cfg(any(test, feature = "testing"))]
1119    #[allow(dead_code)]
1120    /// Testing only helper to create a session for the given Account
1121    pub async fn create_session_for_test_helper(
1122        &mut self,
1123        other: &mut Account,
1124    ) -> (Session, Session) {
1125        use ruma::events::dummy::ToDeviceDummyEventContent;
1126
1127        other.generate_one_time_keys(1);
1128        let one_time_map = other.signed_one_time_keys();
1129        let device = DeviceData::from_account(other);
1130
1131        let mut our_session =
1132            self.create_outbound_session(&device, &one_time_map, self.device_keys()).unwrap();
1133
1134        other.mark_keys_as_published();
1135
1136        let message = our_session
1137            .encrypt(&device, "m.dummy", ToDeviceDummyEventContent::new(), None)
1138            .await
1139            .unwrap()
1140            .deserialize()
1141            .unwrap();
1142
1143        #[cfg(feature = "experimental-algorithms")]
1144        let content = if let ToDeviceEncryptedEventContent::OlmV2Curve25519AesSha2(c) = message {
1145            c
1146        } else {
1147            panic!("Invalid encrypted event algorithm {}", message.algorithm());
1148        };
1149
1150        #[cfg(not(feature = "experimental-algorithms"))]
1151        let ToDeviceEncryptedEventContent::OlmV1Curve25519AesSha2(content) = message else {
1152            panic!("Invalid encrypted event algorithm {}", message.algorithm());
1153        };
1154
1155        let OlmMessage::PreKey(prekey) = content.ciphertext else {
1156            panic!("Wrong Olm message type");
1157        };
1158
1159        let our_device = DeviceData::from_account(self);
1160        let other_session = other
1161            .create_inbound_session(
1162                our_device.curve25519_key().unwrap(),
1163                other.device_keys(),
1164                &prekey,
1165            )
1166            .unwrap();
1167
1168        (our_session, other_session.session)
1169    }
1170
1171    async fn decrypt_olm_helper(
1172        &mut self,
1173        store: &Store,
1174        sender: &UserId,
1175        sender_key: Curve25519PublicKey,
1176        ciphertext: &OlmMessage,
1177        decryption_settings: &DecryptionSettings,
1178    ) -> OlmResult<OlmDecryptionInfo> {
1179        let message_hash = OlmMessageHash::new(sender_key, ciphertext);
1180
1181        match self
1182            .decrypt_and_parse_olm_message(
1183                store,
1184                sender,
1185                sender_key,
1186                ciphertext,
1187                decryption_settings,
1188            )
1189            .await
1190        {
1191            Ok((session, result)) => {
1192                Ok(OlmDecryptionInfo { session, message_hash, result, inbound_group_session: None })
1193            }
1194            Err(OlmError::SessionWedged(user_id, sender_key)) => {
1195                if store.is_message_known(&message_hash).await? {
1196                    info!(?sender_key, "An Olm message got replayed, decryption failed");
1197                    Err(OlmError::ReplayedMessage(user_id, sender_key))
1198                } else {
1199                    Err(OlmError::SessionWedged(user_id, sender_key))
1200                }
1201            }
1202            Err(e) => Err(e),
1203        }
1204    }
1205
1206    #[cfg(feature = "experimental-algorithms")]
1207    async fn decrypt_olm_v2(
1208        &mut self,
1209        store: &Store,
1210        sender: &UserId,
1211        content: &OlmV2Curve25519AesSha2Content,
1212        decryption_settings: &DecryptionSettings,
1213    ) -> OlmResult<OlmDecryptionInfo> {
1214        self.decrypt_olm_helper(
1215            store,
1216            sender,
1217            content.sender_key,
1218            &content.ciphertext,
1219            decryption_settings,
1220        )
1221        .await
1222    }
1223
1224    #[instrument(skip_all, fields(sender, sender_key = ?content.sender_key))]
1225    async fn decrypt_olm_v1(
1226        &mut self,
1227        store: &Store,
1228        sender: &UserId,
1229        content: &OlmV1Curve25519AesSha2Content,
1230        decryption_settings: &DecryptionSettings,
1231    ) -> OlmResult<OlmDecryptionInfo> {
1232        if content.recipient_key != self.static_data.identity_keys.curve25519 {
1233            warn!("Olm event doesn't contain a ciphertext for our key");
1234
1235            Err(EventError::MissingCiphertext.into())
1236        } else {
1237            Box::pin(self.decrypt_olm_helper(
1238                store,
1239                sender,
1240                content.sender_key,
1241                &content.ciphertext,
1242                decryption_settings,
1243            ))
1244            .await
1245        }
1246    }
1247
1248    #[instrument(skip_all, fields(algorithm = ?event.content.algorithm()))]
1249    pub(crate) async fn decrypt_to_device_event(
1250        &mut self,
1251        store: &Store,
1252        event: &EncryptedToDeviceEvent,
1253        decryption_settings: &DecryptionSettings,
1254    ) -> OlmResult<OlmDecryptionInfo> {
1255        trace!("Decrypting a to-device event");
1256
1257        match &event.content {
1258            ToDeviceEncryptedEventContent::OlmV1Curve25519AesSha2(c) => {
1259                self.decrypt_olm_v1(store, &event.sender, c, decryption_settings).await
1260            }
1261            #[cfg(feature = "experimental-algorithms")]
1262            ToDeviceEncryptedEventContent::OlmV2Curve25519AesSha2(c) => {
1263                self.decrypt_olm_v2(store, &event.sender, c, decryption_settings).await
1264            }
1265            ToDeviceEncryptedEventContent::Unknown(_) => {
1266                warn!(
1267                    "Error decrypting an to-device event, unsupported \
1268                    encryption algorithm"
1269                );
1270
1271                Err(EventError::UnsupportedAlgorithm.into())
1272            }
1273        }
1274    }
1275
1276    /// Handles a response to a /keys/upload request.
1277    pub fn receive_keys_upload_response(
1278        &mut self,
1279        response: &upload_keys::v3::Response,
1280    ) -> OlmResult<()> {
1281        if !self.shared() {
1282            debug!("Marking account as shared");
1283        }
1284        self.mark_as_shared();
1285
1286        debug!("Marking one-time keys as published");
1287        // First mark the current keys as published, as updating the key counts might
1288        // generate some new keys if we're still below the limit.
1289        self.mark_keys_as_published();
1290        self.update_key_counts(&response.one_time_key_counts, None);
1291
1292        Ok(())
1293    }
1294
1295    /// Try to decrypt an olm message, creating a new session if necessary.
1296    async fn decrypt_olm_message(
1297        &mut self,
1298        store: &Store,
1299        sender: &UserId,
1300        sender_key: Curve25519PublicKey,
1301        message: &OlmMessage,
1302    ) -> Result<(SessionType, String), OlmError> {
1303        let existing_sessions = store.get_sessions(&sender_key.to_base64()).await?;
1304
1305        match message {
1306            OlmMessage::Normal(_) => {
1307                let mut errors_by_olm_session = Vec::new();
1308
1309                if let Some(sessions) = existing_sessions {
1310                    // Try to decrypt the message using each Session we share with the
1311                    // given curve25519 sender key.
1312                    for session in sessions.lock().await.iter_mut() {
1313                        match session.decrypt(message).await {
1314                            Ok(p) => {
1315                                // success!
1316                                return Ok((SessionType::Existing(session.clone()), p));
1317                            }
1318
1319                            Err(e) => {
1320                                // An error here is completely normal, after all we don't know
1321                                // which session was used to encrypt a message.
1322                                // We keep hold of the error, so that if *all* sessions fail to
1323                                // decrypt, we can log something useful.
1324                                errors_by_olm_session.push((session.session_id().to_owned(), e));
1325                            }
1326                        }
1327                    }
1328                }
1329
1330                warn!(
1331                    ?errors_by_olm_session,
1332                    "Failed to decrypt a non-pre-key message with all available sessions"
1333                );
1334                Err(OlmError::SessionWedged(sender.to_owned(), sender_key))
1335            }
1336
1337            OlmMessage::PreKey(prekey_message) => {
1338                // First try to decrypt using an existing session.
1339                if let Some(sessions) = existing_sessions {
1340                    for session in sessions.lock().await.iter_mut() {
1341                        if prekey_message.session_id() != session.session_id() {
1342                            // wrong session
1343                            continue;
1344                        }
1345
1346                        if let Ok(p) = session.decrypt(message).await {
1347                            // success!
1348                            return Ok((SessionType::Existing(session.clone()), p));
1349                        }
1350
1351                        // The message was intended for this session, but we weren't able to
1352                        // decrypt it.
1353                        //
1354                        // There's no point trying any other sessions, nor should we try to
1355                        // create a new one since we have already previously created a `Session`
1356                        // with the same keys.
1357                        //
1358                        // (Attempts to create a new session would likely fail anyway since the
1359                        // corresponding one-time key would've been already used up in the
1360                        // previous session creation operation. The one exception where this
1361                        // would not be so is if the fallback key was used for creating the
1362                        // session in lieu of an OTK.)
1363
1364                        warn!(
1365                            session_id = session.session_id(),
1366                            "Failed to decrypt a pre-key message with the corresponding session"
1367                        );
1368
1369                        return Err(OlmError::SessionWedged(
1370                            session.our_device_keys.user_id.to_owned(),
1371                            session.sender_key(),
1372                        ));
1373                    }
1374                }
1375
1376                let device_keys = store.get_own_device().await?.as_device_keys().clone();
1377                let result =
1378                    match self.create_inbound_session(sender_key, device_keys, prekey_message) {
1379                        Ok(r) => r,
1380                        Err(e) => {
1381                            warn!(
1382                                "Failed to create a new Olm session from a pre-key message: {e:?}"
1383                            );
1384                            return Err(OlmError::SessionWedged(sender.to_owned(), sender_key));
1385                        }
1386                    };
1387
1388                // We need to add the new session to the session cache, otherwise
1389                // we might try to create the same session again.
1390                // TODO: separate the session cache from the storage so we only add
1391                // it to the cache but don't store it.
1392                let mut changes =
1393                    Changes { sessions: vec![result.session.clone()], ..Default::default() };
1394
1395                // Any new Olm session will bump the Olm wedging index for the
1396                // sender's device, if we have their device, which will cause us
1397                // to re-send existing Megolm sessions to them the next time we
1398                // use the session.  If we don't have their device, this means
1399                // that we haven't tried to send them any Megolm sessions yet,
1400                // so we don't need to worry about it.
1401                if let Some(device) = store.get_device_from_curve_key(sender, sender_key).await? {
1402                    let mut device_data = device.inner;
1403                    device_data.olm_wedging_index.increment();
1404
1405                    changes.devices =
1406                        DeviceChanges { changed: vec![device_data], ..Default::default() };
1407                }
1408
1409                store.save_changes(changes).await?;
1410
1411                Ok((SessionType::New(result.session), result.plaintext))
1412            }
1413        }
1414    }
1415
1416    /// Decrypt an Olm message, creating a new Olm session if necessary, and
1417    /// parse the result.
1418    #[instrument(skip(self, store), fields(session, session_id))]
1419    async fn decrypt_and_parse_olm_message(
1420        &mut self,
1421        store: &Store,
1422        sender: &UserId,
1423        sender_key: Curve25519PublicKey,
1424        message: &OlmMessage,
1425        decryption_settings: &DecryptionSettings,
1426    ) -> OlmResult<(SessionType, DecryptionResult)> {
1427        let (session, plaintext) =
1428            self.decrypt_olm_message(store, sender, sender_key, message).await?;
1429
1430        trace!("Successfully decrypted an Olm message");
1431
1432        match self
1433            .parse_decrypted_to_device_event(
1434                store,
1435                sender,
1436                sender_key,
1437                plaintext,
1438                decryption_settings,
1439            )
1440            .await
1441        {
1442            Ok(result) => Ok((session, result)),
1443            Err(e) => {
1444                // We might have created a new session but decryption might still
1445                // have failed, store it for the error case here, this is fine
1446                // since we don't expect this to happen often or at all.
1447                match session {
1448                    SessionType::New(s) | SessionType::Existing(s) => {
1449                        store.save_sessions(&[s]).await?;
1450                    }
1451                }
1452
1453                warn!(
1454                    error = ?e,
1455                    "A to-device message was successfully decrypted but \
1456                    parsing and checking the event fields failed"
1457                );
1458
1459                Err(e)
1460            }
1461        }
1462    }
1463
1464    /// Parse the decrypted plaintext as JSON and verify that it wasn't
1465    /// forwarded by a third party.
1466    ///
1467    /// These checks are mandated by the spec[1]:
1468    ///
1469    /// > Other properties are included in order to prevent an attacker from
1470    /// > publishing someone else's Curve25519 keys as their own and
1471    /// > subsequently claiming to have sent messages which they didn't.
1472    /// > sender must correspond to the user who sent the event, recipient to
1473    /// > the local user, and recipient_keys to the local Ed25519 key.
1474    ///
1475    /// # Arguments
1476    ///
1477    /// * `sender` -  The `sender` field from the top level of the received
1478    ///   event.
1479    /// * `sender_key` - The `sender_key` from the cleartext `content` of the
1480    ///   received event (which should also have been used to find or establish
1481    ///   the Olm session that was used to decrypt the event -- so it is
1482    ///   guaranteed to be correct).
1483    /// * `plaintext` - The decrypted content of the event.
1484    async fn parse_decrypted_to_device_event(
1485        &self,
1486        store: &Store,
1487        sender: &UserId,
1488        sender_key: Curve25519PublicKey,
1489        plaintext: String,
1490        decryption_settings: &DecryptionSettings,
1491    ) -> OlmResult<DecryptionResult> {
1492        let event: Box<AnyDecryptedOlmEvent> = serde_json::from_str(&plaintext)?;
1493        let identity_keys = &self.static_data.identity_keys;
1494
1495        if event.recipient() != self.static_data.user_id {
1496            Err(EventError::MismatchedSender(
1497                event.recipient().to_owned(),
1498                self.static_data.user_id.clone(),
1499            )
1500            .into())
1501        }
1502        // Check that the `sender` in the decrypted to-device event matches that at the
1503        // top level of the encrypted event.
1504        else if event.sender() != sender {
1505            Err(EventError::MismatchedSender(event.sender().to_owned(), sender.to_owned()).into())
1506        } else if identity_keys.ed25519 != event.recipient_keys().ed25519 {
1507            Err(EventError::MismatchedKeys(
1508                identity_keys.ed25519.into(),
1509                event.recipient_keys().ed25519.into(),
1510            )
1511            .into())
1512        } else {
1513            let sender_device = Self::get_event_sender_device(store, sender_key, &event).await?;
1514            let encryption_info = Self::get_olm_encryption_info(sender_key, sender, &sender_device);
1515
1516            let result = DecryptionResult {
1517                event,
1518                raw_event: Raw::from_json(RawJsonValue::from_string(plaintext)?),
1519                sender_key,
1520                encryption_info,
1521            };
1522
1523            // Return an error if the sender is unverified (and we care)
1524            if !self.is_from_verified_device_or_allowed_type(decryption_settings, &result) {
1525                Err(OlmError::UnverifiedSenderDevice)
1526            } else {
1527                // Sender is ok - return the decrypted event
1528                Ok(result)
1529            }
1530        }
1531    }
1532
1533    /// Look up the [`Device`] that sent us a successfully-decrypted event.
1534    ///
1535    /// We first look for the sender device in our store; if it is found then we
1536    /// return that (having checked that the keys match). If the device is
1537    /// not found in the store, we return the details
1538    /// from `sender_device_keys`, if present. If the device is not in the
1539    /// store, and the event lacks `sender_device_keys`, an error is returned.
1540    ///
1541    /// Also validates the `sender_device_keys` field, if present, regardless of
1542    /// whether it is used.
1543    ///
1544    /// `m.room_key` events are special-cased and return `None`: we look up
1545    /// their devices later on.
1546    async fn get_event_sender_device(
1547        store: &Store,
1548        sender_key: Curve25519PublicKey,
1549        event: &AnyDecryptedOlmEvent,
1550    ) -> OlmResult<Option<Device>> {
1551        // If the event contained sender_device_keys, check them now.
1552        // WARN: If you move or modify this check, ensure that the code below is still
1553        // valid. The processing of the historic room key bundle depends on this being
1554        // here.
1555        let sender_device_keys = Self::check_sender_device_keys(event, sender_key)?;
1556        if let AnyDecryptedOlmEvent::RoomKey(_) = event {
1557            // If this event is an `m.room_key` event, defer the check for
1558            // the Ed25519 key of the sender until we decrypt room events.
1559            // This ensures that we receive the room key even if we don't
1560            // have access to the device.
1561            return Ok(None);
1562        }
1563
1564        // MSC4268 requires room key bundle events to have a `sender_device_keys` field.
1565        // Enforce that now.
1566        if let AnyDecryptedOlmEvent::RoomKeyBundle(_) = event {
1567            sender_device_keys.ok_or(EventError::MissingSigningKey).inspect_err(|_| {
1568                warn!("The room key bundle was missing the sender device keys in the event")
1569            })?;
1570        }
1571
1572        // For event types other than `m.room_key`, we need to look up the device in the
1573        // database irrespective of whether the `sender_device_keys` field is
1574        // present in the event, because it may have been marked as "locally
1575        // trusted" in the database.
1576        let store_device = store.get_device_from_curve_key(event.sender(), sender_key).await?;
1577
1578        match (store_device, sender_device_keys) {
1579            // If the device is in the database, it had better have an Ed25519 key which
1580            // matches that in the event.
1581            (Some(device), _) => {
1582                let key = device.ed25519_key().ok_or(EventError::MissingSigningKey)?;
1583                if key != event.keys().ed25519 {
1584                    return Err(EventError::MismatchedKeys(
1585                        key.into(),
1586                        event.keys().ed25519.into(),
1587                    )
1588                    .into());
1589                }
1590                Ok(Some(device))
1591            }
1592
1593            (None, Some(sender_device_keys)) => {
1594                // We have already validated the signature on `sender_device_keys`, so this
1595                // try_into cannot fail.
1596                let sender_device_data = sender_device_keys.try_into().expect("Conversion of DeviceKeys to DeviceData failed despite the signature already having been checked");
1597                Ok(Some(store.wrap_device_data(sender_device_data).await?))
1598            }
1599
1600            (None, None) => Err(OlmError::EventError(EventError::MissingSigningKey)),
1601        }
1602    }
1603
1604    /// Return true if:
1605    ///
1606    /// * the sending device is verified, or
1607    /// * the event type is one of those we allow to be sent from unverified
1608    ///   devices, or
1609    /// * we are not in "exclude_insecure_devices" mode, so everything is
1610    ///   allowed.
1611    ///
1612    /// Return false if:
1613    ///
1614    /// * we are in "exclude_insecure_devices" mode AND the sending device is
1615    ///   unverified.
1616    fn is_from_verified_device_or_allowed_type(
1617        &self,
1618        decryption_settings: &DecryptionSettings,
1619        result: &DecryptionResult,
1620    ) -> bool {
1621        let event_type = result.event.event_type();
1622
1623        // If we're in "exclude insecure devices" mode, we prevent most
1624        // to-device events with unverified senders from being allowed
1625        // through here, but there are some exceptions:
1626        //
1627        // * m.room_key - we hold on to these until later, so if the sender becomes
1628        //   verified later we can still use the key.
1629        //
1630        // * m.room_key_request, m.room_key.withheld, m.key.verification.*,
1631        //   m.secret.request - these are allowed as plaintext events, so we also allow
1632        //   them encrypted from insecure devices. Note: the list of allowed types here
1633        //   should match with what is allowed in handle_to_device_event.
1634        match event_type {
1635            "m.room_key"
1636            | "m.room_key.withheld"
1637            | "m.room_key_request"
1638            | "m.secret.request"
1639            | "m.key.verification.key"
1640            | "m.key.verification.mac"
1641            | "m.key.verification.done"
1642            | "m.key.verification.ready"
1643            | "m.key.verification.start"
1644            | "m.key.verification.accept"
1645            | "m.key.verification.cancel"
1646            | "m.key.verification.request" => {
1647                // This is one of the exception types - we allow it even if the sender device is
1648                // not verified.
1649                true
1650            }
1651            _ => {
1652                // This is not an exception type - check for "exclude insecure devices" mode,
1653                // and whether the sender is verified.
1654                satisfies_sender_trust_requirement(
1655                    &result.encryption_info,
1656                    &decryption_settings.sender_device_trust_requirement,
1657                )
1658            }
1659        }
1660    }
1661
1662    /// Gets the EncryptionInfo for a successfully decrypted to-device message
1663    /// that have passed the mismatched sender_key/user_id validation.
1664    ///
1665    /// `sender_device` is optional because for some to-device messages we defer
1666    /// the check for the ed25519 key, in that case the
1667    /// `verification_state` will have a `MissingDevice` link problem.
1668    fn get_olm_encryption_info(
1669        sender_key: Curve25519PublicKey,
1670        sender_id: &UserId,
1671        sender_device: &Option<Device>,
1672    ) -> EncryptionInfo {
1673        let verification_state = sender_device
1674            .as_ref()
1675            .map(|device| {
1676                if device.is_verified() {
1677                    // The device is locally verified or signed by a verified user
1678                    VerificationState::Verified
1679                } else if device.is_cross_signed_by_owner() {
1680                    // The device is not verified, but it is signed by its owner
1681                    if device
1682                        .device_owner_identity
1683                        .as_ref()
1684                        .expect("A device cross-signed by the owner must have an owner identity")
1685                        .was_previously_verified()
1686                    {
1687                        VerificationState::Unverified(VerificationLevel::VerificationViolation)
1688                    } else {
1689                        VerificationState::Unverified(VerificationLevel::UnverifiedIdentity)
1690                    }
1691                } else {
1692                    // No identity or not signed
1693                    VerificationState::Unverified(VerificationLevel::UnsignedDevice)
1694                }
1695            })
1696            .unwrap_or(VerificationState::Unverified(VerificationLevel::None(
1697                DeviceLinkProblem::MissingDevice,
1698            )));
1699
1700        EncryptionInfo {
1701            sender: sender_id.to_owned(),
1702            sender_device: sender_device.as_ref().map(|d| d.device_id().to_owned()),
1703            forwarder: None,
1704            algorithm_info: AlgorithmInfo::OlmV1Curve25519AesSha2 {
1705                curve25519_public_key_base64: sender_key.to_base64(),
1706            },
1707            verification_state,
1708        }
1709    }
1710
1711    /// If the plaintext of the decrypted message includes a
1712    /// `sender_device_keys` property per [MSC4147], check that it is valid.
1713    ///
1714    /// In particular, we check that:
1715    ///
1716    ///  * The Curve25519 key in the `sender_device_keys` matches that used to
1717    ///    establish the Olm session that was used to decrypt the event.
1718    ///
1719    ///  * The `sender_device_keys` contains a valid self-signature by the
1720    ///    Ed25519 key in the device data.
1721    ///
1722    ///  * The Ed25519 key in the device data matches that in the `keys` field
1723    ///    in the event, for consistency and sanity.
1724    ///
1725    /// The first two checks are sufficient to bind together the Ed25519 and
1726    /// Curve25519 keys:
1727    ///
1728    ///  * Only the holder of the secret part of the Curve25519 key that was
1729    ///    used to construct the Olm session (the 'owner' of that key) can
1730    ///    encrypt the device data in that Olm session. By including the Ed25519
1731    ///    key in the device data, the owner of the Curve25519 key is claiming
1732    ///    ownership of the Ed25519 key.
1733    ///
1734    ///  * Only the owner of the Ed25519 key can construct the self-signature on
1735    ///    the device data. By including the Curve25519 key in the device data
1736    ///    and then signing it, the owner of the Ed25519 key is claiming
1737    ///    ownership of the Curve25519 key.
1738    ///
1739    ///  * Since we now have claims in both directions, the two key owners must
1740    ///    either be the same entity, or working in sufficiently close
1741    ///    collaboration that they can be treated as such.
1742    ///
1743    /// # Arguments
1744    ///
1745    /// * `event` - The decrypted and deserialized plaintext of the event.
1746    /// * `sender_key` - The Curve25519 key that the sender used to establish
1747    ///   the Olm session that was used to decrypt the event.
1748    ///
1749    /// # Returns
1750    ///
1751    /// A reference to the `sender_device_keys` in the event, if it exists and
1752    /// is valid.
1753    ///
1754    /// [MSC4147]: https://github.com/matrix-org/matrix-spec-proposals/pull/4147
1755    fn check_sender_device_keys(
1756        event: &AnyDecryptedOlmEvent,
1757        sender_key: Curve25519PublicKey,
1758    ) -> OlmResult<Option<&DeviceKeys>> {
1759        let Some(sender_device_keys) = event.sender_device_keys() else {
1760            return Ok(None);
1761        };
1762
1763        // Check the signature within the device_keys structure
1764        sender_device_keys.check_self_signature().map_err(|err| {
1765            warn!(
1766                "Received a to-device message with sender_device_keys with \
1767                 invalid signature: {err:?}",
1768            );
1769            OlmError::EventError(EventError::InvalidSenderDeviceKeys)
1770        })?;
1771
1772        // Check that the Ed25519 key in the sender_device_keys matches the `ed25519`
1773        // key in the `keys` field in the event.
1774        if sender_device_keys.ed25519_key() != Some(event.keys().ed25519) {
1775            warn!(
1776                "Received a to-device message with sender_device_keys with incorrect \
1777                 ed25519 key: expected {:?}, got {:?}",
1778                event.keys().ed25519,
1779                sender_device_keys.ed25519_key(),
1780            );
1781            return Err(OlmError::EventError(EventError::InvalidSenderDeviceKeys));
1782        }
1783
1784        // Check that the Curve25519 key in the sender_device_keys matches the key that
1785        // was used for the Olm session.
1786        if sender_device_keys.curve25519_key() != Some(sender_key) {
1787            warn!(
1788                "Received a to-device message with sender_device_keys with incorrect \
1789                 curve25519 key: expected {sender_key:?}, got {:?}",
1790                sender_device_keys.curve25519_key(),
1791            );
1792            return Err(OlmError::EventError(EventError::InvalidSenderDeviceKeys));
1793        }
1794
1795        Ok(Some(sender_device_keys))
1796    }
1797
1798    /// Internal use only.
1799    ///
1800    /// Cloning should only be done for testing purposes or when we are certain
1801    /// that we don't want the inner state to be shared.
1802    #[doc(hidden)]
1803    pub fn deep_clone(&self) -> Self {
1804        // `vodozemac::Account` isn't really cloneable, but... Don't tell anyone.
1805        Self::from_pickle(self.pickle()).unwrap()
1806    }
1807}
1808
1809impl PartialEq for Account {
1810    fn eq(&self, other: &Self) -> bool {
1811        self.identity_keys() == other.identity_keys() && self.shared() == other.shared()
1812    }
1813}
1814
1815/// Calculate the shared history flag from the history visibility as defined in
1816/// [MSC3061]
1817///
1818/// The MSC defines that the shared history flag should be set to true when the
1819/// history visibility setting is set to `shared` or `world_readable`:
1820///
1821/// > A room key is flagged as having been used for shared history when it was
1822/// > used to encrypt a message while the room's history visibility setting
1823/// > was set to world_readable or shared.
1824///
1825/// In all other cases, even if we encounter a custom history visibility, we
1826/// should return false:
1827///
1828/// > If the client does not have an m.room.history_visibility state event for
1829/// > the room, or its value is not understood, the client should treat it as if
1830/// > its value is joined for the purposes of determining whether the key is
1831/// > used for shared history.
1832///
1833/// [MSC3061]: https://github.com/matrix-org/matrix-spec-proposals/pull/3061
1834pub(crate) fn shared_history_from_history_visibility(
1835    history_visibility: &HistoryVisibility,
1836) -> bool {
1837    match history_visibility {
1838        HistoryVisibility::Shared | HistoryVisibility::WorldReadable => true,
1839        HistoryVisibility::Invited | HistoryVisibility::Joined | _ => false,
1840    }
1841}
1842
1843/// Expand the pickle key for an older version of dehydrated devices
1844///
1845/// The `org.matrix.msc3814.v1.olm` variant of dehydrated devices used the
1846/// libolm Account pickle format for the dehydrated device. The libolm pickle
1847/// encryption scheme uses HKDF to deterministically expand an input key
1848/// material, usually 32 bytes, into a AES key, MAC key, and the initialization
1849/// vector (IV).
1850///
1851/// This means that the same input key material will always end up producing the
1852/// same AES key, and IV.
1853///
1854/// This encryption scheme is used in the Olm double ratchet and was designed to
1855/// minimize the size of the ciphertext. As a tradeof, it requires a unique
1856/// input key material for each plaintext that gets encrypted, otherwise IV
1857/// reuse happens.
1858///
1859/// To combat the IV reuse, we're going to create a per-dehydrated-device unique
1860/// pickle key by expanding the key itself with the device ID used as the salt.
1861fn expand_legacy_pickle_key(key: &[u8; 32], device_id: &DeviceId) -> Box<[u8; 32]> {
1862    let kdf: Hkdf<Sha256> = Hkdf::new(Some(device_id.as_bytes()), key);
1863    let mut key = Box::new([0u8; 32]);
1864
1865    kdf.expand(b"dehydrated-device-pickle-key", key.as_mut_slice())
1866        .expect("We should be able to expand the 32 byte pickle key");
1867
1868    key
1869}
1870
1871/// Does the to-device event satisfy the sender trust requirement from the
1872/// decryption settings?
1873fn satisfies_sender_trust_requirement(
1874    encryption_info: &EncryptionInfo,
1875    trust_requirement: &TrustRequirement,
1876) -> bool {
1877    trace!(
1878        verification_state = ?encryption_info.verification_state,
1879        ?trust_requirement, "check_to_device_sender_trust_requirement",
1880    );
1881
1882    match (&encryption_info.verification_state, trust_requirement) {
1883        // If we don't care, everything is OK.
1884        (_, TrustRequirement::Untrusted) => true,
1885
1886        // Verified is OK whatever our requirements are.
1887        (VerificationState::Verified, _) => true,
1888
1889        // We do care, and we are not fully verified: check more deeply.
1890        // (Note that for to-device messages the legacy trust requirement is not relevant.)
1891        (
1892            VerificationState::Unverified(verification_level),
1893            TrustRequirement::CrossSignedOrLegacy | TrustRequirement::CrossSigned,
1894        ) => match verification_level {
1895            // The device is signed but the identity is only pinned - this is fine.
1896            VerificationLevel::UnverifiedIdentity => true,
1897
1898            // The device is unsigned or missing, or the user is in verification violation,
1899            // or the sender is mismatched: this is not fine.
1900            VerificationLevel::UnsignedDevice
1901            | VerificationLevel::None(_)
1902            | VerificationLevel::VerificationViolation
1903            | VerificationLevel::MismatchedSender => false,
1904        },
1905    }
1906}
1907
1908#[cfg(test)]
1909mod tests {
1910    use std::{
1911        collections::{BTreeMap, BTreeSet},
1912        ops::Deref,
1913        time::Duration,
1914    };
1915
1916    use anyhow::Result;
1917    use matrix_sdk_test::async_test;
1918    use ruma::{
1919        DeviceId, MilliSecondsSinceUnixEpoch, OneTimeKeyAlgorithm, OneTimeKeyId, UserId, device_id,
1920        events::room::history_visibility::HistoryVisibility, room_id, user_id,
1921    };
1922    use serde_json::json;
1923
1924    use super::Account;
1925    use crate::{
1926        DeviceData, EncryptionSettings,
1927        olm::{SignedJsonObject, account::shared_history_from_history_visibility},
1928        types::{DeviceKeys, SignedKey},
1929    };
1930
1931    fn user_id() -> &'static UserId {
1932        user_id!("@alice:localhost")
1933    }
1934
1935    fn device_id() -> &'static DeviceId {
1936        device_id!("DEVICEID")
1937    }
1938
1939    #[test]
1940    fn test_one_time_key_creation() -> Result<()> {
1941        let mut account = Account::with_device_id(user_id(), device_id());
1942
1943        let (_, one_time_keys, _) = account.keys_for_upload();
1944        assert!(!one_time_keys.is_empty());
1945
1946        let (_, second_one_time_keys, _) = account.keys_for_upload();
1947        assert!(!second_one_time_keys.is_empty());
1948
1949        let one_time_key_ids: BTreeSet<&OneTimeKeyId> =
1950            one_time_keys.keys().map(Deref::deref).collect();
1951        let second_one_time_key_ids: BTreeSet<&OneTimeKeyId> =
1952            second_one_time_keys.keys().map(Deref::deref).collect();
1953
1954        assert_eq!(one_time_key_ids, second_one_time_key_ids);
1955
1956        account.mark_keys_as_published();
1957        account.update_uploaded_key_count(50);
1958        account.generate_one_time_keys_if_needed();
1959
1960        let (_, third_one_time_keys, _) = account.keys_for_upload();
1961        assert!(third_one_time_keys.is_empty());
1962
1963        account.update_uploaded_key_count(0);
1964        account.generate_one_time_keys_if_needed();
1965
1966        let (_, fourth_one_time_keys, _) = account.keys_for_upload();
1967        assert!(!fourth_one_time_keys.is_empty());
1968
1969        let fourth_one_time_key_ids: BTreeSet<&OneTimeKeyId> =
1970            fourth_one_time_keys.keys().map(Deref::deref).collect();
1971
1972        assert_ne!(one_time_key_ids, fourth_one_time_key_ids);
1973        Ok(())
1974    }
1975
1976    #[test]
1977    fn test_fallback_key_creation() -> Result<()> {
1978        let mut account = Account::with_device_id(user_id(), device_id());
1979
1980        let (_, _, fallback_keys) = account.keys_for_upload();
1981
1982        // We don't create fallback keys since we don't know if the server
1983        // supports them, we need to receive a sync response to decide if we're
1984        // going to create them or not.
1985        assert!(
1986            fallback_keys.is_empty(),
1987            "We should not upload fallback keys until we know if the server supports them."
1988        );
1989
1990        let one_time_keys = BTreeMap::from([(OneTimeKeyAlgorithm::SignedCurve25519, 50u8.into())]);
1991
1992        // A `None` here means that the server doesn't support fallback keys, no
1993        // fallback key gets uploaded.
1994        account.update_key_counts(&one_time_keys, None);
1995        let (_, _, fallback_keys) = account.keys_for_upload();
1996        assert!(
1997            fallback_keys.is_empty(),
1998            "We should not upload a fallback key if we're certain that the server doesn't support \
1999             them."
2000        );
2001
2002        // The empty array means that the server supports fallback keys but
2003        // there isn't a unused fallback key on the server. This time we upload
2004        // a fallback key.
2005        let unused_fallback_keys = &[];
2006        account.update_key_counts(&one_time_keys, Some(unused_fallback_keys.as_ref()));
2007        let (_, _, fallback_keys) = account.keys_for_upload();
2008        assert!(
2009            !fallback_keys.is_empty(),
2010            "We should upload the initial fallback key if the server supports them."
2011        );
2012        account.mark_keys_as_published();
2013
2014        // There's no unused fallback key on the server, but our initial fallback key
2015        // did not yet expire.
2016        let unused_fallback_keys = &[];
2017        account.update_key_counts(&one_time_keys, Some(unused_fallback_keys.as_ref()));
2018        let (_, _, fallback_keys) = account.keys_for_upload();
2019        assert!(
2020            fallback_keys.is_empty(),
2021            "We should not upload new fallback keys unless our current fallback key expires."
2022        );
2023
2024        let fallback_key_timestamp =
2025            account.fallback_creation_timestamp.unwrap().to_system_time().unwrap()
2026                - Duration::from_secs(3600 * 24 * 30);
2027
2028        account.fallback_creation_timestamp =
2029            Some(MilliSecondsSinceUnixEpoch::from_system_time(fallback_key_timestamp).unwrap());
2030
2031        account.update_key_counts(&one_time_keys, None);
2032        let (_, _, fallback_keys) = account.keys_for_upload();
2033        assert!(
2034            !fallback_keys.is_empty(),
2035            "Now that our fallback key has expired, we should try to upload a new one, even if the \
2036             server supposedly doesn't support fallback keys anymore"
2037        );
2038
2039        Ok(())
2040    }
2041
2042    #[test]
2043    fn test_fallback_key_signing() -> Result<()> {
2044        let key = vodozemac::Curve25519PublicKey::from_base64(
2045            "7PUPP6Ijt5R8qLwK2c8uK5hqCNF9tOzWYgGaAay5JBs",
2046        )?;
2047        let account = Account::with_device_id(user_id(), device_id());
2048
2049        let key = account.sign_key(key, true);
2050
2051        let canonical_key = key.to_canonical_json()?;
2052
2053        assert_eq!(
2054            canonical_key,
2055            "{\"fallback\":true,\"key\":\"7PUPP6Ijt5R8qLwK2c8uK5hqCNF9tOzWYgGaAay5JBs\"}"
2056        );
2057
2058        account
2059            .has_signed_raw(key.signatures(), &canonical_key)
2060            .expect("Couldn't verify signature");
2061
2062        let device = DeviceData::from_account(&account);
2063        device.verify_one_time_key(&key).expect("The device can verify its own signature");
2064
2065        Ok(())
2066    }
2067
2068    #[test]
2069    fn test_account_and_device_creation_timestamp() -> Result<()> {
2070        let now = MilliSecondsSinceUnixEpoch::now();
2071        let account = Account::with_device_id(user_id(), device_id());
2072        let then = MilliSecondsSinceUnixEpoch::now();
2073
2074        assert!(account.creation_local_time() >= now);
2075        assert!(account.creation_local_time() <= then);
2076
2077        let device = DeviceData::from_account(&account);
2078        assert_eq!(account.creation_local_time(), device.first_time_seen_ts());
2079
2080        Ok(())
2081    }
2082
2083    #[async_test]
2084    async fn test_fallback_key_signature_verification() -> Result<()> {
2085        let fallback_key = json!({
2086            "fallback": true,
2087            "key": "XPFqtLvBepBmW6jSAbBuJbhEpprBhQOX1IjUu+cnMF4",
2088            "signatures": {
2089                "@dkasak_c:matrix.org": {
2090                    "ed25519:EXPDYDPWZH": "RJCBMJPL5hvjxgq8rmLmqkNOuPsaan7JeL1wsE+gW6R39G894lb2sBmzapHeKCn/KFjmkonPLkICApRDS+zyDw"
2091                }
2092            }
2093        });
2094
2095        let device_keys = json!({
2096            "algorithms": [
2097                "m.olm.v1.curve25519-aes-sha2",
2098                "m.megolm.v1.aes-sha2"
2099            ],
2100            "device_id": "EXPDYDPWZH",
2101            "keys": {
2102                "curve25519:EXPDYDPWZH": "k7f3igo0Vrdm88JSSA5d3OCuUfHYELChB2b57aOROB8",
2103                "ed25519:EXPDYDPWZH": "GdjYI8fxs175gSpYRJkyN6FRfvcyTsNOhJ2OR/Ggp+E"
2104            },
2105            "signatures": {
2106                "@dkasak_c:matrix.org": {
2107                    "ed25519:EXPDYDPWZH": "kzrtfQMbJXWXQ1uzhybtwFnGk0JJBS4Mg8VPMusMu6U8MPJccwoHVZKo5+owuHTzIodI+GZYqLmMSzvfvsChAA"
2108                }
2109            },
2110            "user_id": "@dkasak_c:matrix.org",
2111            "unsigned": {}
2112        });
2113
2114        let device_keys: DeviceKeys = serde_json::from_value(device_keys).unwrap();
2115        let device = DeviceData::try_from(&device_keys).unwrap();
2116        let fallback_key: SignedKey = serde_json::from_value(fallback_key).unwrap();
2117
2118        device
2119            .verify_one_time_key(&fallback_key)
2120            .expect("The fallback key should pass the signature verification");
2121
2122        Ok(())
2123    }
2124
2125    #[test]
2126    fn test_shared_history_flag_from_history_visibility() {
2127        assert!(
2128            shared_history_from_history_visibility(&HistoryVisibility::WorldReadable),
2129            "The world readable visibility should set the shared history flag to true"
2130        );
2131
2132        assert!(
2133            shared_history_from_history_visibility(&HistoryVisibility::Shared),
2134            "The shared visibility should set the shared history flag to true"
2135        );
2136
2137        assert!(
2138            !shared_history_from_history_visibility(&HistoryVisibility::Joined),
2139            "The joined visibility should set the shared history flag to false"
2140        );
2141
2142        assert!(
2143            !shared_history_from_history_visibility(&HistoryVisibility::Invited),
2144            "The invited visibility should set the shared history flag to false"
2145        );
2146
2147        let visibility = HistoryVisibility::from("custom_visibility");
2148        assert!(
2149            !shared_history_from_history_visibility(&visibility),
2150            "A custom visibility should set the shared history flag to false"
2151        );
2152    }
2153
2154    #[async_test]
2155    async fn test_shared_history_set_when_creating_group_sessions() {
2156        let account = Account::new(user_id());
2157        let room_id = room_id!("!room:id");
2158        let settings = EncryptionSettings {
2159            history_visibility: HistoryVisibility::Shared,
2160            ..Default::default()
2161        };
2162
2163        let (_, session) = account
2164            .create_group_session_pair(room_id, settings, Default::default())
2165            .await
2166            .expect("We should be able to create a group session pair");
2167
2168        assert!(
2169            session.shared_history(),
2170            "The shared history flag should have been set when we created the new session"
2171        );
2172    }
2173}