crypto: Modify all the pickling logic so we return serializeable structs.

master
Damir Jelić 2020-09-02 11:45:35 +02:00
parent 269cfc3d34
commit 81b127b6e7
5 changed files with 155 additions and 74 deletions

View File

@ -67,9 +67,9 @@ pub struct Account {
uploaded_signed_key_count: Arc<AtomicI64>, uploaded_signed_key_count: Arc<AtomicI64>,
} }
#[derive(Debug, Clone, Serialize, Deserialize)]
/// A typed representation of a base64 encoded string containing the account /// A typed representation of a base64 encoded string containing the account
/// pickle. /// pickle.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AccountPickle(String); pub struct AccountPickle(String);
impl AccountPickle { impl AccountPickle {
@ -85,11 +85,11 @@ impl From<String> for AccountPickle {
} }
} }
#[derive(Debug, Clone, Serialize, Deserialize)]
/// A pickled version of an `Account`. /// A pickled version of an `Account`.
/// ///
/// Holds all the information that needs to be stored in a database to restore /// Holds all the information that needs to be stored in a database to restore
/// an account. /// an account.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PickledAccount { pub struct PickledAccount {
/// The user id of the account owner. /// The user id of the account owner.
pub user_id: UserId, pub user_id: UserId,

View File

@ -37,7 +37,7 @@ use olm_rs::{
errors::OlmGroupSessionError, inbound_group_session::OlmInboundGroupSession, errors::OlmGroupSessionError, inbound_group_session::OlmInboundGroupSession,
outbound_group_session::OlmOutboundGroupSession, PicklingMode, outbound_group_session::OlmOutboundGroupSession, PicklingMode,
}; };
use serde::Serialize; use serde::{Deserialize, Serialize};
use serde_json::{json, Value}; use serde_json::{json, Value};
use zeroize::Zeroize; use zeroize::Zeroize;
@ -154,8 +154,15 @@ impl InboundGroupSession {
/// ///
/// * `pickle_mode` - The mode that was used to pickle the group session, /// * `pickle_mode` - The mode that was used to pickle the group session,
/// either an unencrypted mode or an encrypted using passphrase. /// either an unencrypted mode or an encrypted using passphrase.
pub async fn pickle(&self, pickle_mode: PicklingMode) -> String { pub async fn pickle(&self, pickle_mode: PicklingMode) -> PickledInboundGroupSession {
self.inner.lock().await.pickle(pickle_mode) let pickle = self.inner.lock().await.pickle(pickle_mode);
PickledInboundGroupSession {
pickle: InboundGroupSessionPickle::from(pickle),
sender_key: self.sender_key.to_string(),
signing_key: self.signing_key.to_string(),
room_id: (&*self.room_id).clone(),
}
} }
/// Restore a Session from a previously pickled string. /// Restore a Session from a previously pickled string.
@ -178,21 +185,18 @@ impl InboundGroupSession {
/// ///
/// * `room_id` - The id of the room that the session is used in. /// * `room_id` - The id of the room that the session is used in.
pub fn from_pickle( pub fn from_pickle(
pickle: String, pickle: PickledInboundGroupSession,
pickle_mode: PicklingMode, pickle_mode: PicklingMode,
sender_key: String,
signing_key: String,
room_id: RoomId,
) -> Result<Self, OlmGroupSessionError> { ) -> Result<Self, OlmGroupSessionError> {
let session = OlmInboundGroupSession::unpickle(pickle, pickle_mode)?; let session = OlmInboundGroupSession::unpickle(pickle.pickle.0, pickle_mode)?;
let session_id = session.session_id(); let session_id = session.session_id();
Ok(InboundGroupSession { Ok(InboundGroupSession {
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(pickle.sender_key),
signing_key: Arc::new(signing_key), signing_key: Arc::new(pickle.signing_key),
room_id: Arc::new(room_id), room_id: Arc::new(pickle.room_id),
forwarding_chains: Arc::new(Mutex::new(None)), forwarding_chains: Arc::new(Mutex::new(None)),
}) })
} }
@ -282,6 +286,39 @@ impl PartialEq for InboundGroupSession {
} }
} }
/// A pickled version of an `InboundGroupSession`.
///
/// Holds all the information that needs to be stored in a database to restore
/// an InboundGroupSession.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PickledInboundGroupSession {
/// The pickle string holding the InboundGroupSession.
pub pickle: InboundGroupSessionPickle,
/// The public curve25519 key of the account that sent us the session
pub sender_key: String,
/// The public ed25519 key of the account that sent us the session.
pub signing_key: String,
/// The id of the room that the session is used in.
pub room_id: RoomId,
}
/// The typed representation of a base64 encoded string of the GroupSession pickle.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct InboundGroupSessionPickle(String);
impl From<String> for InboundGroupSessionPickle {
fn from(pickle_string: String) -> Self {
InboundGroupSessionPickle(pickle_string)
}
}
impl InboundGroupSessionPickle {
/// Get the string representation of the pickle.
pub fn as_str(&self) -> &str {
&self.0
}
}
/// Outbound group session. /// Outbound group session.
/// ///
/// Outbound group sessions are used to exchange room messages between a group /// Outbound group sessions are used to exchange room messages between a group

View File

@ -22,11 +22,13 @@ mod group_sessions;
mod session; mod session;
pub use account::{Account, AccountPickle, IdentityKeys, PickledAccount}; pub use account::{Account, AccountPickle, IdentityKeys, PickledAccount};
pub use group_sessions::{EncryptionSettings, InboundGroupSession}; pub use group_sessions::{
EncryptionSettings, InboundGroupSession, InboundGroupSessionPickle, PickledInboundGroupSession,
};
pub(crate) use group_sessions::{GroupSessionKey, OutboundGroupSession}; pub(crate) use group_sessions::{GroupSessionKey, OutboundGroupSession};
pub use olm_rs::PicklingMode; pub use olm_rs::PicklingMode;
pub(crate) use session::OlmMessage; pub(crate) use session::OlmMessage;
pub use session::Session; pub use session::{PickledSession, Session, SessionPickle};
#[cfg(test)] #[cfg(test)]
pub(crate) mod test { pub(crate) mod test {

View File

@ -20,10 +20,11 @@ use matrix_sdk_common::{
EventType, EventType,
}, },
identifiers::{DeviceId, DeviceKeyAlgorithm, UserId}, identifiers::{DeviceId, DeviceKeyAlgorithm, UserId},
instant::Instant, instant::{Duration, Instant},
locks::Mutex, locks::Mutex,
}; };
use olm_rs::{errors::OlmSessionError, session::OlmSession, PicklingMode}; use olm_rs::{errors::OlmSessionError, session::OlmSession, PicklingMode};
use serde::{Deserialize, Serialize};
use serde_json::{json, Value}; use serde_json::{json, Value};
use super::IdentityKeys; use super::IdentityKeys;
@ -177,8 +178,16 @@ impl Session {
/// ///
/// * `pickle_mode` - The mode that was used to pickle the session, either /// * `pickle_mode` - The mode that was used to pickle the session, either
/// an unencrypted mode or an encrypted using passphrase. /// an unencrypted mode or an encrypted using passphrase.
pub async fn pickle(&self, pickle_mode: PicklingMode) -> String { pub async fn pickle(&self, pickle_mode: PicklingMode) -> PickledSession {
self.inner.lock().await.pickle(pickle_mode) let pickle = self.inner.lock().await.pickle(pickle_mode);
PickledSession {
pickle: SessionPickle::from(pickle),
sender_key: self.sender_key.to_string(),
// FIXME this should use the duration from the unix epoch.
creation_time: self.creation_time.elapsed(),
last_use_time: self.last_use_time.elapsed(),
}
} }
/// Restore a Session from a previously pickled string. /// Restore a Session from a previously pickled string.
@ -194,40 +203,35 @@ impl Session {
/// ///
/// * `our_idenity_keys` - An clone of the Arc to our own identity keys. /// * `our_idenity_keys` - An clone of the Arc to our own identity keys.
/// ///
/// * `pickle` - The pickled string of the session. /// * `pickle` - The pickled version of the `Session`.
/// ///
/// * `pickle_mode` - The mode that was used to pickle the session, either /// * `pickle_mode` - The mode that was used to pickle the session, either
/// an unencrypted mode or an encrypted using passphrase. /// an unencrypted mode or an encrypted using passphrase.
///
/// * `sender_key` - The public curve25519 key of the account that
/// established the session with us.
///
/// * `creation_time` - The timestamp that marks when the session was
/// created.
///
/// * `last_use_time` - The timestamp that marks when the session was
/// last used to encrypt or decrypt an Olm message.
#[allow(clippy::too_many_arguments)]
pub fn from_pickle( pub fn from_pickle(
user_id: Arc<UserId>, user_id: Arc<UserId>,
device_id: Arc<Box<DeviceId>>, device_id: Arc<Box<DeviceId>>,
our_identity_keys: Arc<IdentityKeys>, our_identity_keys: Arc<IdentityKeys>,
pickle: String, pickle: PickledSession,
pickle_mode: PicklingMode, pickle_mode: PicklingMode,
sender_key: String,
creation_time: Instant,
last_use_time: Instant,
) -> Result<Self, OlmSessionError> { ) -> Result<Self, OlmSessionError> {
let session = OlmSession::unpickle(pickle, pickle_mode)?; let session = OlmSession::unpickle(pickle.pickle.0, pickle_mode)?;
let session_id = session.session_id(); let session_id = session.session_id();
// FIXME this should use the UNIX epoch.
let now = Instant::now();
let creation_time = now.checked_sub(pickle.creation_time).unwrap();
// .ok_or(CryptoStoreError::SessionTimestampError)?;
let last_use_time = now.checked_sub(pickle.last_use_time).unwrap();
// .ok_or(CryptoStoreError::SessionTimestampError)?;
Ok(Session { Ok(Session {
user_id, user_id,
device_id, device_id,
our_identity_keys, 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(pickle.sender_key),
creation_time: Arc::new(creation_time), creation_time: Arc::new(creation_time),
last_use_time: Arc::new(last_use_time), last_use_time: Arc::new(last_use_time),
}) })
@ -239,3 +243,36 @@ impl PartialEq for Session {
self.session_id() == other.session_id() self.session_id() == other.session_id()
} }
} }
/// A pickled version of a `Session`.
///
/// Holds all the information that needs to be stored in a database to restore
/// a Session.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PickledSession {
/// The pickle string holding the Olm Session.
pub pickle: SessionPickle,
/// The curve25519 key of the other user that we share this session with.
pub sender_key: String,
/// The relative time elapsed since the session was created.
pub creation_time: Duration,
/// The relative time elapsed since the session was last used.
pub last_use_time: Duration,
}
/// The typed representation of a base64 encoded string of the Olm Session pickle.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SessionPickle(String);
impl From<String> for SessionPickle {
fn from(picle_string: String) -> Self {
SessionPickle(picle_string)
}
}
impl SessionPickle {
/// Get the string representation of the pickle.
pub fn as_str(&self) -> &str {
&self.0
}
}

View File

@ -26,7 +26,7 @@ use matrix_sdk_common::{
identifiers::{ identifiers::{
DeviceId, DeviceKeyAlgorithm, DeviceKeyId, EventEncryptionAlgorithm, RoomId, UserId, DeviceId, DeviceKeyAlgorithm, DeviceKeyId, EventEncryptionAlgorithm, RoomId, UserId,
}, },
instant::{Duration, Instant}, instant::Duration,
locks::Mutex, locks::Mutex,
}; };
use sqlx::{query, query_as, sqlite::SqliteQueryAs, Connect, Executor, SqliteConnection}; use sqlx::{query, query_as, sqlite::SqliteQueryAs, Connect, Executor, SqliteConnection};
@ -38,14 +38,15 @@ use crate::{
device::{LocalTrust, ReadOnlyDevice}, device::{LocalTrust, ReadOnlyDevice},
memory_stores::{DeviceStore, GroupSessionStore, ReadOnlyUserDevices, SessionStore}, memory_stores::{DeviceStore, GroupSessionStore, ReadOnlyUserDevices, SessionStore},
olm::{ olm::{
Account, AccountPickle, IdentityKeys, InboundGroupSession, PickledAccount, PicklingMode, Account, AccountPickle, IdentityKeys, InboundGroupSession, InboundGroupSessionPickle,
Session, PickledAccount, PickledInboundGroupSession, PickledSession, PicklingMode, Session,
SessionPickle,
}, },
user_identity::UserIdentities, user_identity::UserIdentities,
}; };
/// SQLite based implementation of a `CryptoStore`.
#[derive(Clone)] #[derive(Clone)]
/// SQLite based implementation of a `CryptoStore`.
pub struct SqliteStore { pub struct SqliteStore {
user_id: Arc<UserId>, user_id: Arc<UserId>,
device_id: Arc<Box<DeviceId>>, device_id: Arc<Box<DeviceId>>,
@ -338,7 +339,7 @@ impl SqliteStore {
.ok_or(CryptoStoreError::AccountUnset)?; .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 mut 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 = ?",
) )
@ -347,29 +348,27 @@ impl SqliteStore {
.fetch_all(&mut *connection) .fetch_all(&mut *connection)
.await?; .await?;
let now = Instant::now();
Ok(rows Ok(rows
.iter() .drain(..)
.map(|row| { .map(|row| {
let pickle = &row.0; let pickle = row.0;
let sender_key = &row.1; let sender_key = row.1;
let creation_time = now let creation_time = serde_json::from_str::<Duration>(&row.2)?;
.checked_sub(serde_json::from_str::<Duration>(&row.2)?) let last_use_time = serde_json::from_str::<Duration>(&row.3)?;
.ok_or(CryptoStoreError::SessionTimestampError)?;
let last_use_time = now let pickle = PickledSession {
.checked_sub(serde_json::from_str::<Duration>(&row.3)?) pickle: SessionPickle::from(pickle),
.ok_or(CryptoStoreError::SessionTimestampError)?; last_use_time,
creation_time,
sender_key,
};
Ok(Session::from_pickle( Ok(Session::from_pickle(
self.user_id.clone(), self.user_id.clone(),
self.device_id.clone(), self.device_id.clone(),
account_info.identity_keys.clone(), account_info.identity_keys.clone(),
pickle.to_string(), pickle,
self.get_pickle_mode(), self.get_pickle_mode(),
sender_key.to_string(),
creation_time,
last_use_time,
)?) )?)
}) })
.collect::<Result<Vec<Session>>>()?) .collect::<Result<Vec<Session>>>()?)
@ -379,7 +378,7 @@ impl SqliteStore {
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 mut rows: Vec<(String, String, String, String)> = query_as(
"SELECT pickle, sender_key, signing_key, room_id "SELECT pickle, sender_key, signing_key, room_id
FROM inbound_group_sessions WHERE account_id = ?", FROM inbound_group_sessions WHERE account_id = ?",
) )
@ -388,19 +387,24 @@ impl SqliteStore {
.await?; .await?;
let mut group_sessions = rows let mut group_sessions = rows
.iter() .drain(..)
.map(|row| { .map(|row| {
let pickle = &row.0; let pickle = row.0;
let sender_key = &row.1; let sender_key = row.1;
let signing_key = &row.2; let signing_key = row.2;
let room_id = &row.3; let room_id = row.3;
let pickle = PickledInboundGroupSession {
pickle: InboundGroupSessionPickle::from(pickle),
sender_key,
signing_key,
// FIXME remove this unwrap.
room_id: RoomId::try_from(room_id).unwrap(),
};
Ok(InboundGroupSession::from_pickle( Ok(InboundGroupSession::from_pickle(
pickle.to_string(), pickle,
self.get_pickle_mode(), self.get_pickle_mode(),
sender_key.to_string(),
signing_key.to_owned(),
RoomId::try_from(room_id.as_str()).unwrap(),
)?) )?)
}) })
.collect::<Result<Vec<InboundGroupSession>>>()?; .collect::<Result<Vec<InboundGroupSession>>>()?;
@ -754,11 +758,12 @@ impl CryptoStore for SqliteStore {
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 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())?;
let pickle = session.pickle(self.get_pickle_mode()).await; let pickle = session.pickle(self.get_pickle_mode()).await;
let session_id = session.session_id();
let creation_time = serde_json::to_string(&pickle.creation_time)?;
let last_use_time = serde_json::to_string(&pickle.last_use_time)?;
let mut connection = self.connection.lock().await; let mut connection = self.connection.lock().await;
query( query(
@ -770,8 +775,8 @@ impl CryptoStore for SqliteStore {
.bind(&account_id) .bind(&account_id)
.bind(&*creation_time) .bind(&*creation_time)
.bind(&*last_use_time) .bind(&*last_use_time)
.bind(&*session.sender_key) .bind(&pickle.sender_key)
.bind(&pickle) .bind(&pickle.pickle.as_str())
.execute(&mut *connection) .execute(&mut *connection)
.await?; .await?;
} }
@ -800,10 +805,10 @@ impl CryptoStore for SqliteStore {
) )
.bind(session_id) .bind(session_id)
.bind(account_id) .bind(account_id)
.bind(&*session.sender_key) .bind(pickle.sender_key)
.bind(&*session.signing_key) .bind(pickle.signing_key)
.bind(&*session.room_id.to_string()) .bind(pickle.room_id.as_str())
.bind(&pickle) .bind(pickle.pickle.as_str())
.execute(&mut *connection) .execute(&mut *connection)
.await?; .await?;