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