Skip to main content

matrix_sdk/encryption/secret_storage/
mod.rs

1// Copyright 2023 The Matrix.org Foundation C.I.C.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Secret Storage Support
16//!
17//! This submodule provides essential functionality for secret storage in
18//! compliance with the [Matrix protocol specification][spec].
19//!
20//! Secret storage is a critical component that provides an encrypted
21//! key/value storage system. It leverages [account data] events stored on the
22//! Matrix homeserver to ensure secure and private storage of sensitive
23//! information.
24//!
25//! For detailed information and usage guidelines, refer to the documentation of
26//! the [`SecretStore`] struct.
27//!
28//! # Examples
29//!
30//! ```no_run
31//! # use matrix_sdk::Client;
32//! # use url::Url;
33//! # async {
34//! # let homeserver = Url::parse("http://example.com")?;
35//! # let client = Client::new(homeserver).await?;
36//! use ruma::events::secret::request::SecretName;
37//!
38//! // Open the store.
39//! let secret_store = client
40//!     .encryption()
41//!     .secret_storage()
42//!     .open_secret_store("It's a secret to everybody")
43//!     .await?;
44//!
45//! // Import the secrets.
46//! secret_store.import_secrets().await?;
47//!
48//! // Our own device should now be verified.
49//! let device = client
50//!     .encryption()
51//!     .get_own_device()
52//!     .await?
53//!     .expect("We should be able to retrieve our own device");
54//!
55//! assert!(device.is_cross_signed_by_owner());
56//!
57//! # anyhow::Ok(()) };
58//! ```
59//!
60//! [spec]: https://spec.matrix.org/v1.8/client-server-api/#secret-storage
61//! [account data]: https://spec.matrix.org/v1.8/client-server-api/#client-config
62
63use std::string::FromUtf8Error;
64
65use matrix_sdk_base::crypto::{
66    CryptoStoreError, SecretImportError,
67    secret_storage::{DecodeError, MacError, SecretStorageKey},
68};
69use ruma::{
70    events::{
71        EventContentFromType, GlobalAccountDataEventType,
72        secret::request::SecretName,
73        secret_storage::{
74            default_key::SecretStorageDefaultKeyEventContent, key::SecretStorageKeyEventContent,
75        },
76    },
77    serde::Raw,
78};
79use serde_json::value::to_raw_value;
80use thiserror::Error;
81
82use super::identities::ManualVerifyError;
83use crate::Client;
84
85mod futures;
86mod secret_store;
87
88pub use futures::CreateStore;
89pub use secret_store::SecretStore;
90
91/// Convenience type alias for the secret-storage specific results.
92pub type Result<T, E = SecretStorageError> = std::result::Result<T, E>;
93
94/// Error type for errors when importing a secret from secret storage.
95#[derive(Debug, Error)]
96pub enum ImportError {
97    /// A typical SDK error.
98    #[error(transparent)]
99    Sdk(#[from] crate::Error),
100
101    /// Error when deserializing account data events.
102    #[error(transparent)]
103    Json(#[from] serde_json::Error),
104
105    /// The key that we tried to import was invalid.
106    #[error(transparent)]
107    Key(vodozemac::KeyError),
108
109    /// The public key of the imported private key doesn't match the public key
110    /// that was uploaded to the server.
111    #[error(
112        "The public key of the imported private key doesn't match the public\
113            key that was uploaded to the server"
114    )]
115    MismatchedPublicKeys,
116
117    /// Error describing a decryption failure of a secret.
118    #[error(transparent)]
119    Decryption(#[from] DecryptionError),
120}
121
122/// Error type for the secret-storage subsystem.
123#[derive(Debug, Error)]
124pub enum SecretStorageError {
125    /// A typical SDK error.
126    #[error(transparent)]
127    Sdk(#[from] crate::Error),
128
129    /// Error when deserializing account data events.
130    #[error(transparent)]
131    Json(#[from] serde_json::Error),
132
133    /// The secret storage key could not have been decoded or verified
134    /// successfully.
135    #[error(transparent)]
136    SecretStorageKey(#[from] DecodeError),
137
138    /// The secret store could not be opened because info about the
139    /// secret-storage key could not have been found in the account data of the
140    /// user.
141    #[error(
142        "The info about the secret key could not have been found in the account data of the user"
143    )]
144    MissingKeyInfo {
145        /// The key ID of the default key. Will be set to the key ID in the
146        /// `m.secret_storage.default_key` event. If the
147        /// `m.secret_storage.default_key` does not exits, will be `None`.
148        key_id: Option<String>,
149    },
150
151    /// An error when importing from the secret store into the local store.
152    #[error("Error while importing {name}: {error}")]
153    ImportError {
154        /// The name of the secret that was being imported when the error
155        /// occurred.
156        name: SecretName,
157        /// The error that occurred.
158        error: ImportError,
159    },
160
161    /// A general storage error.
162    #[error(transparent)]
163    Storage(#[from] CryptoStoreError),
164
165    /// An error happened while trying to mark our own device as verified after
166    /// the private cross-signing keys have been imported.
167    #[error(transparent)]
168    Verification(#[from] ManualVerifyError),
169
170    /// Error describing a decryption failure of a secret.
171    #[error(transparent)]
172    Decryption(#[from] DecryptionError),
173
174    /// The private decryption key we found does not match the public key for
175    /// the enabled backup.
176    #[error("The backup decryption key does not match the latest backup version")]
177    InconsistentBackupDecryptionKey,
178
179    /// The private backup decryption key is missing, even though backups are
180    /// enabled.
181    #[error("The backup decryption key is missing")]
182    MissingOrInvalidBackupDecryptionKey,
183}
184
185impl SecretStorageError {
186    /// Create a `SecretStorageError::ImportError` from a secret name and any
187    /// error that can be converted directly into an `ImportError`
188    fn into_import_error(secret_name: SecretName, error: impl Into<ImportError>) -> Self {
189        SecretStorageError::ImportError { name: secret_name, error: error.into() }
190    }
191
192    /// Create a `SecretStorageError` from a `SecretImportError`
193    ///
194    /// `SecretImportError::Key` and `SecretImportError::MismatchedPublicKeys`
195    /// become `SecretStorageError::ImportError`s, whereas
196    /// `SecretImportError::Store` becomes `SecretStorageError::Storage` since
197    /// the error is with the crypto storage rather than in importing the
198    /// secret.
199    fn from_secret_import_error(error: SecretImportError) -> Self {
200        match error {
201            SecretImportError::Key { name, error } => {
202                SecretStorageError::ImportError { name, error: ImportError::Key(error) }
203            }
204            SecretImportError::MismatchedPublicKeys { name } => {
205                SecretStorageError::ImportError { name, error: ImportError::MismatchedPublicKeys }
206            }
207            SecretImportError::Store(error) => SecretStorageError::Storage(error),
208        }
209    }
210}
211
212/// Error type describing decryption failures of the secret-storage system.
213#[derive(Debug, Error)]
214pub enum DecryptionError {
215    /// The secret could not have been decrypted.
216    #[error("Could not decrypt the secret using the secret storage key, invalid MAC.")]
217    Mac(#[from] MacError),
218
219    /// Could not decode the secret, the secret is not valid UTF-8.
220    #[error("Could not decode the secret, the secret is not valid UTF-8")]
221    Utf8(#[from] FromUtf8Error),
222}
223
224/// A high-level API to manage secret storage.
225///
226/// To get this, use
227/// [`client.encryption().secret_storage()`](super::Encryption::secret_storage).
228#[derive(Debug)]
229pub struct SecretStorage {
230    pub(super) client: Client,
231}
232
233impl SecretStorage {
234    /// Open the [`SecretStore`] with the given `key`.
235    ///
236    /// The `secret_storage_key` can be a passphrase or a Base58 encoded secret
237    /// storage key.
238    ///
239    /// # Examples
240    ///
241    /// ```no_run
242    /// # use matrix_sdk::Client;
243    /// # use url::Url;
244    /// # async {
245    /// # let homeserver = Url::parse("http://example.com")?;
246    /// # let client = Client::new(homeserver).await?;
247    /// use ruma::events::secret::request::SecretName;
248    ///
249    /// let secret_store = client
250    ///     .encryption()
251    ///     .secret_storage()
252    ///     .open_secret_store("It's a secret to everybody")
253    ///     .await?;
254    ///
255    /// let my_secret = "Top secret secret";
256    /// let my_secret_name = "m.treasure";
257    ///
258    /// secret_store.put_secret(my_secret_name, my_secret);
259    ///
260    /// # anyhow::Ok(()) };
261    /// ```
262    pub async fn open_secret_store(&self, secret_storage_key: &str) -> Result<SecretStore> {
263        let maybe_default_key_id = self.fetch_default_key_id().await?;
264
265        if let Some(default_key_id) = maybe_default_key_id {
266            let default_key_id = default_key_id.deserialize()?;
267
268            let event_type =
269                GlobalAccountDataEventType::SecretStorageKey(default_key_id.key_id.to_owned());
270            let secret_key =
271                self.client.account().fetch_account_data(event_type.to_owned()).await?;
272
273            if let Some(secret_key_content) = secret_key {
274                let event_type = event_type.to_string();
275                let secret_key_content = to_raw_value(&secret_key_content)?;
276
277                let secret_key_content =
278                    SecretStorageKeyEventContent::from_parts(&event_type, &secret_key_content)?;
279
280                let key =
281                    SecretStorageKey::from_account_data(secret_storage_key, secret_key_content)?;
282
283                Ok(SecretStore { client: self.client.to_owned(), key })
284            } else {
285                Err(SecretStorageError::MissingKeyInfo { key_id: Some(default_key_id.key_id) })
286            }
287        } else {
288            Err(SecretStorageError::MissingKeyInfo { key_id: None })
289        }
290    }
291
292    /// Create a new [`SecretStore`].
293    ///
294    /// The [`SecretStore`] will be protected by a randomly generated key, or
295    /// optionally a passphrase can be provided as well.
296    ///
297    /// In both cases, whether a passphrase was provided or not, the key to open
298    /// the [`SecretStore`] can be obtained using the
299    /// [`SecretStore::secret_storage_key()`] method.
300    ///
301    /// _Note_: This method will set the new secret storage key as the default
302    /// key in the `m.secret_storage.default_key` event. All the known secrets
303    /// will be re-encrypted and uploaded to the homeserver as well. This
304    /// includes the following secrets:
305    ///
306    /// - `m.cross_signing.master`: The master cross-signing key.
307    /// - `m.cross_signing.self_signing`: The self-signing cross-signing key.
308    /// - `m.cross_signing.user_signing`: The user-signing cross-signing key.
309    ///
310    /// # Examples
311    ///
312    /// ```no_run
313    /// # use matrix_sdk::Client;
314    /// # use url::Url;
315    /// # async {
316    /// # let homeserver = Url::parse("http://example.com")?;
317    /// # let client = Client::new(homeserver).await?;
318    /// use ruma::events::secret::request::SecretName;
319    ///
320    /// let secret_store = client
321    ///     .encryption()
322    ///     .secret_storage()
323    ///     .create_secret_store()
324    ///     .await?;
325    ///
326    /// let my_secret = "Top secret secret";
327    /// let my_secret_name = SecretName::from("m.treasure");
328    ///
329    /// secret_store.put_secret(my_secret_name, my_secret);
330    ///
331    /// let secret_storage_key = secret_store.secret_storage_key();
332    ///
333    /// println!("Your secret storage key is {secret_storage_key}, save it somewhere safe.");
334    ///
335    /// # anyhow::Ok(()) };
336    /// ```
337    pub fn create_secret_store(&self) -> CreateStore<'_> {
338        CreateStore { secret_storage: self, passphrase: None }
339    }
340
341    /// Run a network request to find if secret storage is set up for this user.
342    pub async fn is_enabled(&self) -> crate::Result<bool> {
343        if let Some(content) = self.fetch_default_key_id().await? {
344            // Since we can't delete account data events, we're going to treat
345            // deserialization failures as secret storage being disabled.
346            Ok(content.deserialize().is_ok())
347        } else {
348            // No account data event found, must be disabled.
349            Ok(false)
350        }
351    }
352
353    /// Fetch the `m.secret_storage.default_key` event from the server.
354    pub async fn fetch_default_key_id(
355        &self,
356    ) -> crate::Result<Option<Raw<SecretStorageDefaultKeyEventContent>>> {
357        self.client
358            .account()
359            .fetch_account_data_static::<SecretStorageDefaultKeyEventContent>()
360            .await
361    }
362}