mirror of
https://github.com/matrix-org/matrix-rust-sdk.git
synced 2026-05-08 07:56:55 -04:00
Updated the store encryption to use a enum Secret instead of passphrase
This commit is contained in:
@@ -52,7 +52,7 @@ use crate::{
|
||||
repeat_vars, EncryptableStore, Key, SqliteAsyncConnExt, SqliteKeyValueStoreAsyncConnExt,
|
||||
SqliteKeyValueStoreConnExt,
|
||||
},
|
||||
OpenStoreError, SqliteStoreConfig,
|
||||
OpenStoreError, Secret, SqliteStoreConfig,
|
||||
};
|
||||
|
||||
/// The database name.
|
||||
@@ -83,9 +83,18 @@ impl EncryptableStore for SqliteCryptoStore {
|
||||
}
|
||||
|
||||
impl SqliteCryptoStore {
|
||||
/// Open the SQLite-based event cache store at the given path using the
|
||||
/// given passphrase to encrypt private data
|
||||
pub async fn open(
|
||||
path: impl AsRef<Path>,
|
||||
passphrase: Option<&str>,
|
||||
) -> Result<Self, OpenStoreError> {
|
||||
Self::open_with_config(SqliteStoreConfig::new(path).passphrase(passphrase)).await
|
||||
}
|
||||
|
||||
/// Open the SQLite-based crypto store at the given path using the given
|
||||
/// key to encrypt private data.
|
||||
pub async fn open(
|
||||
pub async fn open_with_key(
|
||||
path: impl AsRef<Path>,
|
||||
key: Option<&[u8; 32]>,
|
||||
) -> Result<Self, OpenStoreError> {
|
||||
@@ -94,7 +103,7 @@ impl SqliteCryptoStore {
|
||||
|
||||
/// Open the SQLite-based crypto store with the config open config.
|
||||
pub async fn open_with_config(config: SqliteStoreConfig) -> Result<Self, OpenStoreError> {
|
||||
let SqliteStoreConfig { path, key, pool_config, runtime_config } = config;
|
||||
let SqliteStoreConfig { path, pool_config, runtime_config, secret } = config;
|
||||
|
||||
fs::create_dir_all(&path).await.map_err(OpenStoreError::CreateDir)?;
|
||||
|
||||
@@ -103,7 +112,7 @@ impl SqliteCryptoStore {
|
||||
|
||||
let pool = config.create_pool(Runtime::Tokio1)?;
|
||||
|
||||
let this = Self::open_with_pool(pool, key.as_deref()).await?;
|
||||
let this = Self::open_with_pool(pool, secret).await?;
|
||||
this.pool.get().await?.apply_runtime_config(runtime_config).await?;
|
||||
|
||||
Ok(this)
|
||||
@@ -113,7 +122,7 @@ impl SqliteCryptoStore {
|
||||
/// pool. The given key will be used to encrypt private data.
|
||||
async fn open_with_pool(
|
||||
pool: SqlitePool,
|
||||
key: Option<&[u8; 32]>,
|
||||
secret: Option<Secret>,
|
||||
) -> Result<Self, OpenStoreError> {
|
||||
let conn = pool.get().await?;
|
||||
|
||||
@@ -121,8 +130,8 @@ impl SqliteCryptoStore {
|
||||
debug!("Opened sqlite store with version {}", version);
|
||||
run_migrations(&conn, version).await?;
|
||||
|
||||
let store_cipher = match key {
|
||||
Some(k) => Some(Arc::new(conn.get_or_create_store_cipher(k).await?)),
|
||||
let store_cipher = match secret {
|
||||
Some(s) => Some(Arc::new(conn.get_or_create_store_cipher(s).await?)),
|
||||
None => None,
|
||||
};
|
||||
|
||||
@@ -1891,14 +1900,14 @@ mod tests {
|
||||
assert!(backup_keys.decryption_key.is_some());
|
||||
}
|
||||
|
||||
async fn get_store(name: &str, key: Option<&[u8; 32]>, clear_data: bool) -> SqliteCryptoStore {
|
||||
async fn get_store(name: &str, passphrase: Option<&str>, clear_data: bool) -> SqliteCryptoStore {
|
||||
let tmpdir_path = TMP_DIR.path().join(name);
|
||||
|
||||
if clear_data {
|
||||
let _ = fs::remove_dir_all(&tmpdir_path).await;
|
||||
}
|
||||
|
||||
SqliteCryptoStore::open(tmpdir_path.to_str().unwrap(), key)
|
||||
SqliteCryptoStore::open(tmpdir_path.to_str().unwrap(), passphrase)
|
||||
.await
|
||||
.expect("Can't create a key protected store")
|
||||
}
|
||||
@@ -1918,18 +1927,15 @@ mod encrypted_tests {
|
||||
|
||||
static TMP_DIR: Lazy<TempDir> = Lazy::new(|| tempdir().unwrap());
|
||||
|
||||
async fn get_store(name: &str, key: Option<&[u8; 32]>, clear_data: bool) -> SqliteCryptoStore {
|
||||
async fn get_store(name: &str, passphrase: Option<&str>, clear_data: bool) -> SqliteCryptoStore {
|
||||
let tmpdir_path = TMP_DIR.path().join(name);
|
||||
let pass = key.unwrap_or(&[
|
||||
74, 156, 222, 8, 61, 113, 145, 197, 29, 84, 179, 240, 6, 38, 126, 253, 92, 203, 173,
|
||||
47, 134, 13, 251, 100, 196, 42, 109, 159, 221, 73, 10, 187,
|
||||
]);
|
||||
let pass = passphrase.unwrap_or(&"default_test_password");
|
||||
|
||||
if clear_data {
|
||||
let _ = fs::remove_dir_all(&tmpdir_path).await;
|
||||
}
|
||||
|
||||
SqliteCryptoStore::open(tmpdir_path.to_str().unwrap(), Some(key))
|
||||
SqliteCryptoStore::open(tmpdir_path.to_str().unwrap(), Some(passphrase))
|
||||
.await
|
||||
.expect("Can't create a key protected store")
|
||||
}
|
||||
|
||||
@@ -32,7 +32,9 @@ use matrix_sdk_base::{
|
||||
};
|
||||
use matrix_sdk_store_encryption::StoreCipher;
|
||||
use ruma::{
|
||||
events::relation::RelationType, EventId, MilliSecondsSinceUnixEpoch, OwnedEventId, RoomId,
|
||||
events::{relation::RelationType, secret_storage::key::PassPhrase},
|
||||
time::SystemTime,
|
||||
EventId, MilliSecondsSinceUnixEpoch, MxcUri, OwnedEventId, RoomId,
|
||||
};
|
||||
use rusqlite::{params_from_iter, OptionalExtension, ToSql, Transaction, TransactionBehavior};
|
||||
use tokio::{
|
||||
@@ -47,7 +49,7 @@ use crate::{
|
||||
repeat_vars, EncryptableStore, Key, SqliteAsyncConnExt, SqliteKeyValueStoreAsyncConnExt,
|
||||
SqliteKeyValueStoreConnExt, SqliteTransactionExt,
|
||||
},
|
||||
OpenStoreError, SqliteStoreConfig,
|
||||
OpenStoreError, Secret, SqliteStoreConfig,
|
||||
};
|
||||
|
||||
mod keys {
|
||||
@@ -102,8 +104,17 @@ impl EncryptableStore for SqliteEventCacheStore {
|
||||
|
||||
impl SqliteEventCacheStore {
|
||||
/// Open the SQLite-based event cache store at the given path using the
|
||||
/// given key to encrypt private data.
|
||||
/// given passphrase to encrypt private data.
|
||||
pub async fn open(
|
||||
path: impl AsRef<Path>,
|
||||
passphrase: Option<&str>,
|
||||
) -> Result<Self, OpenStoreError> {
|
||||
Self::open_with_config(SqliteStoreConfig::new(path).passphrase(passphrase)).await
|
||||
}
|
||||
|
||||
/// Open the SQLite-based event cache store at the given path using the
|
||||
/// given key to encrypt private data.
|
||||
pub async fn open_with_key(
|
||||
path: impl AsRef<Path>,
|
||||
key: Option<&[u8; 32]>,
|
||||
) -> Result<Self, OpenStoreError> {
|
||||
@@ -117,7 +128,7 @@ impl SqliteEventCacheStore {
|
||||
|
||||
let _timer = timer!("open_with_config");
|
||||
|
||||
let SqliteStoreConfig { path, key, pool_config, runtime_config } = config;
|
||||
let SqliteStoreConfig { path, pool_config, runtime_config, secret } = config;
|
||||
|
||||
fs::create_dir_all(&path).await.map_err(OpenStoreError::CreateDir)?;
|
||||
|
||||
@@ -126,7 +137,7 @@ impl SqliteEventCacheStore {
|
||||
|
||||
let pool = config.create_pool(Runtime::Tokio1)?;
|
||||
|
||||
let this = Self::open_with_pool(pool, key.as_ref()).await?;
|
||||
let this = Self::open_with_pool(pool, secret).await?;
|
||||
this.write().await?.apply_runtime_config(runtime_config).await?;
|
||||
|
||||
Ok(this)
|
||||
@@ -136,15 +147,15 @@ impl SqliteEventCacheStore {
|
||||
/// pool. The given key will be used to encrypt private data.
|
||||
async fn open_with_pool(
|
||||
pool: SqlitePool,
|
||||
key: Option<&[u8; 32]>,
|
||||
secret: Option<Secret>,
|
||||
) -> Result<Self, OpenStoreError> {
|
||||
let conn = pool.get().await?;
|
||||
|
||||
let version = conn.db_version().await?;
|
||||
run_migrations(&conn, version).await?;
|
||||
|
||||
let store_cipher = match key {
|
||||
Some(k) => Some(Arc::new(conn.get_or_create_store_cipher(k).await?)),
|
||||
let store_cipher = match secret {
|
||||
Some(s) => Some(Arc::new(conn.get_or_create_store_cipher(s).await?)),
|
||||
None => None,
|
||||
};
|
||||
|
||||
@@ -2443,10 +2454,7 @@ mod encrypted_tests {
|
||||
|
||||
Ok(SqliteEventCacheStore::open(
|
||||
tmpdir_path.to_str().unwrap(),
|
||||
Some(&[
|
||||
42, 199, 108, 12, 87, 250, 163, 31, 221, 60, 5, 144, 89, 237, 118, 201, 33, 176,
|
||||
248, 73, 154, 8, 130, 190, 57, 110, 226, 3, 99, 45, 164, 209,
|
||||
]),
|
||||
Some("default_test_password"),
|
||||
)
|
||||
.await
|
||||
.unwrap())
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@@ -47,13 +46,19 @@ pub use self::state_store::{SqliteStateStore, DATABASE_NAME as STATE_STORE_DATAB
|
||||
#[cfg(test)]
|
||||
matrix_sdk_test_utils::init_tracing_for_tests!();
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub enum Secret {
|
||||
Key([u8; 32]),
|
||||
PassPhrase(String),
|
||||
}
|
||||
|
||||
/// A configuration structure used for opening a store.
|
||||
#[derive(Clone)]
|
||||
pub struct SqliteStoreConfig {
|
||||
/// Path to the database, without the file name.
|
||||
path: PathBuf,
|
||||
/// Key to open the store, if any
|
||||
key: Option<[u8; 32]>,
|
||||
/// Secret to open the store, if any
|
||||
secret: Option<Secret>,
|
||||
/// The pool configuration for [`deadpool_sqlite`].
|
||||
pool_config: PoolConfig,
|
||||
/// The runtime configuration to apply when opening an SQLite connection.
|
||||
@@ -86,9 +91,9 @@ impl SqliteStoreConfig {
|
||||
{
|
||||
Self {
|
||||
path: path.as_ref().to_path_buf(),
|
||||
key: None,
|
||||
pool_config: PoolConfig::new(max(POOL_MINIMUM_SIZE, num_cpus::get_physical() * 4)),
|
||||
runtime_config: RuntimeConfig::default(),
|
||||
secret: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,9 +128,20 @@ impl SqliteStoreConfig {
|
||||
self
|
||||
}
|
||||
|
||||
/// Define the passphrase if the store is encoded.
|
||||
pub fn passphrase(mut self, passphrase: Option<&str>) -> Self {
|
||||
self.secret = if let Some(passphrase) = passphrase {
|
||||
Some(Secret::PassPhrase(passphrase.to_owned()))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
self
|
||||
}
|
||||
|
||||
/// Define the key if the store is encoded.
|
||||
pub fn key(mut self, key: Option<&[u8; 32]>) -> Self {
|
||||
self.key = key.map(|key| key.to_owned());
|
||||
self.secret =
|
||||
if let Some(key) = key { Some(Secret::Key(key.map(|key| key))) } else { None };
|
||||
self
|
||||
}
|
||||
|
||||
@@ -229,7 +245,7 @@ mod tests {
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
use super::{SqliteStoreConfig, POOL_MINIMUM_SIZE};
|
||||
use super::{Secret, SqliteStoreConfig, POOL_MINIMUM_SIZE};
|
||||
|
||||
#[test]
|
||||
fn test_new() {
|
||||
@@ -265,14 +281,14 @@ mod tests {
|
||||
|
||||
assert_eq!(store_config.path, PathBuf::from("foo"));
|
||||
assert_eq!(
|
||||
store_config.key,
|
||||
Some(
|
||||
store_config.secret,
|
||||
Some(Secret::Key(
|
||||
[
|
||||
143, 27, 202, 78, 96, 55, 13, 149, 247, 8, 33, 120, 204, 92, 171, 66, 19, 238,
|
||||
61, 107, 132, 211, 40, 244, 71, 190, 99, 14, 173, 225, 6, 156,
|
||||
]
|
||||
.to_owned()
|
||||
)
|
||||
))
|
||||
);
|
||||
assert_eq!(store_config.pool_config.max_size, 42);
|
||||
assert!(store_config.runtime_config.optimize.not());
|
||||
|
||||
@@ -48,7 +48,7 @@ use crate::{
|
||||
repeat_vars, EncryptableStore, Key, SqliteAsyncConnExt, SqliteKeyValueStoreAsyncConnExt,
|
||||
SqliteKeyValueStoreConnExt,
|
||||
},
|
||||
OpenStoreError, SqliteStoreConfig,
|
||||
OpenStoreError, Secret, SqliteStoreConfig,
|
||||
};
|
||||
|
||||
mod keys {
|
||||
@@ -92,9 +92,18 @@ impl fmt::Debug for SqliteStateStore {
|
||||
}
|
||||
|
||||
impl SqliteStateStore {
|
||||
/// Open the SQLite-based event cache store at the given path using the
|
||||
/// given passphrase to encrypt private data
|
||||
pub async fn open(
|
||||
path: impl AsRef<Path>,
|
||||
passphrase: Option<&str>,
|
||||
) -> Result<Self, OpenStoreError> {
|
||||
Self::open_with_config(SqliteStoreConfig::new(path).passphrase(passphrase)).await
|
||||
}
|
||||
|
||||
/// Open the SQLite-based state store at the given path using the given
|
||||
/// key to encrypt private data.
|
||||
pub async fn open(
|
||||
pub async fn open_with_key(
|
||||
path: impl AsRef<Path>,
|
||||
key: Option<&[u8; 32]>,
|
||||
) -> Result<Self, OpenStoreError> {
|
||||
@@ -103,7 +112,7 @@ impl SqliteStateStore {
|
||||
|
||||
/// Open the SQLite-based state store with the config open config.
|
||||
pub async fn open_with_config(config: SqliteStoreConfig) -> Result<Self, OpenStoreError> {
|
||||
let SqliteStoreConfig { path, key, pool_config, runtime_config } = config;
|
||||
let SqliteStoreConfig { path, pool_config, runtime_config, secret } = config;
|
||||
|
||||
fs::create_dir_all(&path).await.map_err(OpenStoreError::CreateDir)?;
|
||||
|
||||
@@ -112,7 +121,7 @@ impl SqliteStateStore {
|
||||
|
||||
let pool = config.create_pool(Runtime::Tokio1)?;
|
||||
|
||||
let this = Self::open_with_pool(pool, key.as_ref()).await?;
|
||||
let this = Self::open_with_pool(pool, secret).await?;
|
||||
this.pool.get().await?.apply_runtime_config(runtime_config).await?;
|
||||
|
||||
Ok(this)
|
||||
@@ -122,7 +131,7 @@ impl SqliteStateStore {
|
||||
/// The given key will be used to encrypt private data.
|
||||
pub async fn open_with_pool(
|
||||
pool: SqlitePool,
|
||||
key: Option<&[u8; 32]>,
|
||||
secret: Option<Secret>,
|
||||
) -> Result<Self, OpenStoreError> {
|
||||
let conn = pool.get().await?;
|
||||
|
||||
@@ -133,8 +142,8 @@ impl SqliteStateStore {
|
||||
version = 1;
|
||||
}
|
||||
|
||||
let store_cipher = match key {
|
||||
Some(k) => Some(Arc::new(conn.get_or_create_store_cipher(k).await?)),
|
||||
let store_cipher = match secret {
|
||||
Some(s) => Some(Arc::new(conn.get_or_create_store_cipher(s).await?)),
|
||||
None => None,
|
||||
};
|
||||
let this = Self { store_cipher, pool };
|
||||
@@ -2273,11 +2282,7 @@ mod encrypted_tests {
|
||||
tracing::info!("using store @ {}", tmpdir_path.to_str().unwrap());
|
||||
|
||||
Ok(SqliteStateStore::open(
|
||||
tmpdir_path.to_str().unwrap(),
|
||||
Some(&[
|
||||
201, 17, 66, 135, 59, 240, 28, 102, 88, 219, 3, 147, 125, 34, 76, 250, 190, 12,
|
||||
224, 97, 49, 164, 143, 254, 61, 85, 202, 16, 111, 0, 178, 73,
|
||||
]),
|
||||
tmpdir_path.to_str().unwrap(), Some("default_test_password")
|
||||
)
|
||||
.await
|
||||
.unwrap())
|
||||
@@ -2372,15 +2377,12 @@ mod migration_tests {
|
||||
use crate::{
|
||||
error::{Error, Result},
|
||||
utils::{EncryptableStore as _, SqliteAsyncConnExt, SqliteKeyValueStoreAsyncConnExt},
|
||||
OpenStoreError,
|
||||
OpenStoreError, Secret,
|
||||
};
|
||||
|
||||
static TMP_DIR: Lazy<TempDir> = Lazy::new(|| tempdir().unwrap());
|
||||
static NUM: AtomicU32 = AtomicU32::new(0);
|
||||
const SECRET: &[u8; 32] = &[
|
||||
119, 34, 208, 91, 14, 160, 239, 67, 198, 122, 9, 53, 211, 80, 103, 251, 61, 199, 26, 142,
|
||||
193, 240, 77, 36, 5, 187, 164, 112, 218, 38, 150, 249,
|
||||
];
|
||||
const SECRET: &str = "secret";
|
||||
|
||||
fn new_path() -> PathBuf {
|
||||
let name = NUM.fetch_add(1, SeqCst).to_string();
|
||||
@@ -2398,7 +2400,7 @@ mod migration_tests {
|
||||
|
||||
init(&conn).await?;
|
||||
|
||||
let store_cipher = Some(Arc::new(conn.get_or_create_store_cipher(SECRET).await.unwrap()));
|
||||
let store_cipher = Some(Arc::new(conn.get_or_create_store_cipher(Secret::PassPhrase(SECRET.to_owned())).await.unwrap()));
|
||||
let this = SqliteStateStore { store_cipher, pool };
|
||||
this.run_migrations(&conn, 1, Some(version)).await?;
|
||||
|
||||
|
||||
@@ -24,14 +24,17 @@ use async_trait::async_trait;
|
||||
use deadpool_sqlite::Object as SqliteAsyncConn;
|
||||
use itertools::Itertools;
|
||||
use matrix_sdk_store_encryption::StoreCipher;
|
||||
use ruma::{serde::Raw, time::SystemTime, OwnedEventId, OwnedRoomId};
|
||||
use ruma::{
|
||||
events::secret_storage::key::PassPhrase, serde::Raw, time::SystemTime, OwnedEventId,
|
||||
OwnedRoomId,
|
||||
};
|
||||
use rusqlite::{limits::Limit, OptionalExtension, Params, Row, Statement, Transaction};
|
||||
use serde::{de::DeserializeOwned, Serialize};
|
||||
use tracing::{error, warn};
|
||||
|
||||
use crate::{
|
||||
error::{Error, Result},
|
||||
OpenStoreError, RuntimeConfig,
|
||||
OpenStoreError, RuntimeConfig, Secret,
|
||||
};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
@@ -393,7 +396,7 @@ impl SqliteKeyValueStoreConnExt for rusqlite::Connection {
|
||||
///
|
||||
/// ```sql
|
||||
/// CREATE TABLE "kv" (
|
||||
/// "key" TEXT PRIMARY KEY NOT NULL,
|
||||
/// "key" TEXT PRIMARY KEY NOT NULL,d
|
||||
/// "value" BLOB NOT NULL
|
||||
/// );
|
||||
/// ```
|
||||
@@ -457,16 +460,22 @@ pub(crate) trait SqliteKeyValueStoreAsyncConnExt: SqliteAsyncConnExt {
|
||||
/// Get the [`StoreCipher`] of the database or create it.
|
||||
async fn get_or_create_store_cipher(
|
||||
&self,
|
||||
key: &[u8; 32],
|
||||
secret: Secret,
|
||||
) -> Result<StoreCipher, OpenStoreError> {
|
||||
let encrypted_cipher = self.get_kv("cipher").await.map_err(OpenStoreError::LoadCipher)?;
|
||||
|
||||
let cipher = if let Some(encrypted) = encrypted_cipher {
|
||||
StoreCipher::import_with_key(key, &encrypted)?
|
||||
match secret {
|
||||
Secret::PassPhrase(passphrase) => StoreCipher::import(&passphrase, &encrypted)?,
|
||||
Secret::Key(key) => StoreCipher::import_with_key(&key, &encrypted)?,
|
||||
}
|
||||
} else {
|
||||
let cipher = StoreCipher::new()?;
|
||||
//#[cfg(not(test))]
|
||||
let export = cipher.export_with_key(key);
|
||||
let export = match secret {
|
||||
Secret::PassPhrase(passphrase) => cipher.export(&passphrase),
|
||||
Secret::Key(key) => cipher.export_with_key(&key),
|
||||
};
|
||||
//#[cfg(test)]
|
||||
//let export = cipher._insecure_export_fast_for_testing(passphrase);
|
||||
self.set_kv("cipher", export?).await.map_err(OpenStoreError::SaveCipher)?;
|
||||
|
||||
Reference in New Issue
Block a user