base: Initial support for an encrypted sled store.
parent
06a973a1b8
commit
4a06c9e82d
|
@ -181,7 +181,7 @@ pub struct BaseClient {
|
|||
#[cfg(feature = "encryption")]
|
||||
cryptostore: Arc<Mutex<Option<Box<dyn CryptoStore>>>>,
|
||||
store_path: Arc<Option<PathBuf>>,
|
||||
store_passphrase: Arc<Zeroizing<String>>,
|
||||
store_passphrase: Arc<Option<Zeroizing<String>>>,
|
||||
/// Any implementor of EventEmitter will act as the callbacks for various
|
||||
/// events.
|
||||
event_emitter: Arc<RwLock<Option<Emitter>>>,
|
||||
|
@ -282,8 +282,13 @@ impl BaseClient {
|
|||
/// previous login call.
|
||||
pub fn new_with_config(config: BaseClientConfig) -> Result<Self> {
|
||||
let store = if let Some(path) = &config.store_path {
|
||||
if let Some(passphrase) = &config.passphrase {
|
||||
info!("Opening an encrypted store in path {}", path.display());
|
||||
SledStore::open_with_passphrase(path, passphrase)?
|
||||
} else {
|
||||
info!("Opening store in path {}", path.display());
|
||||
SledStore::open_with_path(path)?
|
||||
}
|
||||
} else {
|
||||
SledStore::open()?
|
||||
};
|
||||
|
@ -301,10 +306,7 @@ impl BaseClient {
|
|||
#[cfg(feature = "encryption")]
|
||||
cryptostore: Mutex::new(config.crypto_store).into(),
|
||||
store_path: config.store_path.into(),
|
||||
store_passphrase: config
|
||||
.passphrase
|
||||
.unwrap_or_else(|| Zeroizing::new("DEFAULT_PASSPHRASE".to_owned()))
|
||||
.into(),
|
||||
store_passphrase: config.passphrase.into(),
|
||||
event_emitter: RwLock::new(None).into(),
|
||||
})
|
||||
}
|
||||
|
@ -377,7 +379,7 @@ impl BaseClient {
|
|||
&session.user_id,
|
||||
&session.device_id,
|
||||
path,
|
||||
&self.store_passphrase,
|
||||
self.store_passphrase.as_deref().map(|p| p.as_str()),
|
||||
)
|
||||
.await
|
||||
.map_err(OlmError::from)?,
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
|
||||
mod store_key;
|
||||
|
||||
use std::{convert::TryFrom, path::Path, time::SystemTime};
|
||||
use std::{convert::TryFrom, path::Path, sync::Arc, time::SystemTime};
|
||||
|
||||
use futures::stream::{self, Stream};
|
||||
use matrix_sdk_common::{
|
||||
|
@ -25,6 +25,7 @@ use matrix_sdk_common::{
|
|||
},
|
||||
identifiers::{RoomId, UserId},
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use sled::{
|
||||
transaction::{ConflictableTransactionError, TransactionError},
|
||||
|
@ -34,11 +35,20 @@ use tracing::info;
|
|||
|
||||
use crate::deserialized_responses::MemberEvent;
|
||||
|
||||
use self::store_key::{EncryptedEvent, StoreKey};
|
||||
|
||||
use super::{Result, RoomInfo, StateChanges, StoreError};
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub enum DatabaseType {
|
||||
Unencrypted,
|
||||
Encrypted(store_key::EncryptedStoreKey),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SledStore {
|
||||
inner: Db,
|
||||
store_key: Arc<Option<StoreKey>>,
|
||||
session: Tree,
|
||||
account_data: Tree,
|
||||
members: Tree,
|
||||
|
@ -55,7 +65,7 @@ pub struct SledStore {
|
|||
}
|
||||
|
||||
impl SledStore {
|
||||
fn open_helper(db: Db) -> Result<Self> {
|
||||
fn open_helper(db: Db, store_key: Option<StoreKey>) -> Result<Self> {
|
||||
let session = db.open_tree("session")?;
|
||||
let account_data = db.open_tree("account_data")?;
|
||||
|
||||
|
@ -75,6 +85,7 @@ impl SledStore {
|
|||
|
||||
Ok(Self {
|
||||
inner: db,
|
||||
store_key: store_key.into(),
|
||||
session,
|
||||
account_data,
|
||||
members,
|
||||
|
@ -94,14 +105,64 @@ impl SledStore {
|
|||
pub fn open() -> Result<Self> {
|
||||
let db = Config::new().temporary(true).open()?;
|
||||
|
||||
SledStore::open_helper(db)
|
||||
SledStore::open_helper(db, None)
|
||||
}
|
||||
|
||||
pub fn open_with_passphrase(path: impl AsRef<Path>, passphrase: &str) -> Result<Self> {
|
||||
let path = path.as_ref().join("matrix-sdk-state");
|
||||
let db = Config::new().temporary(false).path(path).open()?;
|
||||
|
||||
let store_key: Option<DatabaseType> = db
|
||||
.get("store_key")?
|
||||
.map(|k| serde_json::from_slice(&k).map_err(StoreError::Json))
|
||||
.transpose()?;
|
||||
|
||||
let store_key = if let Some(key) = store_key {
|
||||
if let DatabaseType::Encrypted(k) = key {
|
||||
let key = StoreKey::import(passphrase, k).unwrap();
|
||||
key
|
||||
} else {
|
||||
panic!("Trying to open an unencrypted store with a passphrase");
|
||||
}
|
||||
} else {
|
||||
let key = StoreKey::new();
|
||||
let encrypted_key = DatabaseType::Encrypted(key.export(passphrase));
|
||||
db.insert("store_key", serde_json::to_vec(&encrypted_key)?)?;
|
||||
key
|
||||
};
|
||||
|
||||
SledStore::open_helper(db, Some(store_key))
|
||||
}
|
||||
|
||||
pub fn open_with_path(path: impl AsRef<Path>) -> Result<Self> {
|
||||
let path = path.as_ref().join("matrix-sdk-state");
|
||||
let db = Config::new().temporary(false).path(path).open()?;
|
||||
|
||||
SledStore::open_helper(db)
|
||||
SledStore::open_helper(db, None)
|
||||
}
|
||||
|
||||
fn serialize_event(
|
||||
&self,
|
||||
event: &impl Serialize,
|
||||
) -> std::result::Result<Vec<u8>, serde_json::Error> {
|
||||
if let Some(key) = &*self.store_key {
|
||||
let encrypted = key.encrypt(event).unwrap();
|
||||
serde_json::to_vec(&encrypted)
|
||||
} else {
|
||||
serde_json::to_vec(event)
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize_event<T: for<'b> Deserialize<'b>>(
|
||||
&self,
|
||||
event: &[u8],
|
||||
) -> std::result::Result<T, serde_json::Error> {
|
||||
if let Some(key) = &*self.store_key {
|
||||
let encrypted: EncryptedEvent = serde_json::from_slice(&event)?;
|
||||
Ok(key.decrypt(encrypted).unwrap())
|
||||
} else {
|
||||
serde_json::from_slice(event)
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn save_filter(&self, filter_name: &str, filter_id: &str) -> Result<()> {
|
||||
|
@ -184,7 +245,7 @@ impl SledStore {
|
|||
|
||||
members.insert(
|
||||
format!("{}{}", room.as_str(), &event.state_key).as_str(),
|
||||
serde_json::to_vec(&event)
|
||||
self.serialize_event(&event)
|
||||
.map_err(ConflictableTransactionError::Abort)?,
|
||||
)?;
|
||||
}
|
||||
|
@ -194,7 +255,7 @@ impl SledStore {
|
|||
for (user_id, profile) in users {
|
||||
profiles.insert(
|
||||
format!("{}{}", room.as_str(), user_id.as_str()).as_str(),
|
||||
serde_json::to_vec(&profile)
|
||||
self.serialize_event(&profile)
|
||||
.map_err(ConflictableTransactionError::Abort)?,
|
||||
)?;
|
||||
}
|
||||
|
@ -203,7 +264,7 @@ impl SledStore {
|
|||
for (event_type, event) in &changes.account_data {
|
||||
account_data.insert(
|
||||
event_type.as_str(),
|
||||
serde_json::to_vec(&event)
|
||||
self.serialize_event(&event)
|
||||
.map_err(ConflictableTransactionError::Abort)?,
|
||||
)?;
|
||||
}
|
||||
|
@ -212,7 +273,7 @@ impl SledStore {
|
|||
for (event_type, event) in events {
|
||||
room_account_data.insert(
|
||||
format!("{}{}", room.as_str(), event_type).as_str(),
|
||||
serde_json::to_vec(&event)
|
||||
self.serialize_event(&event)
|
||||
.map_err(ConflictableTransactionError::Abort)?,
|
||||
)?;
|
||||
}
|
||||
|
@ -229,7 +290,7 @@ impl SledStore {
|
|||
event.state_key(),
|
||||
)
|
||||
.as_bytes(),
|
||||
serde_json::to_vec(&event)
|
||||
self.serialize_event(&event)
|
||||
.map_err(ConflictableTransactionError::Abort)?,
|
||||
)?;
|
||||
}
|
||||
|
@ -239,7 +300,7 @@ impl SledStore {
|
|||
for (room_id, room_info) in &changes.room_infos {
|
||||
rooms.insert(
|
||||
room_id.as_bytes(),
|
||||
serde_json::to_vec(room_info)
|
||||
self.serialize_event(room_info)
|
||||
.map_err(ConflictableTransactionError::Abort)?,
|
||||
)?;
|
||||
}
|
||||
|
@ -247,7 +308,7 @@ impl SledStore {
|
|||
for (sender, event) in &changes.presence {
|
||||
presence.insert(
|
||||
sender.as_bytes(),
|
||||
serde_json::to_vec(&event)
|
||||
self.serialize_event(&event)
|
||||
.map_err(ConflictableTransactionError::Abort)?,
|
||||
)?;
|
||||
}
|
||||
|
@ -255,7 +316,7 @@ impl SledStore {
|
|||
for (room_id, info) in &changes.invited_room_info {
|
||||
striped_rooms.insert(
|
||||
room_id.as_str(),
|
||||
serde_json::to_vec(&info)
|
||||
self.serialize_event(&info)
|
||||
.map_err(ConflictableTransactionError::Abort)?,
|
||||
)?;
|
||||
}
|
||||
|
@ -264,7 +325,7 @@ impl SledStore {
|
|||
for event in events.values() {
|
||||
stripped_members.insert(
|
||||
format!("{}{}", room.as_str(), &event.state_key).as_str(),
|
||||
serde_json::to_vec(&event)
|
||||
self.serialize_event(&event)
|
||||
.map_err(ConflictableTransactionError::Abort)?,
|
||||
)?;
|
||||
}
|
||||
|
@ -281,7 +342,7 @@ impl SledStore {
|
|||
event.state_key(),
|
||||
)
|
||||
.as_bytes(),
|
||||
serde_json::to_vec(&event)
|
||||
self.serialize_event(&event)
|
||||
.map_err(ConflictableTransactionError::Abort)?,
|
||||
)?;
|
||||
}
|
||||
|
@ -305,7 +366,7 @@ impl SledStore {
|
|||
Ok(self
|
||||
.presence
|
||||
.get(user_id.as_bytes())?
|
||||
.map(|e| serde_json::from_slice(&e))
|
||||
.map(|e| self.deserialize_event(&e))
|
||||
.transpose()?)
|
||||
}
|
||||
|
||||
|
@ -318,7 +379,7 @@ impl SledStore {
|
|||
Ok(self
|
||||
.room_state
|
||||
.get(format!("{}{}{}", room_id.as_str(), event_type, state_key).as_bytes())?
|
||||
.map(|e| serde_json::from_slice(&e))
|
||||
.map(|e| self.deserialize_event(&e))
|
||||
.transpose()?)
|
||||
}
|
||||
|
||||
|
@ -330,7 +391,7 @@ impl SledStore {
|
|||
Ok(self
|
||||
.profiles
|
||||
.get(format!("{}{}", room_id.as_str(), user_id.as_str()))?
|
||||
.map(|p| serde_json::from_slice(&p))
|
||||
.map(|p| self.deserialize_event(&p))
|
||||
.transpose()?)
|
||||
}
|
||||
|
||||
|
@ -342,7 +403,7 @@ impl SledStore {
|
|||
Ok(self
|
||||
.members
|
||||
.get(format!("{}{}", room_id.as_str(), state_key.as_str()))?
|
||||
.map(|v| serde_json::from_slice(&v))
|
||||
.map(|v| self.deserialize_event(&v))
|
||||
.transpose()?)
|
||||
}
|
||||
|
||||
|
@ -375,10 +436,11 @@ impl SledStore {
|
|||
}
|
||||
|
||||
pub async fn get_room_infos(&self) -> impl Stream<Item = Result<RoomInfo>> {
|
||||
let db = self.clone();
|
||||
stream::iter(
|
||||
self.room_info
|
||||
.iter()
|
||||
.map(|r| serde_json::from_slice(&r?.1).map_err(StoreError::Json)),
|
||||
.map(move |r| db.deserialize_event(&r?.1).map_err(StoreError::Json)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -265,7 +265,7 @@ impl OlmMachine {
|
|||
user_id: &UserId,
|
||||
device_id: &DeviceId,
|
||||
path: impl AsRef<Path>,
|
||||
passphrase: &str,
|
||||
passphrase: Option<&str>,
|
||||
) -> StoreResult<Self> {
|
||||
let store = SledStore::open_with_passphrase(path, passphrase)?;
|
||||
|
||||
|
@ -1775,7 +1775,7 @@ pub(crate) mod test {
|
|||
&user_id(),
|
||||
&alice_device_id(),
|
||||
tmpdir.as_ref(),
|
||||
"test",
|
||||
Some("test"),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
@ -1795,7 +1795,7 @@ pub(crate) mod test {
|
|||
&user_id,
|
||||
&alice_device_id(),
|
||||
tmpdir.as_ref(),
|
||||
"test",
|
||||
Some("test"),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
|
|
@ -81,11 +81,11 @@ impl From<TransactionError<serde_json::Error>> for CryptoStoreError {
|
|||
}
|
||||
|
||||
impl SledStore {
|
||||
pub fn open_with_passphrase(path: impl AsRef<Path>, passphrase: &str) -> Result<Self> {
|
||||
pub fn open_with_passphrase(path: impl AsRef<Path>, passphrase: Option<&str>) -> Result<Self> {
|
||||
let path = path.as_ref().join("matrix-sdk-crypto");
|
||||
let db = Config::new().temporary(false).path(path).open()?;
|
||||
|
||||
SledStore::open_helper(db, Some(passphrase))
|
||||
SledStore::open_helper(db, passphrase)
|
||||
}
|
||||
|
||||
fn open_helper(db: Db, passphrase: Option<&str>) -> Result<Self> {
|
||||
|
|
Loading…
Reference in New Issue