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    secret_storage::{DecodeError, MacError, SecretStorageKey},
67    CryptoStoreError, SecretImportError,
68};
69use ruma::events::{
70    secret_storage::{
71        default_key::SecretStorageDefaultKeyEventContent, key::SecretStorageKeyEventContent,
72    },
73    EventContentFromType, GlobalAccountDataEventType,
74};
75use serde_json::value::to_raw_value;
76use thiserror::Error;
77
78use super::identities::ManualVerifyError;
79use crate::Client;
80
81mod futures;
82mod secret_store;
83
84pub use futures::CreateStore;
85pub use secret_store::SecretStore;
86
87/// Convenicence type alias for the secret-storage specific results.
88pub type Result<T, E = SecretStorageError> = std::result::Result<T, E>;
89
90/// Error type for the secret-storage subsystem.
91#[derive(Debug, Error)]
92pub enum SecretStorageError {
93    /// A typical SDK error.
94    #[error(transparent)]
95    Sdk(#[from] crate::Error),
96
97    /// Error when deserializing account data events.
98    #[error(transparent)]
99    Json(#[from] serde_json::Error),
100
101    /// The secret storage key could not have been decoded or verified
102    /// successfully.
103    #[error(transparent)]
104    SecretStorageKey(#[from] DecodeError),
105
106    /// The secret store could not be opened because info about the
107    /// secret-storage key could not have been found in the account data of
108    /// the user.
109    #[error(
110        "The info about the secret key could not have been found in the account data of the user"
111    )]
112    MissingKeyInfo {
113        /// The key ID of the default key. Will be set to the key ID in the
114        /// `m.secret_storage.default_key` event. If the
115        /// `m.secret_storage.default_key` does not exits, will be
116        /// `None`.
117        key_id: Option<String>,
118    },
119
120    /// A secret could not have been imported from the secret store into the
121    /// local store.
122    #[error(transparent)]
123    SecretImportError(#[from] SecretImportError),
124
125    /// A general storage error.
126    #[error(transparent)]
127    Storage(#[from] CryptoStoreError),
128
129    /// An error happened while trying to mark our own device as verified after
130    /// the private cross-signing keys have been imported.
131    #[error(transparent)]
132    Verification(#[from] ManualVerifyError),
133
134    /// Error describing a decryption failure of a secret.
135    #[error(transparent)]
136    Decryption(#[from] DecryptionError),
137}
138
139/// Error type describing decryption failures of the secret-storage system.
140#[derive(Debug, Error)]
141pub enum DecryptionError {
142    /// The secret could not have been decrypted.
143    #[error("Could not decrypt the secret using the secret storage key, invalid MAC.")]
144    Mac(#[from] MacError),
145
146    /// Could not decode the secret, the secret is not valid UTF-8.
147    #[error("Could not decode the secret, the secret is not valid UTF-8")]
148    Utf8(#[from] FromUtf8Error),
149}
150
151/// A high-level API to manage secret storage.
152///
153/// To get this, use [`Client::encryption()::secret_storage()`].
154#[derive(Debug)]
155pub struct SecretStorage {
156    pub(super) client: Client,
157}
158
159impl SecretStorage {
160    /// Open the [`SecretStore`] with the given `key`.
161    ///
162    /// The `secret_storage_key` can be a passphrase or a Base58 encoded secret
163    /// storage key.
164    ///
165    /// # Examples
166    ///
167    /// ```no_run
168    /// # use matrix_sdk::Client;
169    /// # use url::Url;
170    /// # async {
171    /// # let homeserver = Url::parse("http://example.com")?;
172    /// # let client = Client::new(homeserver).await?;
173    /// use ruma::events::secret::request::SecretName;
174    ///
175    /// let secret_store = client
176    ///     .encryption()
177    ///     .secret_storage()
178    ///     .open_secret_store("It's a secret to everybody")
179    ///     .await?;
180    ///
181    /// let my_secret = "Top secret secret";
182    /// let my_secret_name = "m.treasure";
183    ///
184    /// secret_store.put_secret(my_secret_name, my_secret);
185    ///
186    /// # anyhow::Ok(()) };
187    /// ```
188    pub async fn open_secret_store(&self, secret_storage_key: &str) -> Result<SecretStore> {
189        let maybe_default_key_id = self
190            .client
191            .account()
192            .fetch_account_data(GlobalAccountDataEventType::SecretStorageDefaultKey)
193            .await?;
194
195        if let Some(default_key_id) = maybe_default_key_id {
196            let default_key_id =
197                default_key_id.deserialize_as::<SecretStorageDefaultKeyEventContent>()?;
198
199            let event_type =
200                GlobalAccountDataEventType::SecretStorageKey(default_key_id.key_id.to_owned());
201            let secret_key =
202                self.client.account().fetch_account_data(event_type.to_owned()).await?;
203
204            if let Some(secret_key_content) = secret_key {
205                let event_type = event_type.to_string();
206                let secret_key_content = to_raw_value(&secret_key_content)?;
207
208                let secret_key_content =
209                    SecretStorageKeyEventContent::from_parts(&event_type, &secret_key_content)?;
210
211                let key =
212                    SecretStorageKey::from_account_data(secret_storage_key, secret_key_content)?;
213
214                Ok(SecretStore { client: self.client.to_owned(), key })
215            } else {
216                Err(SecretStorageError::MissingKeyInfo { key_id: Some(default_key_id.key_id) })
217            }
218        } else {
219            Err(SecretStorageError::MissingKeyInfo { key_id: None })
220        }
221    }
222
223    /// Create a new [`SecretStore`].
224    ///
225    /// The [`SecretStore`] will be protected by a randomly generated key, or
226    /// optionally a passphrase can be provided as well.
227    ///
228    /// In both cases, whether a passphrase was provided or not, the key to open
229    /// the [`SecretStore`] can be obtained using the
230    /// [`SecretStore::secret_storage_key()`] method.
231    ///
232    /// *Note*: This method will set the new secret storage key as the default
233    /// key in the `m.secret_storage.default_key` event. All the known secrets
234    /// will be re-encrypted and uploaded to the homeserver as well. This
235    /// includes the following secrets:
236    ///
237    /// - `m.cross_signing.master`: The master cross-signing key.
238    /// - `m.cross_signing.self_signing`: The self-signing cross-signing key.
239    /// - `m.cross_signing.user_signing`: The user-signing cross-signing key.
240    ///
241    /// # Examples
242    ///
243    /// ```no_run
244    /// # use matrix_sdk::Client;
245    /// # use url::Url;
246    /// # async {
247    /// # let homeserver = Url::parse("http://example.com")?;
248    /// # let client = Client::new(homeserver).await?;
249    /// use ruma::events::secret::request::SecretName;
250    ///
251    /// let secret_store = client
252    ///     .encryption()
253    ///     .secret_storage()
254    ///     .create_secret_store()
255    ///     .await?;
256    ///
257    /// let my_secret = "Top secret secret";
258    /// let my_secret_name = SecretName::from("m.treasure");
259    ///
260    /// secret_store.put_secret(my_secret_name, my_secret);
261    ///
262    /// let secret_storage_key = secret_store.secret_storage_key();
263    ///
264    /// println!("Your secret storage key is {secret_storage_key}, save it somewhere safe.");
265    ///
266    /// # anyhow::Ok(()) };
267    /// ```
268    pub fn create_secret_store(&self) -> CreateStore<'_> {
269        CreateStore { secret_storage: self, passphrase: None }
270    }
271
272    /// Run a network request to find if secret storage is set up for this user.
273    pub async fn is_enabled(&self) -> crate::Result<bool> {
274        if let Some(content) = self
275            .client
276            .account()
277            .fetch_account_data(GlobalAccountDataEventType::SecretStorageDefaultKey)
278            .await?
279        {
280            // Since we can't delete account data events, we're going to treat
281            // deserialization failures as secret storage being disabled.
282            Ok(content.deserialize_as::<SecretStorageDefaultKeyEventContent>().is_ok())
283        } else {
284            // No account data event found, must be disabled.
285            Ok(false)
286        }
287    }
288}