crypto: Initial scaffolding for handling user identities in key queries.

master
Damir Jelić 2020-08-18 12:50:03 +02:00
parent 150862ec0c
commit f96437a242
5 changed files with 131 additions and 8 deletions

View File

@ -60,6 +60,10 @@ use super::{
OlmMessage, OutboundGroupSession, OlmMessage, OutboundGroupSession,
}, },
store::{memorystore::MemoryStore, Result as StoreResult}, store::{memorystore::MemoryStore, Result as StoreResult},
user_identity::{
MasterPubkey, OwnUserIdentity, SelfSigningPubkey, UserIdentities, UserIdentity,
UserSigningPubkey,
},
verification::{Sas, VerificationMachine}, verification::{Sas, VerificationMachine},
CryptoStore, CryptoStore,
}; };
@ -429,6 +433,9 @@ impl OlmMachine {
let mut changed_devices = Vec::new(); let mut changed_devices = Vec::new();
for (user_id, device_map) in device_keys_map { for (user_id, device_map) in device_keys_map {
// TODO move this out into the handle keys query response method
// since we might fail handle the new device at any point here or
// when updating the user identities.
self.store.update_tracked_user(user_id, false).await?; self.store.update_tracked_user(user_id, false).await?;
for (device_id, device_keys) in device_map.iter() { for (device_id, device_keys) in device_map.iter() {
@ -492,6 +499,63 @@ impl OlmMachine {
Ok(changed_devices) Ok(changed_devices)
} }
async fn handle_cross_singing_keys(
&self,
response: &get_keys::Response,
) -> StoreResult<Vec<UserIdentities>> {
let mut changed = Vec::new();
for (user_id, master_key) in &response.master_keys {
let master_key = MasterPubkey::from(master_key);
let self_signing = if let Some(s) = response.self_signing_keys.get(user_id) {
SelfSigningPubkey::from(s)
} else {
continue;
};
let identity = if let Some(mut i) = self.store.get_user_identity(user_id).await? {
match &mut i {
UserIdentities::Own(ref mut identity) => {
let user_signing = if let Some(s) = response.user_signing_keys.get(user_id)
{
UserSigningPubkey::from(s)
} else {
continue;
};
identity
.update(master_key, self_signing, user_signing)
.map(|_| i)
}
UserIdentities::Other(ref mut identity) => {
identity.update(master_key, self_signing).map(|_| i)
}
}
} else if user_id == self.user_id() {
if let Some(s) = response.user_signing_keys.get(user_id) {
let user_signing = UserSigningPubkey::from(s);
OwnUserIdentity::new(master_key, self_signing, user_signing)
.map(UserIdentities::Own)
} else {
continue;
}
} else {
UserIdentity::new(master_key, self_signing).map(UserIdentities::Other)
};
match identity {
Ok(i) => changed.push(i),
Err(_e) => {
// TODO log some error here
continue;
}
}
}
Ok(changed)
}
/// Receive a successful keys query response. /// Receive a successful keys query response.
/// ///
/// Returns a list of devices newly discovered devices and devices that /// Returns a list of devices newly discovered devices and devices that
@ -504,13 +568,14 @@ impl OlmMachine {
pub async fn receive_keys_query_response( pub async fn receive_keys_query_response(
&self, &self,
response: &get_keys::Response, response: &get_keys::Response,
) -> OlmResult<Vec<ReadOnlyDevice>> { ) -> OlmResult<(Vec<ReadOnlyDevice>, Vec<UserIdentities>)> {
let changed_devices = self let changed_devices = self
.handle_devices_from_key_query(&response.device_keys) .handle_devices_from_key_query(&response.device_keys)
.await?; .await?;
self.store.save_devices(&changed_devices).await?; self.store.save_devices(&changed_devices).await?;
let changed_identities = self.handle_cross_singing_keys(response).await?;
Ok(changed_devices) Ok((changed_devices, changed_identities))
} }
/// Get a request to upload E2EE keys to the server. /// Get a request to upload E2EE keys to the server.

View File

@ -25,6 +25,7 @@ use super::{Account, CryptoStore, InboundGroupSession, Result, Session};
use crate::{ use crate::{
device::ReadOnlyDevice, device::ReadOnlyDevice,
memory_stores::{DeviceStore, GroupSessionStore, ReadOnlyUserDevices, SessionStore}, memory_stores::{DeviceStore, GroupSessionStore, ReadOnlyUserDevices, SessionStore},
user_identity::UserIdentities,
}; };
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct MemoryStore { pub struct MemoryStore {
@ -131,6 +132,10 @@ impl CryptoStore for MemoryStore {
Ok(()) Ok(())
} }
async fn get_user_identity(&self, _user_id: &UserId) -> Result<Option<UserIdentities>> {
Ok(None)
}
} }
#[cfg(test)] #[cfg(test)]

View File

@ -30,6 +30,7 @@ use super::{
device::ReadOnlyDevice, device::ReadOnlyDevice,
memory_stores::ReadOnlyUserDevices, memory_stores::ReadOnlyUserDevices,
olm::{Account, InboundGroupSession, Session}, olm::{Account, InboundGroupSession, Session},
user_identity::UserIdentities,
}; };
pub mod memorystore; pub mod memorystore;
@ -197,4 +198,11 @@ pub trait CryptoStore: Debug {
/// ///
/// * `user_id` - The user for which we should get all the devices. /// * `user_id` - The user for which we should get all the devices.
async fn get_user_devices(&self, user_id: &UserId) -> Result<ReadOnlyUserDevices>; async fn get_user_devices(&self, user_id: &UserId) -> Result<ReadOnlyUserDevices>;
/// Get the user identity that is attached to the given user id.
///
/// # Arguments
///
/// * `user_id` - The user for which we should get the identity.
async fn get_user_identity(&self, user_id: &UserId) -> Result<Option<UserIdentities>>;
} }

View File

@ -38,6 +38,7 @@ use super::{CryptoStore, CryptoStoreError, Result};
use crate::{ use crate::{
device::{ReadOnlyDevice, TrustState}, device::{ReadOnlyDevice, TrustState},
memory_stores::{DeviceStore, GroupSessionStore, ReadOnlyUserDevices, SessionStore}, memory_stores::{DeviceStore, GroupSessionStore, ReadOnlyUserDevices, SessionStore},
user_identity::UserIdentities,
Account, IdentityKeys, InboundGroupSession, Session, Account, IdentityKeys, InboundGroupSession, Session,
}; };
@ -883,6 +884,10 @@ impl CryptoStore for SqliteStore {
async fn get_user_devices(&self, user_id: &UserId) -> Result<ReadOnlyUserDevices> { async fn get_user_devices(&self, user_id: &UserId) -> Result<ReadOnlyUserDevices> {
Ok(self.devices.user_devices(user_id)) Ok(self.devices.user_devices(user_id))
} }
async fn get_user_identity(&self, _user_id: &UserId) -> Result<Option<UserIdentities>> {
Ok(None)
}
} }
#[cfg(not(tarpaulin_include))] #[cfg(not(tarpaulin_include))]

View File

@ -77,12 +77,6 @@ impl<'a> CrossSigningSubKeys<'a> {
} }
} }
pub struct UserIdentity {
user_id: Arc<UserId>,
master_key: MasterPubkey,
self_signing_key: SelfSigningPubkey,
}
impl MasterPubkey { impl MasterPubkey {
fn verify_subkey<'a>( fn verify_subkey<'a>(
&self, &self,
@ -151,6 +145,19 @@ impl SelfSigningPubkey {
} }
} }
#[derive(Debug, Clone)]
pub enum UserIdentities {
Own(OwnUserIdentity),
Other(UserIdentity),
}
#[derive(Debug, Clone)]
pub struct UserIdentity {
user_id: Arc<UserId>,
master_key: MasterPubkey,
self_signing_key: SelfSigningPubkey,
}
impl UserIdentity { impl UserIdentity {
pub fn new( pub fn new(
master_key: MasterPubkey, master_key: MasterPubkey,
@ -165,11 +172,25 @@ impl UserIdentity {
}) })
} }
pub fn update(
&mut self,
master_key: MasterPubkey,
self_signing_key: SelfSigningPubkey,
) -> Result<(), SignatureError> {
master_key.verify_subkey(&self_signing_key)?;
self.master_key = master_key;
self.self_signing_key = self_signing_key;
Ok(())
}
pub fn is_device_signed(&self, device: &ReadOnlyDevice) -> Result<(), SignatureError> { pub fn is_device_signed(&self, device: &ReadOnlyDevice) -> Result<(), SignatureError> {
self.self_signing_key.verify_device(device) self.self_signing_key.verify_device(device)
} }
} }
#[derive(Debug, Clone)]
pub struct OwnUserIdentity { pub struct OwnUserIdentity {
user_id: Arc<UserId>, user_id: Arc<UserId>,
master_key: MasterPubkey, master_key: MasterPubkey,
@ -196,6 +217,25 @@ impl OwnUserIdentity {
}) })
} }
pub fn update(
&mut self,
master_key: MasterPubkey,
self_signing_key: SelfSigningPubkey,
user_signing_key: UserSigningPubkey,
) -> Result<(), SignatureError> {
master_key.verify_subkey(&self_signing_key)?;
master_key.verify_subkey(&user_signing_key)?;
self.self_signing_key = self_signing_key;
self.user_signing_key = user_signing_key;
self.master_key = master_key;
// FIXME reset the verification state if the master key changed.
Ok(())
}
pub fn is_identity_signed(&self, identity: &UserIdentity) -> Result<(), SignatureError> { pub fn is_identity_signed(&self, identity: &UserIdentity) -> Result<(), SignatureError> {
self.user_signing_key self.user_signing_key
.verify_master_key(&identity.master_key) .verify_master_key(&identity.master_key)