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