crypto: Store a copy of the user_id/device_id and identity keys in sessions.

master
Damir Jelić 2020-07-21 12:40:23 +02:00
parent 3f1439fe28
commit e50cf39a17
4 changed files with 65 additions and 24 deletions

View File

@ -37,7 +37,7 @@ pub use device::{Device, TrustState};
pub use error::{MegolmError, OlmError}; pub use error::{MegolmError, OlmError};
pub use machine::{OlmMachine, OneTimeKeys}; pub use machine::{OlmMachine, OneTimeKeys};
pub use memory_stores::{DeviceStore, GroupSessionStore, SessionStore, UserDevices}; pub use memory_stores::{DeviceStore, GroupSessionStore, SessionStore, UserDevices};
pub use olm::{Account, InboundGroupSession, OutboundGroupSession, Session}; pub use olm::{Account, IdentityKeys, InboundGroupSession, OutboundGroupSession, Session};
#[cfg(feature = "sqlite-cryptostore")] #[cfg(feature = "sqlite-cryptostore")]
pub use store::sqlite::SqliteStore; pub use store::sqlite::SqliteStore;
pub use store::{CryptoStore, CryptoStoreError}; pub use store::{CryptoStore, CryptoStoreError};

View File

@ -52,7 +52,7 @@ pub struct Account {
pub(crate) user_id: Arc<UserId>, pub(crate) user_id: Arc<UserId>,
pub(crate) device_id: Arc<Box<DeviceId>>, pub(crate) device_id: Arc<Box<DeviceId>>,
inner: Arc<Mutex<OlmAccount>>, inner: Arc<Mutex<OlmAccount>>,
identity_keys: Arc<IdentityKeys>, pub(crate) identity_keys: Arc<IdentityKeys>,
shared: Arc<AtomicBool>, shared: Arc<AtomicBool>,
/// The number of signed one-time keys we have uploaded to the server. If /// The number of signed one-time keys we have uploaded to the server. If
/// this is None, no action will be taken. After a sync request the client /// this is None, no action will be taken. After a sync request the client
@ -395,6 +395,9 @@ impl Account {
let session_id = session.session_id(); let session_id = session.session_id();
Ok(Session { Ok(Session {
user_id: self.user_id.clone(),
device_id: self.device_id.clone(),
our_identity_keys: self.identity_keys.clone(),
inner: Arc::new(Mutex::new(session)), inner: Arc::new(Mutex::new(session)),
session_id: Arc::new(session_id), session_id: Arc::new(session_id),
sender_key: Arc::new(their_identity_key.to_owned()), sender_key: Arc::new(their_identity_key.to_owned()),
@ -495,6 +498,9 @@ impl Account {
let session_id = session.session_id(); let session_id = session.session_id();
Ok(Session { Ok(Session {
user_id: self.user_id.clone(),
device_id: self.device_id.clone(),
our_identity_keys: self.identity_keys.clone(),
inner: Arc::new(Mutex::new(session)), inner: Arc::new(Mutex::new(session)),
session_id: Arc::new(session_id), session_id: Arc::new(session_id),
sender_key: Arc::new(their_identity_key.to_owned()), sender_key: Arc::new(their_identity_key.to_owned()),

View File

@ -27,7 +27,7 @@ pub use olm_rs::{
utility::OlmUtility, utility::OlmUtility,
}; };
use super::Account; use super::{Account, IdentityKeys};
use crate::error::{EventError, OlmResult}; use crate::error::{EventError, OlmResult};
use crate::Device; use crate::Device;
@ -37,6 +37,7 @@ use matrix_sdk_common::{
room::encrypted::{CiphertextInfo, EncryptedEventContent, OlmV1Curve25519AesSha2Content}, room::encrypted::{CiphertextInfo, EncryptedEventContent, OlmV1Curve25519AesSha2Content},
EventType, EventType,
}, },
identifiers::{DeviceId, UserId},
instant::Instant, instant::Instant,
locks::Mutex, locks::Mutex,
}; };
@ -45,6 +46,9 @@ use matrix_sdk_common::{
/// `Account`s /// `Account`s
#[derive(Clone)] #[derive(Clone)]
pub struct Session { pub struct Session {
pub(crate) user_id: Arc<UserId>,
pub(crate) device_id: Arc<Box<DeviceId>>,
pub(crate) our_identity_keys: Arc<IdentityKeys>,
pub(crate) inner: Arc<Mutex<OlmSession>>, pub(crate) inner: Arc<Mutex<OlmSession>>,
pub(crate) session_id: Arc<String>, pub(crate) session_id: Arc<String>,
pub(crate) sender_key: Arc<String>, pub(crate) sender_key: Arc<String>,
@ -197,6 +201,9 @@ impl Session {
/// * `last_use_time` - The timestamp that marks when the session was /// * `last_use_time` - The timestamp that marks when the session was
/// last used to encrypt or decrypt an Olm message. /// last used to encrypt or decrypt an Olm message.
pub fn from_pickle( pub fn from_pickle(
user_id: Arc<UserId>,
device_id: Arc<Box<DeviceId>>,
our_identity_keys: Arc<IdentityKeys>,
pickle: String, pickle: String,
pickle_mode: PicklingMode, pickle_mode: PicklingMode,
sender_key: String, sender_key: String,
@ -207,6 +214,9 @@ impl Session {
let session_id = session.session_id(); let session_id = session.session_id();
Ok(Session { Ok(Session {
user_id,
device_id,
our_identity_keys,
inner: Arc::new(Mutex::new(session)), inner: Arc::new(Mutex::new(session)),
session_id: Arc::new(session_id), session_id: Arc::new(session_id),
sender_key: Arc::new(sender_key), sender_key: Arc::new(sender_key),

View File

@ -26,9 +26,10 @@ use olm_rs::PicklingMode;
use sqlx::{query, query_as, sqlite::SqliteQueryAs, Connect, Executor, SqliteConnection}; use sqlx::{query, query_as, sqlite::SqliteQueryAs, Connect, Executor, SqliteConnection};
use zeroize::Zeroizing; use zeroize::Zeroizing;
use super::{Account, CryptoStore, CryptoStoreError, InboundGroupSession, Result, Session}; use super::{CryptoStore, CryptoStoreError, Result};
use crate::device::{Device, TrustState}; use crate::device::{Device, TrustState};
use crate::memory_stores::{DeviceStore, GroupSessionStore, SessionStore, UserDevices}; use crate::memory_stores::{DeviceStore, GroupSessionStore, SessionStore, UserDevices};
use crate::{Account, IdentityKeys, InboundGroupSession, Session};
use matrix_sdk_common::api::r0::keys::{AlgorithmAndDeviceId, KeyAlgorithm}; use matrix_sdk_common::api::r0::keys::{AlgorithmAndDeviceId, KeyAlgorithm};
use matrix_sdk_common::events::Algorithm; use matrix_sdk_common::events::Algorithm;
use matrix_sdk_common::identifiers::{DeviceId, RoomId, UserId}; use matrix_sdk_common::identifiers::{DeviceId, RoomId, UserId};
@ -36,8 +37,8 @@ use matrix_sdk_common::identifiers::{DeviceId, RoomId, UserId};
/// SQLite based implementation of a `CryptoStore`. /// SQLite based implementation of a `CryptoStore`.
pub struct SqliteStore { pub struct SqliteStore {
user_id: Arc<UserId>, user_id: Arc<UserId>,
device_id: Arc<String>, device_id: Arc<Box<DeviceId>>,
account_id: Option<i64>, account_info: Option<AccountInfo>,
path: PathBuf, path: PathBuf,
sessions: SessionStore, sessions: SessionStore,
@ -50,6 +51,11 @@ pub struct SqliteStore {
pickle_passphrase: Option<Zeroizing<String>>, pickle_passphrase: Option<Zeroizing<String>>,
} }
struct AccountInfo {
account_id: i64,
identity_keys: Arc<IdentityKeys>,
}
static DATABASE_NAME: &str = "matrix-sdk-crypto.db"; static DATABASE_NAME: &str = "matrix-sdk-crypto.db";
impl SqliteStore { impl SqliteStore {
@ -118,8 +124,8 @@ impl SqliteStore {
let connection = SqliteConnection::connect(url.as_ref()).await?; let connection = SqliteConnection::connect(url.as_ref()).await?;
let store = SqliteStore { let store = SqliteStore {
user_id: Arc::new(user_id.to_owned()), user_id: Arc::new(user_id.to_owned()),
device_id: Arc::new(device_id.to_owned()), device_id: Arc::new(device_id.into()),
account_id: None, account_info: None,
sessions: SessionStore::new(), sessions: SessionStore::new(),
inbound_group_sessions: GroupSessionStore::new(), inbound_group_sessions: GroupSessionStore::new(),
devices: DeviceStore::new(), devices: DeviceStore::new(),
@ -133,6 +139,10 @@ impl SqliteStore {
Ok(store) Ok(store)
} }
fn account_id(&self) -> Option<i64> {
self.account_info.as_ref().map(|i| i.account_id)
}
async fn create_tables(&self) -> Result<()> { async fn create_tables(&self) -> Result<()> {
let mut connection = self.connection.lock().await; let mut connection = self.connection.lock().await;
connection connection
@ -288,14 +298,17 @@ impl SqliteStore {
} }
async fn load_sessions_for(&mut self, sender_key: &str) -> Result<Vec<Session>> { async fn load_sessions_for(&mut self, sender_key: &str) -> Result<Vec<Session>> {
let account_id = self.account_id.ok_or(CryptoStoreError::AccountUnset)?; let account_info = self
.account_info
.as_ref()
.ok_or(CryptoStoreError::AccountUnset)?;
let mut connection = self.connection.lock().await; let mut connection = self.connection.lock().await;
let rows: Vec<(String, String, String, String)> = query_as( let rows: Vec<(String, String, String, String)> = query_as(
"SELECT pickle, sender_key, creation_time, last_use_time "SELECT pickle, sender_key, creation_time, last_use_time
FROM sessions WHERE account_id = ? and sender_key = ?", FROM sessions WHERE account_id = ? and sender_key = ?",
) )
.bind(account_id) .bind(account_info.account_id)
.bind(sender_key) .bind(sender_key)
.fetch_all(&mut *connection) .fetch_all(&mut *connection)
.await?; .await?;
@ -315,6 +328,9 @@ impl SqliteStore {
.ok_or(CryptoStoreError::SessionTimestampError)?; .ok_or(CryptoStoreError::SessionTimestampError)?;
Ok(Session::from_pickle( Ok(Session::from_pickle(
self.user_id.clone(),
self.device_id.clone(),
account_info.identity_keys.clone(),
pickle.to_string(), pickle.to_string(),
self.get_pickle_mode(), self.get_pickle_mode(),
sender_key.to_string(), sender_key.to_string(),
@ -326,7 +342,7 @@ impl SqliteStore {
} }
async fn load_inbound_group_sessions(&self) -> Result<Vec<InboundGroupSession>> { async fn load_inbound_group_sessions(&self) -> Result<Vec<InboundGroupSession>> {
let account_id = self.account_id.ok_or(CryptoStoreError::AccountUnset)?; let account_id = self.account_id().ok_or(CryptoStoreError::AccountUnset)?;
let mut connection = self.connection.lock().await; let mut connection = self.connection.lock().await;
let rows: Vec<(String, String, String, String)> = query_as( let rows: Vec<(String, String, String, String)> = query_as(
@ -357,7 +373,7 @@ impl SqliteStore {
} }
async fn save_tracked_user(&self, user: &UserId, dirty: bool) -> Result<()> { async fn save_tracked_user(&self, user: &UserId, dirty: bool) -> Result<()> {
let account_id = self.account_id.ok_or(CryptoStoreError::AccountUnset)?; let account_id = self.account_id().ok_or(CryptoStoreError::AccountUnset)?;
let mut connection = self.connection.lock().await; let mut connection = self.connection.lock().await;
query( query(
@ -378,7 +394,7 @@ impl SqliteStore {
} }
async fn load_tracked_users(&self) -> Result<(HashSet<UserId>, HashSet<UserId>)> { async fn load_tracked_users(&self) -> Result<(HashSet<UserId>, HashSet<UserId>)> {
let account_id = self.account_id.ok_or(CryptoStoreError::AccountUnset)?; let account_id = self.account_id().ok_or(CryptoStoreError::AccountUnset)?;
let mut connection = self.connection.lock().await; let mut connection = self.connection.lock().await;
let rows: Vec<(String, bool)> = query_as( let rows: Vec<(String, bool)> = query_as(
@ -410,7 +426,7 @@ impl SqliteStore {
} }
async fn load_devices(&self) -> Result<DeviceStore> { async fn load_devices(&self) -> Result<DeviceStore> {
let account_id = self.account_id.ok_or(CryptoStoreError::AccountUnset)?; let account_id = self.account_id().ok_or(CryptoStoreError::AccountUnset)?;
let mut connection = self.connection.lock().await; let mut connection = self.connection.lock().await;
let rows: Vec<(i64, String, String, Option<String>, i64)> = query_as( let rows: Vec<(i64, String, String, Option<String>, i64)> = query_as(
@ -490,7 +506,7 @@ impl SqliteStore {
} }
async fn save_device_helper(&self, device: Device) -> Result<()> { async fn save_device_helper(&self, device: Device) -> Result<()> {
let account_id = self.account_id.ok_or(CryptoStoreError::AccountUnset)?; let account_id = self.account_id().ok_or(CryptoStoreError::AccountUnset)?;
let mut connection = self.connection.lock().await; let mut connection = self.connection.lock().await;
@ -573,20 +589,26 @@ impl CryptoStore for SqliteStore {
WHERE user_id = ? and device_id = ?", WHERE user_id = ? and device_id = ?",
) )
.bind(self.user_id.as_str()) .bind(self.user_id.as_str())
.bind(&*self.device_id) .bind((&*self.device_id).as_ref())
.fetch_optional(&mut *connection) .fetch_optional(&mut *connection)
.await?; .await?;
let result = if let Some((id, pickle, shared, uploaded_key_count)) = row { let result = if let Some((id, pickle, shared, uploaded_key_count)) = row {
self.account_id = Some(id); let account = Account::from_pickle(
Some(Account::from_pickle(
pickle, pickle,
self.get_pickle_mode(), self.get_pickle_mode(),
shared, shared,
uploaded_key_count, uploaded_key_count,
&self.user_id, &self.user_id,
&self.device_id, &self.device_id,
)?) )?;
self.account_info = Some(AccountInfo {
account_id: id,
identity_keys: account.identity_keys.clone(),
});
Some(account)
} else { } else {
return Ok(None); return Ok(None);
}; };
@ -640,19 +662,22 @@ impl CryptoStore for SqliteStore {
.fetch_one(&mut *connection) .fetch_one(&mut *connection)
.await?; .await?;
self.account_id = Some(account_id.0); self.account_info = Some(AccountInfo {
account_id: account_id.0,
identity_keys: account.identity_keys.clone(),
});
Ok(()) Ok(())
} }
async fn save_sessions(&mut self, sessions: &[Session]) -> Result<()> { async fn save_sessions(&mut self, sessions: &[Session]) -> Result<()> {
let account_id = self.account_id().ok_or(CryptoStoreError::AccountUnset)?;
// TODO turn this into a transaction // TODO turn this into a transaction
for session in sessions { for session in sessions {
self.lazy_load_sessions(&session.sender_key).await?; self.lazy_load_sessions(&session.sender_key).await?;
self.sessions.add(session.clone()).await; self.sessions.add(session.clone()).await;
let account_id = self.account_id.ok_or(CryptoStoreError::AccountUnset)?;
let session_id = session.session_id(); let session_id = session.session_id();
let creation_time = serde_json::to_string(&session.creation_time.elapsed())?; let creation_time = serde_json::to_string(&session.creation_time.elapsed())?;
let last_use_time = serde_json::to_string(&session.last_use_time.elapsed())?; let last_use_time = serde_json::to_string(&session.last_use_time.elapsed())?;
@ -683,7 +708,7 @@ impl CryptoStore for SqliteStore {
} }
async fn save_inbound_group_session(&mut self, session: InboundGroupSession) -> Result<bool> { async fn save_inbound_group_session(&mut self, session: InboundGroupSession) -> Result<bool> {
let account_id = self.account_id.ok_or(CryptoStoreError::AccountUnset)?; let account_id = self.account_id().ok_or(CryptoStoreError::AccountUnset)?;
let pickle = session.pickle(self.get_pickle_mode()).await; let pickle = session.pickle(self.get_pickle_mode()).await;
let mut connection = self.connection.lock().await; let mut connection = self.connection.lock().await;
let session_id = session.session_id(); let session_id = session.session_id();
@ -753,7 +778,7 @@ impl CryptoStore for SqliteStore {
} }
async fn delete_device(&self, device: Device) -> Result<()> { async fn delete_device(&self, device: Device) -> Result<()> {
let account_id = self.account_id.ok_or(CryptoStoreError::AccountUnset)?; let account_id = self.account_id().ok_or(CryptoStoreError::AccountUnset)?;
let mut connection = self.connection.lock().await; let mut connection = self.connection.lock().await;
query( query(