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

This commit is contained in:
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 machine::{OlmMachine, OneTimeKeys};
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")]
pub use store::sqlite::SqliteStore;
pub use store::{CryptoStore, CryptoStoreError};

View file

@ -52,7 +52,7 @@ pub struct Account {
pub(crate) user_id: Arc<UserId>,
pub(crate) device_id: Arc<Box<DeviceId>>,
inner: Arc<Mutex<OlmAccount>>,
identity_keys: Arc<IdentityKeys>,
pub(crate) identity_keys: Arc<IdentityKeys>,
shared: Arc<AtomicBool>,
/// 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
@ -395,6 +395,9 @@ impl Account {
let session_id = session.session_id();
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)),
session_id: Arc::new(session_id),
sender_key: Arc::new(their_identity_key.to_owned()),
@ -495,6 +498,9 @@ impl Account {
let session_id = session.session_id();
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)),
session_id: Arc::new(session_id),
sender_key: Arc::new(their_identity_key.to_owned()),

View file

@ -27,7 +27,7 @@ pub use olm_rs::{
utility::OlmUtility,
};
use super::Account;
use super::{Account, IdentityKeys};
use crate::error::{EventError, OlmResult};
use crate::Device;
@ -37,6 +37,7 @@ use matrix_sdk_common::{
room::encrypted::{CiphertextInfo, EncryptedEventContent, OlmV1Curve25519AesSha2Content},
EventType,
},
identifiers::{DeviceId, UserId},
instant::Instant,
locks::Mutex,
};
@ -45,6 +46,9 @@ use matrix_sdk_common::{
/// `Account`s
#[derive(Clone)]
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) session_id: 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 used to encrypt or decrypt an Olm message.
pub fn from_pickle(
user_id: Arc<UserId>,
device_id: Arc<Box<DeviceId>>,
our_identity_keys: Arc<IdentityKeys>,
pickle: String,
pickle_mode: PicklingMode,
sender_key: String,
@ -207,6 +214,9 @@ impl Session {
let session_id = session.session_id();
Ok(Session {
user_id,
device_id,
our_identity_keys,
inner: Arc::new(Mutex::new(session)),
session_id: Arc::new(session_id),
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 zeroize::Zeroizing;
use super::{Account, CryptoStore, CryptoStoreError, InboundGroupSession, Result, Session};
use super::{CryptoStore, CryptoStoreError, Result};
use crate::device::{Device, TrustState};
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::events::Algorithm;
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`.
pub struct SqliteStore {
user_id: Arc<UserId>,
device_id: Arc<String>,
account_id: Option<i64>,
device_id: Arc<Box<DeviceId>>,
account_info: Option<AccountInfo>,
path: PathBuf,
sessions: SessionStore,
@ -50,6 +51,11 @@ pub struct SqliteStore {
pickle_passphrase: Option<Zeroizing<String>>,
}
struct AccountInfo {
account_id: i64,
identity_keys: Arc<IdentityKeys>,
}
static DATABASE_NAME: &str = "matrix-sdk-crypto.db";
impl SqliteStore {
@ -118,8 +124,8 @@ impl SqliteStore {
let connection = SqliteConnection::connect(url.as_ref()).await?;
let store = SqliteStore {
user_id: Arc::new(user_id.to_owned()),
device_id: Arc::new(device_id.to_owned()),
account_id: None,
device_id: Arc::new(device_id.into()),
account_info: None,
sessions: SessionStore::new(),
inbound_group_sessions: GroupSessionStore::new(),
devices: DeviceStore::new(),
@ -133,6 +139,10 @@ impl SqliteStore {
Ok(store)
}
fn account_id(&self) -> Option<i64> {
self.account_info.as_ref().map(|i| i.account_id)
}
async fn create_tables(&self) -> Result<()> {
let mut connection = self.connection.lock().await;
connection
@ -288,14 +298,17 @@ impl SqliteStore {
}
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 rows: Vec<(String, String, String, String)> = query_as(
"SELECT pickle, sender_key, creation_time, last_use_time
FROM sessions WHERE account_id = ? and sender_key = ?",
)
.bind(account_id)
.bind(account_info.account_id)
.bind(sender_key)
.fetch_all(&mut *connection)
.await?;
@ -315,6 +328,9 @@ impl SqliteStore {
.ok_or(CryptoStoreError::SessionTimestampError)?;
Ok(Session::from_pickle(
self.user_id.clone(),
self.device_id.clone(),
account_info.identity_keys.clone(),
pickle.to_string(),
self.get_pickle_mode(),
sender_key.to_string(),
@ -326,7 +342,7 @@ impl SqliteStore {
}
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 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<()> {
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;
query(
@ -378,7 +394,7 @@ impl SqliteStore {
}
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 rows: Vec<(String, bool)> = query_as(
@ -410,7 +426,7 @@ impl SqliteStore {
}
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 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<()> {
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;
@ -573,20 +589,26 @@ impl CryptoStore for SqliteStore {
WHERE user_id = ? and device_id = ?",
)
.bind(self.user_id.as_str())
.bind(&*self.device_id)
.bind((&*self.device_id).as_ref())
.fetch_optional(&mut *connection)
.await?;
let result = if let Some((id, pickle, shared, uploaded_key_count)) = row {
self.account_id = Some(id);
Some(Account::from_pickle(
let account = Account::from_pickle(
pickle,
self.get_pickle_mode(),
shared,
uploaded_key_count,
&self.user_id,
&self.device_id,
)?)
)?;
self.account_info = Some(AccountInfo {
account_id: id,
identity_keys: account.identity_keys.clone(),
});
Some(account)
} else {
return Ok(None);
};
@ -640,19 +662,22 @@ impl CryptoStore for SqliteStore {
.fetch_one(&mut *connection)
.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(())
}
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
for session in sessions {
self.lazy_load_sessions(&session.sender_key).await?;
self.sessions.add(session.clone()).await;
let account_id = self.account_id.ok_or(CryptoStoreError::AccountUnset)?;
let session_id = session.session_id();
let creation_time = serde_json::to_string(&session.creation_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> {
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 mut connection = self.connection.lock().await;
let session_id = session.session_id();
@ -753,7 +778,7 @@ impl CryptoStore for SqliteStore {
}
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;
query(