crypto: Remove private cross signing keys if we detect that they changed

This commit is contained in:
Damir Jelić 2021-08-12 14:06:45 +02:00
parent 4e9fe79619
commit 356506060c
3 changed files with 148 additions and 39 deletions

View file

@ -24,7 +24,7 @@ use ruma::{
api::client::r0::keys::get_keys::Response as KeysQueryResponse, encryption::DeviceKeys, api::client::r0::keys::get_keys::Response as KeysQueryResponse, encryption::DeviceKeys,
DeviceId, DeviceIdBox, UserId, DeviceId, DeviceIdBox, UserId,
}; };
use tracing::{trace, warn}; use tracing::{info, trace, warn};
use crate::{ use crate::{
error::OlmResult, error::OlmResult,
@ -32,6 +32,7 @@ use crate::{
MasterPubkey, ReadOnlyDevice, ReadOnlyOwnUserIdentity, ReadOnlyUserIdentities, MasterPubkey, ReadOnlyDevice, ReadOnlyOwnUserIdentity, ReadOnlyUserIdentities,
ReadOnlyUserIdentity, SelfSigningPubkey, UserSigningPubkey, ReadOnlyUserIdentity, SelfSigningPubkey, UserSigningPubkey,
}, },
olm::PrivateCrossSigningIdentity,
requests::KeysQueryRequest, requests::KeysQueryRequest,
store::{Changes, DeviceChanges, IdentityChanges, Result as StoreResult, Store}, store::{Changes, DeviceChanges, IdentityChanges, Result as StoreResult, Store},
}; };
@ -75,11 +76,13 @@ impl IdentityManager {
) -> OlmResult<(DeviceChanges, IdentityChanges)> { ) -> OlmResult<(DeviceChanges, IdentityChanges)> {
let changed_devices = let changed_devices =
self.handle_devices_from_key_query(response.device_keys.clone()).await?; self.handle_devices_from_key_query(response.device_keys.clone()).await?;
let changed_identities = self.handle_cross_singing_keys(response).await?; let (changed_identities, cross_signing_identity) =
self.handle_cross_singing_keys(response).await?;
let changes = Changes { let changes = Changes {
identities: changed_identities.clone(), identities: changed_identities.clone(),
devices: changed_devices.clone(), devices: changed_devices.clone(),
private_identity: cross_signing_identity,
..Default::default() ..Default::default()
}; };
@ -234,8 +237,9 @@ impl IdentityManager {
async fn handle_cross_singing_keys( async fn handle_cross_singing_keys(
&self, &self,
response: &KeysQueryResponse, response: &KeysQueryResponse,
) -> StoreResult<IdentityChanges> { ) -> StoreResult<(IdentityChanges, Option<PrivateCrossSigningIdentity>)> {
let mut changes = IdentityChanges::default(); let mut changes = IdentityChanges::default();
let mut changed_identity = None;
for (user_id, master_key) in &response.master_keys { for (user_id, master_key) in &response.master_keys {
let master_key = MasterPubkey::from(master_key); let master_key = MasterPubkey::from(master_key);
@ -243,7 +247,10 @@ impl IdentityManager {
let self_signing = if let Some(s) = response.self_signing_keys.get(user_id) { let self_signing = if let Some(s) = response.self_signing_keys.get(user_id) {
SelfSigningPubkey::from(s) SelfSigningPubkey::from(s)
} else { } else {
warn!("User identity for user {} didn't contain a self signing pubkey", user_id); warn!(
user_id = user_id.as_str(),
"A user identity didn't contain a self signing pubkey"
);
continue; continue;
}; };
@ -255,9 +262,9 @@ impl IdentityManager {
UserSigningPubkey::from(s) UserSigningPubkey::from(s)
} else { } else {
warn!( warn!(
"User identity for our own user {} didn't \ user_id = user_id.as_str(),
contain a user signing pubkey", "User identity for our own user didn't \
user_id contain a user signing pubkey",
); );
continue; continue;
}; };
@ -277,8 +284,8 @@ impl IdentityManager {
|| user_signing.user_id() != user_id || user_signing.user_id() != user_id
{ {
warn!( warn!(
"User id mismatch in one of the cross signing keys for user {}", user_id = user_id.as_str(),
user_id "User ID mismatch in one of the cross signing keys",
); );
continue; continue;
} }
@ -287,14 +294,14 @@ impl IdentityManager {
.map(|i| (ReadOnlyUserIdentities::Own(i), true)) .map(|i| (ReadOnlyUserIdentities::Own(i), true))
} else { } else {
warn!( warn!(
"User identity for our own user {} didn't contain a \ user_id = user_id.as_str(),
"User identity for our own user didn't contain a \
user signing pubkey", user signing pubkey",
user_id
); );
continue; continue;
} }
} else if master_key.user_id() != user_id || self_signing.user_id() != user_id { } else if master_key.user_id() != user_id || self_signing.user_id() != user_id {
warn!("User id mismatch in one of the cross signing keys for user {}", user_id); warn!(user = user_id.as_str(), "User ID mismatch in one of the cross signing keys",);
continue; continue;
} else { } else {
ReadOnlyUserIdentity::new(master_key, self_signing) ReadOnlyUserIdentity::new(master_key, self_signing)
@ -303,21 +310,38 @@ impl IdentityManager {
match result { match result {
Ok((i, new)) => { Ok((i, new)) => {
trace!("Updated or created new user identity for {}: {:?}", user_id, i); if let Some(identity) = i.own() {
let private_identity = self.store.private_identity();
let private_identity = private_identity.lock().await;
let result = private_identity.clear_if_differs(identity).await;
if result.any_cleared() {
changed_identity = Some((&*private_identity).clone());
info!(cleared =? result, "Removed some or all of our private cross signing keys");
}
}
if new { if new {
trace!(user_id = user_id.as_str(), identity =? i, "Created new user identity");
changes.new.push(i); changes.new.push(i);
} else { } else {
trace!(user_id = user_id.as_str(), identity =? i, "Updated a user identity");
changes.changed.push(i); changes.changed.push(i);
} }
} }
Err(e) => { Err(e) => {
warn!("Couldn't update or create new user identity for {}: {:?}", user_id, e); warn!(
user_id = user_id.as_str(),
error =? e,
"Couldn't update or create new user identity for"
);
continue; continue;
} }
} }
} }
Ok(changes) Ok((changes, changed_identity))
} }
/// Get a key query request if one is needed. /// Get a key query request if one is needed.

View file

@ -34,9 +34,13 @@ use serde::{Deserialize, Serialize};
use serde_json::Error as JsonError; use serde_json::Error as JsonError;
use crate::{ use crate::{
error::SignatureError, identities::MasterPubkey, requests::UploadSigningKeysRequest, error::SignatureError,
store::SecretImportError, utilities::decode, OwnUserIdentity, ReadOnlyAccount, ReadOnlyDevice, identities::{MasterPubkey, SelfSigningPubkey, UserSigningPubkey},
ReadOnlyOwnUserIdentity, ReadOnlyUserIdentity, requests::UploadSigningKeysRequest,
store::SecretImportError,
utilities::decode,
OwnUserIdentity, ReadOnlyAccount, ReadOnlyDevice, ReadOnlyOwnUserIdentity,
ReadOnlyUserIdentity,
}; };
/// Private cross signing identity. /// Private cross signing identity.
@ -57,6 +61,26 @@ pub struct PrivateCrossSigningIdentity {
pub(crate) self_signing_key: Arc<Mutex<Option<SelfSigning>>>, pub(crate) self_signing_key: Arc<Mutex<Option<SelfSigning>>>,
} }
/// A struct containing information if any of our cross signing keys were
/// cleared because the public keys differ from the keys that are uploaded to
/// the server.
#[derive(Debug, Clone)]
pub struct ClearResult {
/// Was the master key cleared.
master_cleared: bool,
/// Was the self-signing key cleared.
self_signing_cleared: bool,
/// Was the user-signing key cleared.
user_signing_cleared: bool,
}
impl ClearResult {
/// Did we clear any of the private cross signing keys.
pub fn any_cleared(&self) -> bool {
self.master_cleared || self.self_signing_cleared || self.user_signing_cleared
}
}
/// The pickled version of a `PrivateCrossSigningIdentity`. /// The pickled version of a `PrivateCrossSigningIdentity`.
/// ///
/// Can be used to store the identity. /// Can be used to store the identity.
@ -137,6 +161,16 @@ impl PrivateCrossSigningIdentity {
self.master_key.lock().await.as_ref().map(|m| m.public_key.to_owned()) self.master_key.lock().await.as_ref().map(|m| m.public_key.to_owned())
} }
/// Get the public part of the self-signing key, if we have one.
pub async fn self_signing_public_key(&self) -> Option<SelfSigningPubkey> {
self.self_signing_key.lock().await.as_ref().map(|k| k.public_key.to_owned())
}
/// Get the public part of the user-signing key, if we have one.
pub async fn user_signing_public_key(&self) -> Option<UserSigningPubkey> {
self.user_signing_key.lock().await.as_ref().map(|k| k.public_key.to_owned())
}
/// Export the seed of the private cross signing key /// Export the seed of the private cross signing key
/// ///
/// The exported seed will be encoded as unpadded base64. /// The exported seed will be encoded as unpadded base64.
@ -190,7 +224,6 @@ impl PrivateCrossSigningIdentity {
if public_identity.master_key() == &master.public_key { if public_identity.master_key() == &master.public_key {
Ok(Some(master)) Ok(Some(master))
// *self.master_key.lock().await = Some(master);
} else { } else {
Err(SecretImportError::MissmatchedPublicKeys) Err(SecretImportError::MissmatchedPublicKeys)
} }
@ -203,7 +236,6 @@ impl PrivateCrossSigningIdentity {
let subkey = UserSigning::from_seed(self.user_id().clone(), seed); let subkey = UserSigning::from_seed(self.user_id().clone(), seed);
if public_identity.user_signing_key() == &subkey.public_key { if public_identity.user_signing_key() == &subkey.public_key {
// *self.user_signing_key.lock().await = Some(subkey);
Ok(Some(subkey)) Ok(Some(subkey))
} else { } else {
Err(SecretImportError::MissmatchedPublicKeys) Err(SecretImportError::MissmatchedPublicKeys)
@ -217,7 +249,6 @@ impl PrivateCrossSigningIdentity {
let subkey = SelfSigning::from_seed(self.user_id().clone(), seed); let subkey = SelfSigning::from_seed(self.user_id().clone(), seed);
if public_identity.self_signing_key() == &subkey.public_key { if public_identity.self_signing_key() == &subkey.public_key {
// *self.self_signing_key.lock().await = Some(subkey);
Ok(Some(subkey)) Ok(Some(subkey))
} else { } else {
Err(SecretImportError::MissmatchedPublicKeys) Err(SecretImportError::MissmatchedPublicKeys)
@ -241,6 +272,58 @@ impl PrivateCrossSigningIdentity {
Ok(()) Ok(())
} }
/// Remove our private cross signing key if the public keys differ from
/// what's found in the `ReadOnlyOwnUserIdentity`.
pub(crate) async fn clear_if_differs(
&self,
public_identity: &ReadOnlyOwnUserIdentity,
) -> ClearResult {
let result = self.get_public_identity_diff(public_identity).await;
if result.master_cleared {
*self.master_key.lock().await = None;
}
if result.user_signing_cleared {
*self.user_signing_key.lock().await = None;
}
if result.self_signing_cleared {
*self.self_signing_key.lock().await = None;
}
result
}
async fn get_public_identity_diff(
&self,
public_identity: &ReadOnlyOwnUserIdentity,
) -> ClearResult {
let master_differs = self
.master_public_key()
.await
.map(|master| &master != public_identity.master_key())
.unwrap_or(false);
let user_signing_differs = self
.user_signing_public_key()
.await
.map(|subkey| &subkey != public_identity.user_signing_key())
.unwrap_or(false);
let self_signing_differs = self
.self_signing_public_key()
.await
.map(|subkey| &subkey != public_identity.self_signing_key())
.unwrap_or(false);
ClearResult {
master_cleared: master_differs,
user_signing_cleared: user_signing_differs,
self_signing_cleared: self_signing_differs,
}
}
/// Get the names of the secrets we are missing. /// Get the names of the secrets we are missing.
pub(crate) async fn get_missing_secrets(&self) -> Vec<SecretName> { pub(crate) async fn get_missing_secrets(&self) -> Vec<SecretName> {
let mut missing = Vec::new(); let mut missing = Vec::new();
@ -282,6 +365,7 @@ impl PrivateCrossSigningIdentity {
.ok_or(SignatureError::MissingSigningKey)? .ok_or(SignatureError::MissingSigningKey)?
.public_key .public_key
.clone(); .clone();
let self_signing = self let self_signing = self
.self_signing_key .self_signing_key
.lock() .lock()
@ -290,6 +374,7 @@ impl PrivateCrossSigningIdentity {
.ok_or(SignatureError::MissingSigningKey)? .ok_or(SignatureError::MissingSigningKey)?
.public_key .public_key
.clone(); .clone();
let user_signing = self let user_signing = self
.user_signing_key .user_signing_key
.lock() .lock()
@ -298,6 +383,7 @@ impl PrivateCrossSigningIdentity {
.ok_or(SignatureError::MissingSigningKey)? .ok_or(SignatureError::MissingSigningKey)?
.public_key .public_key
.clone(); .clone();
let identity = ReadOnlyOwnUserIdentity::new(master, self_signing, user_signing)?; let identity = ReadOnlyOwnUserIdentity::new(master, self_signing, user_signing)?;
identity.mark_as_verified(); identity.mark_as_verified();
@ -506,23 +592,18 @@ impl PrivateCrossSigningIdentity {
) -> Result<Self, SigningError> { ) -> Result<Self, SigningError> {
let signings: PickledSignings = serde_json::from_str(&pickle.pickle)?; let signings: PickledSignings = serde_json::from_str(&pickle.pickle)?;
let master = if let Some(m) = signings.master_key { let master =
Some(MasterSigning::from_pickle(m, pickle_key)?) signings.master_key.map(|m| MasterSigning::from_pickle(m, pickle_key)).transpose()?;
} else {
None
};
let self_signing = if let Some(s) = signings.self_signing_key { let self_signing = signings
Some(SelfSigning::from_pickle(s, pickle_key)?) .self_signing_key
} else { .map(|s| SelfSigning::from_pickle(s, pickle_key))
None .transpose()?;
};
let user_signing = if let Some(u) = signings.user_signing_key { let user_signing = signings
Some(UserSigning::from_pickle(u, pickle_key)?) .user_signing_key
} else { .map(|s| UserSigning::from_pickle(s, pickle_key))
None .transpose()?;
};
Ok(Self { Ok(Self {
user_id: Arc::new(pickle.user_id), user_id: Arc::new(pickle.user_id),
@ -537,13 +618,13 @@ impl PrivateCrossSigningIdentity {
/// identity. /// identity.
pub(crate) async fn as_upload_request(&self) -> UploadSigningKeysRequest { pub(crate) async fn as_upload_request(&self) -> UploadSigningKeysRequest {
let master_key = let master_key =
self.master_key.lock().await.as_ref().cloned().map(|k| k.public_key.into()); self.master_key.lock().await.as_ref().map(|k| k.public_key.to_owned().into());
let user_signing_key = let user_signing_key =
self.user_signing_key.lock().await.as_ref().cloned().map(|k| k.public_key.into()); self.user_signing_key.lock().await.as_ref().map(|k| k.public_key.to_owned().into());
let self_signing_key = let self_signing_key =
self.self_signing_key.lock().await.as_ref().cloned().map(|k| k.public_key.into()); self.self_signing_key.lock().await.as_ref().map(|k| k.public_key.to_owned().into());
UploadSigningKeysRequest { master_key, self_signing_key, user_signing_key } UploadSigningKeysRequest { master_key, self_signing_key, user_signing_key }
} }

View file

@ -202,6 +202,10 @@ impl Store {
self.identity.lock().await.reset().await; self.identity.lock().await.reset().await;
} }
pub fn private_identity(&self) -> Arc<Mutex<PrivateCrossSigningIdentity>> {
self.identity.clone()
}
pub async fn save_sessions(&self, sessions: &[Session]) -> Result<()> { pub async fn save_sessions(&self, sessions: &[Session]) -> Result<()> {
let changes = Changes { sessions: sessions.to_vec(), ..Default::default() }; let changes = Changes { sessions: sessions.to_vec(), ..Default::default() };