matrix_sdk_crypto/store/
types.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
15//! Data types for persistent storage.
16//!
17//! This module defines the data structures used by the crypto store to
18//! represent objects that are persisted in the database.
19
20use std::{
21    collections::{BTreeMap, HashMap},
22    time::Duration,
23};
24
25use ruma::{OwnedDeviceId, OwnedRoomId, OwnedUserId};
26use serde::{Deserialize, Serialize};
27use vodozemac::{base64_encode, Curve25519PublicKey};
28use zeroize::{Zeroize, ZeroizeOnDrop};
29
30use super::{DehydrationError, GossipRequest};
31use crate::{
32    olm::{
33        InboundGroupSession, OlmMessageHash, OutboundGroupSession, PrivateCrossSigningIdentity,
34        SenderData,
35    },
36    types::{
37        events::{room_key_bundle::RoomKeyBundleContent, room_key_withheld::RoomKeyWithheldEvent},
38        EventEncryptionAlgorithm,
39    },
40    Account, Device, DeviceData, GossippedSecret, Session, UserIdentity, UserIdentityData,
41};
42
43/// Aggregated changes to be saved in the database.
44///
45/// This is an update version of `Changes` that will replace it as #2624
46/// progresses.
47// If you ever add a field here, make sure to update `Changes::is_empty` too.
48#[derive(Default, Debug)]
49#[allow(missing_docs)]
50pub struct PendingChanges {
51    pub account: Option<Account>,
52}
53
54impl PendingChanges {
55    /// Are there any changes stored or is this an empty `Changes` struct?
56    pub fn is_empty(&self) -> bool {
57        self.account.is_none()
58    }
59}
60
61/// Aggregated changes to be saved in the database.
62// If you ever add a field here, make sure to update `Changes::is_empty` too.
63#[derive(Default, Debug)]
64#[allow(missing_docs)]
65pub struct Changes {
66    pub private_identity: Option<PrivateCrossSigningIdentity>,
67    pub backup_version: Option<String>,
68    pub backup_decryption_key: Option<BackupDecryptionKey>,
69    pub dehydrated_device_pickle_key: Option<DehydratedDeviceKey>,
70    pub sessions: Vec<Session>,
71    pub message_hashes: Vec<OlmMessageHash>,
72    pub inbound_group_sessions: Vec<InboundGroupSession>,
73    pub outbound_group_sessions: Vec<OutboundGroupSession>,
74    pub key_requests: Vec<GossipRequest>,
75    pub identities: IdentityChanges,
76    pub devices: DeviceChanges,
77    /// Stores when a `m.room_key.withheld` is received
78    pub withheld_session_info: BTreeMap<OwnedRoomId, BTreeMap<String, RoomKeyWithheldEvent>>,
79    pub room_settings: HashMap<OwnedRoomId, RoomSettings>,
80    pub secrets: Vec<GossippedSecret>,
81    pub next_batch_token: Option<String>,
82
83    /// Historical room key history bundles that we have received and should
84    /// store.
85    pub received_room_key_bundles: Vec<StoredRoomKeyBundleData>,
86}
87
88/// Information about an [MSC4268] room key bundle.
89///
90/// [MSC4268]: https://github.com/matrix-org/matrix-spec-proposals/pull/4268
91#[derive(Clone, Debug, Serialize, Deserialize)]
92pub struct StoredRoomKeyBundleData {
93    /// The user that sent us this data.
94    pub sender_user: OwnedUserId,
95
96    /// The [`Curve25519PublicKey`] of the device that sent us this data.
97    pub sender_key: Curve25519PublicKey,
98
99    /// Information about the sender of this data and how much we trust that
100    /// information.
101    pub sender_data: SenderData,
102
103    /// The room key bundle data itself.
104    pub bundle_data: RoomKeyBundleContent,
105}
106
107/// A user for which we are tracking the list of devices.
108#[derive(Clone, Debug, Serialize, Deserialize)]
109pub struct TrackedUser {
110    /// The user ID of the user.
111    pub user_id: OwnedUserId,
112    /// The outdate/dirty flag of the user, remembers if the list of devices for
113    /// the user is considered to be out of date. If the list of devices is
114    /// out of date, a `/keys/query` request should be sent out for this
115    /// user.
116    pub dirty: bool,
117}
118
119impl Changes {
120    /// Are there any changes stored or is this an empty `Changes` struct?
121    pub fn is_empty(&self) -> bool {
122        self.private_identity.is_none()
123            && self.backup_version.is_none()
124            && self.backup_decryption_key.is_none()
125            && self.dehydrated_device_pickle_key.is_none()
126            && self.sessions.is_empty()
127            && self.message_hashes.is_empty()
128            && self.inbound_group_sessions.is_empty()
129            && self.outbound_group_sessions.is_empty()
130            && self.key_requests.is_empty()
131            && self.identities.is_empty()
132            && self.devices.is_empty()
133            && self.withheld_session_info.is_empty()
134            && self.room_settings.is_empty()
135            && self.secrets.is_empty()
136            && self.next_batch_token.is_none()
137            && self.received_room_key_bundles.is_empty()
138    }
139}
140
141/// This struct is used to remember whether an identity has undergone a change
142/// or remains the same as the one we already know about.
143///
144/// When the homeserver informs us of a potential change in a user's identity or
145/// device during a `/sync` response, it triggers a `/keys/query` request from
146/// our side. In response to this query, the server provides a comprehensive
147/// snapshot of all the user's devices and identities.
148///
149/// Our responsibility is to discern whether a device or identity is new,
150/// changed, or unchanged.
151#[derive(Debug, Clone, Default)]
152#[allow(missing_docs)]
153pub struct IdentityChanges {
154    pub new: Vec<UserIdentityData>,
155    pub changed: Vec<UserIdentityData>,
156    pub unchanged: Vec<UserIdentityData>,
157}
158
159impl IdentityChanges {
160    pub(super) fn is_empty(&self) -> bool {
161        self.new.is_empty() && self.changed.is_empty()
162    }
163
164    /// Convert the vectors contained in the [`IdentityChanges`] into
165    /// three maps from user id to user identity (new, updated, unchanged).
166    pub(super) fn into_maps(
167        self,
168    ) -> (
169        BTreeMap<OwnedUserId, UserIdentityData>,
170        BTreeMap<OwnedUserId, UserIdentityData>,
171        BTreeMap<OwnedUserId, UserIdentityData>,
172    ) {
173        let new: BTreeMap<_, _> = self
174            .new
175            .into_iter()
176            .map(|identity| (identity.user_id().to_owned(), identity))
177            .collect();
178
179        let changed: BTreeMap<_, _> = self
180            .changed
181            .into_iter()
182            .map(|identity| (identity.user_id().to_owned(), identity))
183            .collect();
184
185        let unchanged: BTreeMap<_, _> = self
186            .unchanged
187            .into_iter()
188            .map(|identity| (identity.user_id().to_owned(), identity))
189            .collect();
190
191        (new, changed, unchanged)
192    }
193}
194
195#[derive(Debug, Clone, Default)]
196#[allow(missing_docs)]
197pub struct DeviceChanges {
198    pub new: Vec<DeviceData>,
199    pub changed: Vec<DeviceData>,
200    pub deleted: Vec<DeviceData>,
201}
202
203/// Updates about [`Device`]s which got received over the `/keys/query`
204/// endpoint.
205#[derive(Clone, Debug, Default)]
206pub struct DeviceUpdates {
207    /// The list of newly discovered devices.
208    ///
209    /// A device being in this list does not necessarily mean that the device
210    /// was just created, it just means that it's the first time we're
211    /// seeing this device.
212    pub new: BTreeMap<OwnedUserId, BTreeMap<OwnedDeviceId, Device>>,
213    /// The list of changed devices.
214    pub changed: BTreeMap<OwnedUserId, BTreeMap<OwnedDeviceId, Device>>,
215}
216
217/// Updates about [`UserIdentity`]s which got received over the `/keys/query`
218/// endpoint.
219#[derive(Clone, Debug, Default)]
220pub struct IdentityUpdates {
221    /// The list of newly discovered user identities .
222    ///
223    /// A identity being in this list does not necessarily mean that the
224    /// identity was just created, it just means that it's the first time
225    /// we're seeing this identity.
226    pub new: BTreeMap<OwnedUserId, UserIdentity>,
227    /// The list of changed identities.
228    pub changed: BTreeMap<OwnedUserId, UserIdentity>,
229    /// The list of unchanged identities.
230    pub unchanged: BTreeMap<OwnedUserId, UserIdentity>,
231}
232
233/// The private part of a backup key.
234///
235/// The private part of the key is not used on a regular basis. Rather, it is
236/// used only when we need to *recover* the backup.
237///
238/// Typically, this private key is itself encrypted and stored in server-side
239/// secret storage (SSSS), whence it can be retrieved when it is needed for a
240/// recovery operation. Alternatively, the key can be "gossiped" between devices
241/// via "secret sharing".
242#[derive(Clone, Zeroize, ZeroizeOnDrop, Deserialize, Serialize)]
243#[serde(transparent)]
244pub struct BackupDecryptionKey {
245    pub(crate) inner: Box<[u8; BackupDecryptionKey::KEY_SIZE]>,
246}
247
248impl BackupDecryptionKey {
249    /// The number of bytes the decryption key will hold.
250    pub const KEY_SIZE: usize = 32;
251
252    /// Create a new random decryption key.
253    pub fn new() -> Result<Self, rand::Error> {
254        let mut rng = rand::thread_rng();
255
256        let mut key = Box::new([0u8; Self::KEY_SIZE]);
257        rand::Fill::try_fill(key.as_mut_slice(), &mut rng)?;
258
259        Ok(Self { inner: key })
260    }
261
262    /// Export the [`BackupDecryptionKey`] as a base64 encoded string.
263    pub fn to_base64(&self) -> String {
264        base64_encode(self.inner.as_slice())
265    }
266}
267
268#[cfg(not(tarpaulin_include))]
269impl std::fmt::Debug for BackupDecryptionKey {
270    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
271        f.debug_tuple("BackupDecryptionKey").field(&"...").finish()
272    }
273}
274
275/// The pickle key used to safely store the dehydrated device pickle.
276///
277/// This input key material will be expanded using HKDF into an AES key, MAC
278/// key, and an initialization vector (IV).
279#[derive(Clone, Zeroize, ZeroizeOnDrop, Deserialize, Serialize)]
280#[serde(transparent)]
281pub struct DehydratedDeviceKey {
282    pub(crate) inner: Box<[u8; DehydratedDeviceKey::KEY_SIZE]>,
283}
284
285impl DehydratedDeviceKey {
286    /// The number of bytes the encryption key will hold.
287    pub const KEY_SIZE: usize = 32;
288
289    /// Generates a new random pickle key.
290    pub fn new() -> Result<Self, rand::Error> {
291        let mut rng = rand::thread_rng();
292
293        let mut key = Box::new([0u8; Self::KEY_SIZE]);
294        rand::Fill::try_fill(key.as_mut_slice(), &mut rng)?;
295
296        Ok(Self { inner: key })
297    }
298
299    /// Creates a new dehydration pickle key from the given slice.
300    ///
301    /// Fail if the slice length is not 32.
302    pub fn from_slice(slice: &[u8]) -> Result<Self, DehydrationError> {
303        if slice.len() == 32 {
304            let mut key = Box::new([0u8; 32]);
305            key.copy_from_slice(slice);
306            Ok(DehydratedDeviceKey { inner: key })
307        } else {
308            Err(DehydrationError::PickleKeyLength(slice.len()))
309        }
310    }
311
312    /// Creates a dehydration pickle key from the given bytes.
313    pub fn from_bytes(raw_key: &[u8; 32]) -> Self {
314        let mut inner = Box::new([0u8; Self::KEY_SIZE]);
315        inner.copy_from_slice(raw_key);
316
317        Self { inner }
318    }
319
320    /// Export the [`DehydratedDeviceKey`] as a base64 encoded string.
321    pub fn to_base64(&self) -> String {
322        base64_encode(self.inner.as_slice())
323    }
324}
325
326impl From<&[u8; 32]> for DehydratedDeviceKey {
327    fn from(value: &[u8; 32]) -> Self {
328        DehydratedDeviceKey { inner: Box::new(*value) }
329    }
330}
331
332impl From<DehydratedDeviceKey> for Vec<u8> {
333    fn from(key: DehydratedDeviceKey) -> Self {
334        key.inner.to_vec()
335    }
336}
337
338#[cfg(not(tarpaulin_include))]
339impl std::fmt::Debug for DehydratedDeviceKey {
340    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
341        f.debug_tuple("DehydratedDeviceKey").field(&"...").finish()
342    }
343}
344
345impl DeviceChanges {
346    /// Merge the given `DeviceChanges` into this instance of `DeviceChanges`.
347    pub fn extend(&mut self, other: DeviceChanges) {
348        self.new.extend(other.new);
349        self.changed.extend(other.changed);
350        self.deleted.extend(other.deleted);
351    }
352
353    /// Are there any changes is this an empty [`DeviceChanges`] struct?
354    pub fn is_empty(&self) -> bool {
355        self.new.is_empty() && self.changed.is_empty() && self.deleted.is_empty()
356    }
357}
358
359/// Struct holding info about how many room keys the store has.
360#[derive(Debug, Clone, Default)]
361pub struct RoomKeyCounts {
362    /// The total number of room keys the store has.
363    pub total: usize,
364    /// The number of backed up room keys the store has.
365    pub backed_up: usize,
366}
367
368/// Stored versions of the backup keys.
369#[derive(Default, Clone, Debug)]
370pub struct BackupKeys {
371    /// The key used to decrypt backed up room keys.
372    pub decryption_key: Option<BackupDecryptionKey>,
373    /// The version that we are using for backups.
374    pub backup_version: Option<String>,
375}
376
377/// A struct containing private cross signing keys that can be backed up or
378/// uploaded to the secret store.
379#[derive(Default, Zeroize, ZeroizeOnDrop)]
380pub struct CrossSigningKeyExport {
381    /// The seed of the master key encoded as unpadded base64.
382    pub master_key: Option<String>,
383    /// The seed of the self signing key encoded as unpadded base64.
384    pub self_signing_key: Option<String>,
385    /// The seed of the user signing key encoded as unpadded base64.
386    pub user_signing_key: Option<String>,
387}
388
389#[cfg(not(tarpaulin_include))]
390impl std::fmt::Debug for CrossSigningKeyExport {
391    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
392        f.debug_struct("CrossSigningKeyExport")
393            .field("master_key", &self.master_key.is_some())
394            .field("self_signing_key", &self.self_signing_key.is_some())
395            .field("user_signing_key", &self.user_signing_key.is_some())
396            .finish_non_exhaustive()
397    }
398}
399
400/// Result type telling us if a `/keys/query` response was expected for a given
401/// user.
402#[derive(Clone, Copy, Debug, PartialEq, Eq)]
403pub(crate) enum UserKeyQueryResult {
404    WasPending,
405    WasNotPending,
406
407    /// A query was pending, but we gave up waiting
408    TimeoutExpired,
409}
410
411/// Room encryption settings which are modified by state events or user options
412#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
413pub struct RoomSettings {
414    /// The encryption algorithm that should be used in the room.
415    pub algorithm: EventEncryptionAlgorithm,
416
417    /// Whether state event encryption is enabled.
418    #[cfg(feature = "experimental-encrypted-state-events")]
419    #[serde(default)]
420    pub encrypt_state_events: bool,
421
422    /// Should untrusted devices receive the room key, or should they be
423    /// excluded from the conversation.
424    pub only_allow_trusted_devices: bool,
425
426    /// The maximum time an encryption session should be used for, before it is
427    /// rotated.
428    pub session_rotation_period: Option<Duration>,
429
430    /// The maximum number of messages an encryption session should be used for,
431    /// before it is rotated.
432    pub session_rotation_period_messages: Option<usize>,
433}
434
435impl Default for RoomSettings {
436    fn default() -> Self {
437        Self {
438            algorithm: EventEncryptionAlgorithm::MegolmV1AesSha2,
439            #[cfg(feature = "experimental-encrypted-state-events")]
440            encrypt_state_events: false,
441            only_allow_trusted_devices: false,
442            session_rotation_period: None,
443            session_rotation_period_messages: None,
444        }
445    }
446}
447
448/// Information on a room key that has been received or imported.
449#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
450pub struct RoomKeyInfo {
451    /// The [messaging algorithm] that this key is used for. Will be one of the
452    /// `m.megolm.*` algorithms.
453    ///
454    /// [messaging algorithm]: https://spec.matrix.org/v1.6/client-server-api/#messaging-algorithms
455    pub algorithm: EventEncryptionAlgorithm,
456
457    /// The room where the key is used.
458    pub room_id: OwnedRoomId,
459
460    /// The Curve25519 key of the device which initiated the session originally.
461    pub sender_key: Curve25519PublicKey,
462
463    /// The ID of the session that the key is for.
464    pub session_id: String,
465}
466
467impl From<&InboundGroupSession> for RoomKeyInfo {
468    fn from(group_session: &InboundGroupSession) -> Self {
469        RoomKeyInfo {
470            algorithm: group_session.algorithm().clone(),
471            room_id: group_session.room_id().to_owned(),
472            sender_key: group_session.sender_key(),
473            session_id: group_session.session_id().to_owned(),
474        }
475    }
476}
477
478/// Information on a room key that has been withheld
479#[derive(Clone, Debug, Deserialize, Serialize)]
480pub struct RoomKeyWithheldInfo {
481    /// The room where the key is used.
482    pub room_id: OwnedRoomId,
483
484    /// The ID of the session that the key is for.
485    pub session_id: String,
486
487    /// The `m.room_key.withheld` event that notified us that the key is being
488    /// withheld.
489    pub withheld_event: RoomKeyWithheldEvent,
490}
491
492/// Information about a received historic room key bundle.
493///
494/// This struct contains information needed to uniquely identify a room key
495/// bundle. Only a single bundle per sender for a given room is persisted at a
496/// time.
497///
498/// It is used to notify listeners about received room key bundles.
499#[derive(Debug, Clone)]
500pub struct RoomKeyBundleInfo {
501    /// The user ID of the person that sent us the historic room key bundle.
502    pub sender: OwnedUserId,
503
504    /// The [`Curve25519PublicKey`] of the device that sent us this data.
505    pub sender_key: Curve25519PublicKey,
506
507    /// The ID of the room the bundle should be used in.
508    pub room_id: OwnedRoomId,
509}
510
511impl From<&StoredRoomKeyBundleData> for RoomKeyBundleInfo {
512    fn from(value: &StoredRoomKeyBundleData) -> Self {
513        let StoredRoomKeyBundleData { sender_user, sender_data: _, bundle_data, sender_key } =
514            value;
515        let sender_key = *sender_key;
516
517        Self { sender: sender_user.clone(), room_id: bundle_data.room_id.clone(), sender_key }
518    }
519}