use std::string::FromUtf8Error;
use matrix_sdk_base::crypto::{
CryptoStoreError, SecretImportError,
secret_storage::{DecodeError, MacError, SecretStorageKey},
};
use ruma::{
events::{
EventContentFromType, GlobalAccountDataEventType,
secret::request::SecretName,
secret_storage::{
default_key::SecretStorageDefaultKeyEventContent, key::SecretStorageKeyEventContent,
},
},
serde::Raw,
};
use serde_json::value::to_raw_value;
use thiserror::Error;
use super::identities::ManualVerifyError;
use crate::Client;
mod futures;
mod secret_store;
pub use futures::CreateStore;
pub use secret_store::SecretStore;
pub type Result<T, E = SecretStorageError> = std::result::Result<T, E>;
#[derive(Debug, Error)]
pub enum ImportError {
#[error(transparent)]
Sdk(#[from] crate::Error),
#[error(transparent)]
Json(#[from] serde_json::Error),
#[error(transparent)]
Key(vodozemac::KeyError),
#[error(
"The public key of the imported private key doesn't match the public\
key that was uploaded to the server"
)]
MismatchedPublicKeys,
#[error(transparent)]
Decryption(#[from] DecryptionError),
}
#[derive(Debug, Error)]
pub enum SecretStorageError {
#[error(transparent)]
Sdk(#[from] crate::Error),
#[error(transparent)]
Json(#[from] serde_json::Error),
#[error(transparent)]
SecretStorageKey(#[from] DecodeError),
#[error(
"The info about the secret key could not have been found in the account data of the user"
)]
MissingKeyInfo {
key_id: Option<String>,
},
#[error("Error while importing {name}: {error}")]
ImportError {
name: SecretName,
error: ImportError,
},
#[error(transparent)]
Storage(#[from] CryptoStoreError),
#[error(transparent)]
Verification(#[from] ManualVerifyError),
#[error(transparent)]
Decryption(#[from] DecryptionError),
}
impl SecretStorageError {
fn into_import_error(secret_name: SecretName, error: impl Into<ImportError>) -> Self {
SecretStorageError::ImportError { name: secret_name, error: error.into() }
}
fn from_secret_import_error(error: SecretImportError) -> Self {
match error {
SecretImportError::Key { name, error } => {
SecretStorageError::ImportError { name, error: ImportError::Key(error) }
}
SecretImportError::MismatchedPublicKeys { name } => {
SecretStorageError::ImportError { name, error: ImportError::MismatchedPublicKeys }
}
SecretImportError::Store(error) => SecretStorageError::Storage(error),
}
}
}
#[derive(Debug, Error)]
pub enum DecryptionError {
#[error("Could not decrypt the secret using the secret storage key, invalid MAC.")]
Mac(#[from] MacError),
#[error("Could not decode the secret, the secret is not valid UTF-8")]
Utf8(#[from] FromUtf8Error),
}
#[derive(Debug)]
pub struct SecretStorage {
pub(super) client: Client,
}
impl SecretStorage {
pub async fn open_secret_store(&self, secret_storage_key: &str) -> Result<SecretStore> {
let maybe_default_key_id = self.fetch_default_key_id().await?;
if let Some(default_key_id) = maybe_default_key_id {
let default_key_id = default_key_id.deserialize()?;
let event_type =
GlobalAccountDataEventType::SecretStorageKey(default_key_id.key_id.to_owned());
let secret_key =
self.client.account().fetch_account_data(event_type.to_owned()).await?;
if let Some(secret_key_content) = secret_key {
let event_type = event_type.to_string();
let secret_key_content = to_raw_value(&secret_key_content)?;
let secret_key_content =
SecretStorageKeyEventContent::from_parts(&event_type, &secret_key_content)?;
let key =
SecretStorageKey::from_account_data(secret_storage_key, secret_key_content)?;
Ok(SecretStore { client: self.client.to_owned(), key })
} else {
Err(SecretStorageError::MissingKeyInfo { key_id: Some(default_key_id.key_id) })
}
} else {
Err(SecretStorageError::MissingKeyInfo { key_id: None })
}
}
pub fn create_secret_store(&self) -> CreateStore<'_> {
CreateStore { secret_storage: self, passphrase: None }
}
pub async fn is_enabled(&self) -> crate::Result<bool> {
if let Some(content) = self.fetch_default_key_id().await? {
Ok(content.deserialize().is_ok())
} else {
Ok(false)
}
}
pub async fn fetch_default_key_id(
&self,
) -> crate::Result<Option<Raw<SecretStorageDefaultKeyEventContent>>> {
self.client
.account()
.fetch_account_data_static::<SecretStorageDefaultKeyEventContent>()
.await
}
}