crypto: Correctly store the ed25519 key map for inbound group sessions.

master
Damir Jelić 2020-09-09 15:03:19 +02:00
parent aff1e1d0a8
commit 3e9b0a8e7f
2 changed files with 107 additions and 47 deletions

View File

@ -49,9 +49,10 @@ pub struct InboundGroupSession {
inner: Arc<Mutex<OlmInboundGroupSession>>, inner: Arc<Mutex<OlmInboundGroupSession>>,
session_id: Arc<String>, session_id: Arc<String>,
pub(crate) sender_key: Arc<String>, pub(crate) sender_key: Arc<String>,
pub(crate) signing_key: Arc<String>, pub(crate) signing_key: Arc<BTreeMap<DeviceKeyAlgorithm, String>>,
pub(crate) room_id: Arc<RoomId>, pub(crate) room_id: Arc<RoomId>,
forwarding_chains: Arc<Mutex<Option<Vec<String>>>>, forwarding_chains: Arc<Mutex<Option<Vec<String>>>>,
imported: Arc<bool>,
} }
impl InboundGroupSession { impl InboundGroupSession {
@ -80,13 +81,17 @@ impl InboundGroupSession {
let session = OlmInboundGroupSession::new(&session_key.0)?; let session = OlmInboundGroupSession::new(&session_key.0)?;
let session_id = session.session_id(); let session_id = session.session_id();
let mut keys: BTreeMap<DeviceKeyAlgorithm, String> = BTreeMap::new();
keys.insert(DeviceKeyAlgorithm::Ed25519, signing_key.to_owned());
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.to_owned()), sender_key: Arc::new(sender_key.to_owned()),
signing_key: Arc::new(signing_key.to_owned()), signing_key: Arc::new(keys),
room_id: Arc::new(room_id.clone()), room_id: Arc::new(room_id.clone()),
forwarding_chains: Arc::new(Mutex::new(None)), forwarding_chains: Arc::new(Mutex::new(None)),
imported: Arc::new(false),
}) })
} }
@ -102,9 +107,10 @@ impl InboundGroupSession {
PickledInboundGroupSession { PickledInboundGroupSession {
pickle: InboundGroupSessionPickle::from(pickle), pickle: InboundGroupSessionPickle::from(pickle),
sender_key: self.sender_key.to_string(), sender_key: self.sender_key.to_string(),
signing_key: self.signing_key.to_string(), signing_key: (&*self.signing_key).clone(),
room_id: (&*self.room_id).clone(), room_id: (&*self.room_id).clone(),
forwarding_chains: self.forwarding_chains.lock().await.clone(), forwarding_chains: self.forwarding_chains.lock().await.clone(),
imported: *self.imported,
} }
} }
@ -120,10 +126,6 @@ impl InboundGroupSession {
let session_key = let session_key =
ExportedGroupSessionKey(self.inner.lock().await.export(message_index).ok()?); ExportedGroupSessionKey(self.inner.lock().await.export(message_index).ok()?);
let mut sender_claimed_keys: BTreeMap<DeviceKeyAlgorithm, String> = BTreeMap::new();
sender_claimed_keys.insert(DeviceKeyAlgorithm::Ed25519, (&*self.signing_key).to_owned());
Some(ExportedRoomKey { Some(ExportedRoomKey {
algorithm: EventEncryptionAlgorithm::MegolmV1AesSha2, algorithm: EventEncryptionAlgorithm::MegolmV1AesSha2,
room_id: (&*self.room_id).clone(), room_id: (&*self.room_id).clone(),
@ -136,7 +138,7 @@ impl InboundGroupSession {
.as_ref() .as_ref()
.cloned() .cloned()
.unwrap_or_default(), .unwrap_or_default(),
sender_claimed_keys, sender_claimed_keys: (&*self.signing_key).clone(),
session_key, session_key,
}) })
} }
@ -166,6 +168,7 @@ impl InboundGroupSession {
signing_key: Arc::new(pickle.signing_key), signing_key: Arc::new(pickle.signing_key),
room_id: Arc::new(pickle.room_id), room_id: Arc::new(pickle.room_id),
forwarding_chains: Arc::new(Mutex::new(pickle.forwarding_chains)), forwarding_chains: Arc::new(Mutex::new(pickle.forwarding_chains)),
imported: Arc::new(pickle.imported),
}) })
} }
@ -265,12 +268,15 @@ pub struct PickledInboundGroupSession {
/// The public curve25519 key of the account that sent us the session /// The public curve25519 key of the account that sent us the session
pub sender_key: String, pub sender_key: String,
/// The public ed25519 key of the account that sent us the session. /// The public ed25519 key of the account that sent us the session.
pub signing_key: String, pub signing_key: BTreeMap<DeviceKeyAlgorithm, String>,
/// The id of the room that the session is used in. /// The id of the room that the session is used in.
pub room_id: RoomId, pub room_id: RoomId,
/// The list of claimed ed25519 that forwarded us this key. Will be None if /// The list of claimed ed25519 that forwarded us this key. Will be None if
/// we dirrectly received this session. /// we dirrectly received this session.
pub forwarding_chains: Option<Vec<String>>, pub forwarding_chains: Option<Vec<String>>,
/// Flag remembering if the session was dirrectly sent to us by the sender
/// or if it was imported.
pub imported: bool,
} }
/// The typed representation of a base64 encoded string of the GroupSession pickle. /// The typed representation of a base64 encoded string of the GroupSession pickle.

View File

@ -231,14 +231,16 @@ impl SqliteStore {
.execute( .execute(
r#" r#"
CREATE TABLE IF NOT EXISTS inbound_group_sessions ( CREATE TABLE IF NOT EXISTS inbound_group_sessions (
"session_id" TEXT NOT NULL PRIMARY KEY, "id" INTEGER NOT NULL PRIMARY KEY,
"session_id" TEXT NOT NULL,
"account_id" INTEGER NOT NULL, "account_id" INTEGER NOT NULL,
"sender_key" TEXT NOT NULL, "sender_key" TEXT NOT NULL,
"signing_key" TEXT NOT NULL,
"room_id" TEXT NOT NULL, "room_id" TEXT NOT NULL,
"pickle" BLOB NOT NULL, "pickle" BLOB NOT NULL,
"imported" INTEGER NOT NULL,
FOREIGN KEY ("account_id") REFERENCES "accounts" ("id") FOREIGN KEY ("account_id") REFERENCES "accounts" ("id")
ON DELETE CASCADE ON DELETE CASCADE
UNIQUE(account_id,session_id,sender_key)
); );
CREATE INDEX IF NOT EXISTS "olm_groups_sessions_account_id" ON "inbound_group_sessions" ("account_id"); CREATE INDEX IF NOT EXISTS "olm_groups_sessions_account_id" ON "inbound_group_sessions" ("account_id");
@ -246,6 +248,24 @@ impl SqliteStore {
) )
.await?; .await?;
connection
.execute(
r#"
CREATE TABLE IF NOT EXISTS group_session_claimed_keys (
"id" INTEGER NOT NULL PRIMARY KEY,
"session_id" INTEGER NOT NULL,
"algorithm" TEXT NOT NULL,
"key" TEXT NOT NULL,
FOREIGN KEY ("session_id") REFERENCES "inbound_group_sessions" ("id")
ON DELETE CASCADE
UNIQUE(session_id, algorithm)
);
CREATE INDEX IF NOT EXISTS "group_session_claimed_keys_session_id" ON "inbound_group_sessions" ("session_id");
"#,
)
.await?;
connection connection
.execute( .execute(
r#" r#"
@ -475,45 +495,55 @@ 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 mut rows: Vec<(String, String, String, String)> = query_as( let mut rows: Vec<(i64, String, String, String, bool)> = query_as(
"SELECT pickle, sender_key, signing_key, room_id "SELECT id, pickle, sender_key, room_id, imported
FROM inbound_group_sessions WHERE account_id = ?", FROM inbound_group_sessions WHERE account_id = ?",
) )
.bind(account_id) .bind(account_id)
.fetch_all(&mut *connection) .fetch_all(&mut *connection)
.await?; .await?;
let mut group_sessions = rows for row in rows.drain(..) {
.drain(..) let session_row_id = row.0;
.map(|row| { let pickle = row.1;
let pickle = row.0; let sender_key = row.2;
let sender_key = row.1;
let signing_key = row.2;
let room_id = row.3; let room_id = row.3;
let imported = row.4;
let key_rows: Vec<(String, String)> = query_as(
"SELECT algorithm, key FROM group_session_claimed_keys WHERE session_id = ?",
)
.bind(session_row_id)
.fetch_all(&mut *connection)
.await?;
let claimed_keys: BTreeMap<DeviceKeyAlgorithm, String> = key_rows
.into_iter()
.filter_map(|row| {
let algorithm = row.0.parse::<DeviceKeyAlgorithm>().ok()?;
let key = row.1;
Some((algorithm, key))
})
.collect();
let pickle = PickledInboundGroupSession { let pickle = PickledInboundGroupSession {
pickle: InboundGroupSessionPickle::from(pickle), pickle: InboundGroupSessionPickle::from(pickle),
sender_key, sender_key,
signing_key, signing_key: claimed_keys,
room_id: RoomId::try_from(room_id)?, room_id: RoomId::try_from(room_id)?,
// Fixme we need to store/restore these once we get support // Fixme we need to store/restore these once we get support
// for key requesting/forwarding. // for key requesting/forwarding.
forwarding_chains: None, forwarding_chains: None,
imported,
}; };
Ok(InboundGroupSession::from_pickle( self.inbound_group_sessions
.add(InboundGroupSession::from_pickle(
pickle, pickle,
self.get_pickle_mode(), self.get_pickle_mode(),
)?) )?);
}) }
.collect::<Result<Vec<InboundGroupSession>>>()?;
group_sessions
.drain(..)
.map(|s| {
self.inbound_group_sessions.add(s);
})
.for_each(drop);
Ok(()) Ok(())
} }
@ -1146,23 +1176,47 @@ impl CryptoStore for SqliteStore {
// the key import feature. // the key import feature.
query( query(
"INSERT INTO inbound_group_sessions ( "REPLACE INTO inbound_group_sessions (
session_id, account_id, sender_key, signing_key, session_id, account_id, sender_key,
room_id, pickle room_id, pickle, imported
) VALUES (?1, ?2, ?3, ?4, ?5, ?6) ) VALUES (?1, ?2, ?3, ?4, ?5, ?6)
ON CONFLICT(session_id) DO UPDATE SET
pickle = excluded.pickle
", ",
) )
.bind(session_id) .bind(session_id)
.bind(account_id) .bind(account_id)
.bind(pickle.sender_key) .bind(&pickle.sender_key)
.bind(pickle.signing_key)
.bind(pickle.room_id.as_str()) .bind(pickle.room_id.as_str())
.bind(pickle.pickle.as_str()) .bind(pickle.pickle.as_str())
.bind(pickle.imported)
.execute(&mut *connection) .execute(&mut *connection)
.await?; .await?;
let row: (i64,) = query_as(
"SELECT id FROM inbound_group_sessions
WHERE account_id = ? and session_id = ? and sender_key = ?",
)
.bind(account_id)
.bind(session_id)
.bind(pickle.sender_key)
.fetch_one(&mut *connection)
.await?;
let session_row_id = row.0;
for (key_id, key) in pickle.signing_key {
query(
"INSERT OR IGNORE INTO group_session_claimed_keys (
session_id, algorithm, key
) VALUES (?1, ?2, ?3)
",
)
.bind(session_row_id)
.bind(serde_json::to_string(&key_id)?)
.bind(key)
.execute(&mut *connection)
.await?;
}
Ok(self.inbound_group_sessions.add(session)) Ok(self.inbound_group_sessions.add(session))
} }