crypto: Add the ability to store group sessions to the sqlite store.
parent
23065a5aa3
commit
85f344c32e
|
@ -37,6 +37,8 @@ pub enum OlmError {
|
||||||
MissingCiphertext,
|
MissingCiphertext,
|
||||||
#[error("decryption failed because the session to decrypt the message is missing")]
|
#[error("decryption failed because the session to decrypt the message is missing")]
|
||||||
MissingSession,
|
MissingSession,
|
||||||
|
#[error("the Encrypted message is missing the signing key of the sender")]
|
||||||
|
MissingSigningKey,
|
||||||
#[error("can't finish Olm Session operation {0}")]
|
#[error("can't finish Olm Session operation {0}")]
|
||||||
OlmSession(#[from] OlmSessionError),
|
OlmSession(#[from] OlmSessionError),
|
||||||
#[error("can't finish Olm Session operation {0}")]
|
#[error("can't finish Olm Session operation {0}")]
|
||||||
|
|
|
@ -524,8 +524,14 @@ impl OlmMachine {
|
||||||
match event.content.algorithm {
|
match event.content.algorithm {
|
||||||
Algorithm::MegolmV1AesSha2 => {
|
Algorithm::MegolmV1AesSha2 => {
|
||||||
// TODO check for all the valid fields.
|
// TODO check for all the valid fields.
|
||||||
|
let signing_key = event
|
||||||
|
.keys
|
||||||
|
.get("ed25519")
|
||||||
|
.ok_or(OlmError::MissingSigningKey)?;
|
||||||
|
|
||||||
let session = InboundGroupSession::new(
|
let session = InboundGroupSession::new(
|
||||||
sender_key,
|
sender_key,
|
||||||
|
signing_key,
|
||||||
&event.content.room_id.to_string(),
|
&event.content.room_id.to_string(),
|
||||||
&event.content.session_key,
|
&event.content.session_key,
|
||||||
)?;
|
)?;
|
||||||
|
|
|
@ -207,6 +207,7 @@ impl PartialEq for Session {
|
||||||
pub struct InboundGroupSession {
|
pub struct InboundGroupSession {
|
||||||
inner: OlmInboundGroupSession,
|
inner: OlmInboundGroupSession,
|
||||||
pub(crate) sender_key: String,
|
pub(crate) sender_key: String,
|
||||||
|
pub(crate) signing_key: String,
|
||||||
pub(crate) room_id: String,
|
pub(crate) room_id: String,
|
||||||
forwarding_chains: Option<Vec<String>>,
|
forwarding_chains: Option<Vec<String>>,
|
||||||
}
|
}
|
||||||
|
@ -214,12 +215,14 @@ pub struct InboundGroupSession {
|
||||||
impl InboundGroupSession {
|
impl InboundGroupSession {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
sender_key: &str,
|
sender_key: &str,
|
||||||
|
signing_key: &str,
|
||||||
room_id: &str,
|
room_id: &str,
|
||||||
session_key: &str,
|
session_key: &str,
|
||||||
) -> Result<Self, OlmGroupSessionError> {
|
) -> Result<Self, OlmGroupSessionError> {
|
||||||
Ok(InboundGroupSession {
|
Ok(InboundGroupSession {
|
||||||
inner: OlmInboundGroupSession::new(session_key)?,
|
inner: OlmInboundGroupSession::new(session_key)?,
|
||||||
sender_key: sender_key.to_owned(),
|
sender_key: sender_key.to_owned(),
|
||||||
|
signing_key: signing_key.to_owned(),
|
||||||
room_id: room_id.to_owned(),
|
room_id: room_id.to_owned(),
|
||||||
forwarding_chains: None,
|
forwarding_chains: None,
|
||||||
})
|
})
|
||||||
|
@ -229,6 +232,10 @@ impl InboundGroupSession {
|
||||||
self.inner.session_id()
|
self.inner.session_id()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn pickle(&self, pickle_mode: PicklingMode) -> String {
|
||||||
|
self.inner.pickle(pickle_mode)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn first_known_index(&self) -> u32 {
|
pub fn first_known_index(&self) -> u32 {
|
||||||
self.inner.first_known_index()
|
self.inner.first_known_index()
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,7 @@ use serde_json::Error as SerdeError;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
|
|
||||||
use super::olm::{Account, Session};
|
use super::olm::{Account, InboundGroupSession, Session};
|
||||||
use olm_rs::errors::{OlmAccountError, OlmSessionError};
|
use olm_rs::errors::{OlmAccountError, OlmSessionError};
|
||||||
use olm_rs::PicklingMode;
|
use olm_rs::PicklingMode;
|
||||||
|
|
||||||
|
|
|
@ -25,8 +25,8 @@ use sqlx::{query, query_as, sqlite::SqliteQueryAs, Connect, Executor, SqliteConn
|
||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
use zeroize::Zeroizing;
|
use zeroize::Zeroizing;
|
||||||
|
|
||||||
use super::{Account, CryptoStore, CryptoStoreError, Result, Session};
|
use super::{Account, CryptoStore, CryptoStoreError, InboundGroupSession, Result, Session};
|
||||||
use crate::crypto::memory_stores::SessionStore;
|
use crate::crypto::memory_stores::{GroupSessionStore, SessionStore};
|
||||||
|
|
||||||
pub struct SqliteStore {
|
pub struct SqliteStore {
|
||||||
user_id: Arc<String>,
|
user_id: Arc<String>,
|
||||||
|
@ -34,6 +34,7 @@ pub struct SqliteStore {
|
||||||
account_id: Option<i64>,
|
account_id: Option<i64>,
|
||||||
path: PathBuf,
|
path: PathBuf,
|
||||||
sessions: SessionStore,
|
sessions: SessionStore,
|
||||||
|
inbound_group_sessions: GroupSessionStore,
|
||||||
connection: Arc<Mutex<SqliteConnection>>,
|
connection: Arc<Mutex<SqliteConnection>>,
|
||||||
pickle_passphrase: Option<Zeroizing<String>>,
|
pickle_passphrase: Option<Zeroizing<String>>,
|
||||||
}
|
}
|
||||||
|
@ -78,6 +79,7 @@ impl SqliteStore {
|
||||||
device_id: Arc::new(device_id.to_owned()),
|
device_id: Arc::new(device_id.to_owned()),
|
||||||
account_id: None,
|
account_id: None,
|
||||||
sessions: SessionStore::new(),
|
sessions: SessionStore::new(),
|
||||||
|
inbound_group_sessions: GroupSessionStore::new(),
|
||||||
path: path.as_ref().to_owned(),
|
path: path.as_ref().to_owned(),
|
||||||
connection: Arc::new(Mutex::new(connection)),
|
connection: Arc::new(Mutex::new(connection)),
|
||||||
pickle_passphrase: passphrase,
|
pickle_passphrase: passphrase,
|
||||||
|
@ -122,6 +124,25 @@ impl SqliteStore {
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
connection
|
||||||
|
.execute(
|
||||||
|
r#"
|
||||||
|
CREATE TABLE IF NOT EXISTS inbound_group_sessions (
|
||||||
|
"session_id" TEXT NOT NULL PRIMARY KEY,
|
||||||
|
"account_id" INTEGER NOT NULL,
|
||||||
|
"sender_key" TEXT NOT NULL,
|
||||||
|
"signing_key" TEXT NOT NULL,
|
||||||
|
"room_id" TEXT NOT NULL,
|
||||||
|
"pickle" BLOB NOT NULL,
|
||||||
|
FOREIGN KEY ("account_id") REFERENCES "accounts" ("id")
|
||||||
|
ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX "olm_groups_sessions_account_id" ON "inbound_group_sessions" ("account_id");
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -180,6 +201,34 @@ impl SqliteStore {
|
||||||
.collect::<Result<Vec<Arc<Mutex<Session>>>>>()?)
|
.collect::<Result<Vec<Arc<Mutex<Session>>>>>()?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn save_inbound_group_session(&mut self, session: InboundGroupSession) -> Result<()> {
|
||||||
|
let account_id = self.account_id.ok_or(CryptoStoreError::AccountUnset)?;
|
||||||
|
let pickle = session.pickle(self.get_pickle_mode());
|
||||||
|
let mut connection = self.connection.lock().await;
|
||||||
|
|
||||||
|
query(
|
||||||
|
"INSERT INTO inbound_group_sessions (
|
||||||
|
session_id, account_id, sender_key, signing_key,
|
||||||
|
room_id, pickle
|
||||||
|
) VALUES (?1, ?2, ?3, ?4, ?5, ?6)
|
||||||
|
ON CONFLICT(session_id) DO UPDATE SET
|
||||||
|
pickle = ?6
|
||||||
|
WHERE session_id = ?1
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.bind(&session.session_id())
|
||||||
|
.bind(account_id)
|
||||||
|
.bind(&session.sender_key)
|
||||||
|
.bind(&session.signing_key)
|
||||||
|
.bind(&session.room_id)
|
||||||
|
.bind(&pickle)
|
||||||
|
.execute(&mut *connection)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
self.inbound_group_sessions.add(session);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn get_pickle_mode(&self) -> PicklingMode {
|
fn get_pickle_mode(&self) -> PicklingMode {
|
||||||
match &self.pickle_passphrase {
|
match &self.pickle_passphrase {
|
||||||
Some(p) => PicklingMode::Encrypted {
|
Some(p) => PicklingMode::Encrypted {
|
||||||
|
@ -303,11 +352,12 @@ impl std::fmt::Debug for SqliteStore {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
|
use olm_rs::outbound_group_session::OlmOutboundGroupSession;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use tempfile::tempdir;
|
use tempfile::tempdir;
|
||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
|
|
||||||
use super::{Account, CryptoStore, Session, SqliteStore};
|
use super::{Account, CryptoStore, InboundGroupSession, Session, SqliteStore};
|
||||||
|
|
||||||
static USER_ID: &str = "@example:localhost";
|
static USER_ID: &str = "@example:localhost";
|
||||||
static DEVICE_ID: &str = "DEVICEID";
|
static DEVICE_ID: &str = "DEVICEID";
|
||||||
|
@ -443,4 +493,31 @@ mod test {
|
||||||
|
|
||||||
assert_eq!(*sess, *loaded_session.lock().await);
|
assert_eq!(*sess, *loaded_session.lock().await);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn save_inbound_group_session() {
|
||||||
|
let mut store = get_store().await;
|
||||||
|
let account = get_account();
|
||||||
|
|
||||||
|
store
|
||||||
|
.save_account(account.clone())
|
||||||
|
.await
|
||||||
|
.expect("Can't save account");
|
||||||
|
|
||||||
|
let acc = account.lock().await;
|
||||||
|
let identity_keys = acc.identity_keys();
|
||||||
|
let outbound_session = OlmOutboundGroupSession::new();
|
||||||
|
let session = InboundGroupSession::new(
|
||||||
|
identity_keys.curve25519(),
|
||||||
|
identity_keys.ed25519(),
|
||||||
|
"!test:localhost",
|
||||||
|
&outbound_session.session_key(),
|
||||||
|
)
|
||||||
|
.expect("Can't create session");
|
||||||
|
|
||||||
|
store
|
||||||
|
.save_inbound_group_session(session)
|
||||||
|
.await
|
||||||
|
.expect("Can't save group session");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue