From 728d80ed0612b5003078e2e85ea32d658b953365 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Mon, 19 Oct 2020 16:03:01 +0200 Subject: [PATCH] crypto: Connect the cross signing to the main state machine. --- matrix_sdk_crypto/src/machine.rs | 52 +++++++++++++++++++++++++--- matrix_sdk_crypto/src/olm/mod.rs | 1 + matrix_sdk_crypto/src/olm/signing.rs | 30 +++++++++++----- matrix_sdk_crypto/src/requests.rs | 20 +++++++++++ 4 files changed, 90 insertions(+), 13 deletions(-) diff --git a/matrix_sdk_crypto/src/machine.rs b/matrix_sdk_crypto/src/machine.rs index 8206daff..675d5be7 100644 --- a/matrix_sdk_crypto/src/machine.rs +++ b/matrix_sdk_crypto/src/machine.rs @@ -17,6 +17,7 @@ use std::path::Path; use std::{collections::BTreeMap, mem, sync::Arc}; use dashmap::DashMap; +use matrix_sdk_common::locks::Mutex; use tracing::{debug, error, info, instrument, trace, warn}; use matrix_sdk_common::{ @@ -50,9 +51,9 @@ use crate::{ key_request::KeyRequestMachine, olm::{ Account, EncryptionSettings, ExportedRoomKey, GroupSessionKey, IdentityKeys, - InboundGroupSession, ReadOnlyAccount, + InboundGroupSession, PrivateCrossSigningIdentity, ReadOnlyAccount, }, - requests::{IncomingResponse, OutgoingRequest}, + requests::{IncomingResponse, OutgoingRequest, UploadSigningKeysRequest}, session_manager::{GroupSessionManager, SessionManager}, store::{CryptoStore, MemoryStore, Result as StoreResult, Store}, verification::{Sas, VerificationMachine}, @@ -69,6 +70,11 @@ pub struct OlmMachine { device_id: Arc>, /// Our underlying Olm Account holding our identity keys. account: Account, + /// The private part of our cross signing identity. + /// Used to sign devices and other users, might be missing if some other + /// device bootstraped cross signing or cross signing isn't bootstrapped at + /// all. + user_identity: Arc>, /// Store for the encryption keys. /// Persists all the encryption keys so a client can resume the session /// without the need to create new keys. @@ -114,7 +120,13 @@ impl OlmMachine { let device_id: DeviceIdBox = device_id.into(); let account = ReadOnlyAccount::new(&user_id, &device_id); - OlmMachine::new_helper(user_id, device_id, store, account) + OlmMachine::new_helper( + user_id, + device_id, + store, + account, + PrivateCrossSigningIdentity::empty(user_id.to_owned()), + ) } fn new_helper( @@ -122,6 +134,7 @@ impl OlmMachine { device_id: DeviceIdBox, store: Box, account: ReadOnlyAccount, + user_identity: PrivateCrossSigningIdentity, ) -> Self { let user_id = Arc::new(user_id.clone()); @@ -169,6 +182,7 @@ impl OlmMachine { verification_machine, key_request_machine, identity_manager, + user_identity: Arc::new(Mutex::new(user_identity)), } } @@ -207,7 +221,12 @@ impl OlmMachine { } }; - Ok(OlmMachine::new_helper(&user_id, device_id, store, account)) + // TODO load the identity like we load an account. + let identity = PrivateCrossSigningIdentity::empty(user_id.clone()); + + Ok(OlmMachine::new_helper( + &user_id, device_id, store, account, identity, + )) } /// Create a new machine with the default crypto store. @@ -311,11 +330,36 @@ impl OlmMachine { IncomingResponse::ToDevice(_) => { self.mark_to_device_request_as_sent(&request_id).await?; } + IncomingResponse::SigningKeysUpload(_) => { + self.receive_cross_signing_upload_response().await?; + } }; Ok(()) } + /// Mark the cross signing identity as shared. + async fn receive_cross_signing_upload_response(&self) -> StoreResult<()> { + self.user_identity.lock().await.mark_as_shared(); + // TOOD save the identity. + Ok(()) + } + + /// Create a new cross signing identity and get the upload request to push + /// the new public keys to the server. + /// + /// **Warning**: This will delete any existing cross signing keys that might + /// exist on the server and thus will reset the trust between all the + /// devices. + /// + /// Uploading these keys will require user interactive auth. + pub async fn bootstrap_cross_signing(&self) -> UploadSigningKeysRequest { + let mut lock = self.user_identity.lock().await; + *lock = PrivateCrossSigningIdentity::new(self.user_id().to_owned()).await; + // TODO save the identity. + lock.as_upload_request().await + } + /// Should device or one-time keys be uploaded to the server. /// /// This needs to be checked periodically, ideally after every sync request. diff --git a/matrix_sdk_crypto/src/olm/mod.rs b/matrix_sdk_crypto/src/olm/mod.rs index f2e39094..df2b387a 100644 --- a/matrix_sdk_crypto/src/olm/mod.rs +++ b/matrix_sdk_crypto/src/olm/mod.rs @@ -32,6 +32,7 @@ pub use group_sessions::{ pub(crate) use group_sessions::{GroupSessionKey, OutboundGroupSession}; pub use olm_rs::{account::IdentityKeys, PicklingMode}; pub use session::{PickledSession, Session, SessionPickle}; +pub(crate) use signing::PrivateCrossSigningIdentity; pub(crate) use utility::Utility; #[cfg(test)] diff --git a/matrix_sdk_crypto/src/olm/signing.rs b/matrix_sdk_crypto/src/olm/signing.rs index 0f47a81f..8395db9c 100644 --- a/matrix_sdk_crypto/src/olm/signing.rs +++ b/matrix_sdk_crypto/src/olm/signing.rs @@ -32,12 +32,15 @@ use zeroize::Zeroizing; use olm_rs::{errors::OlmUtilityError, pk::OlmPkSigning, utility::OlmUtility}; use matrix_sdk_common::{ - api::r0::keys::{upload_signing_keys::Request as UploadRequest, CrossSigningKey, KeyUsage}, + api::r0::keys::{CrossSigningKey, KeyUsage}, identifiers::UserId, locks::Mutex, }; -use crate::identities::{MasterPubkey, SelfSigningPubkey, UserSigningPubkey}; +use crate::{ + identities::{MasterPubkey, SelfSigningPubkey, UserSigningPubkey}, + requests::UploadSigningKeysRequest, +}; fn encode>(input: T) -> String { encode_config(input, URL_SAFE_NO_PAD) @@ -320,7 +323,17 @@ impl Signing { } impl PrivateCrossSigningIdentity { - async fn new(user_id: UserId) -> Self { + pub(crate) fn empty(user_id: UserId) -> Self { + Self { + user_id: Arc::new(user_id), + shared: Arc::new(AtomicBool::new(false)), + master_key: Arc::new(Mutex::new(None)), + self_signing_key: Arc::new(Mutex::new(None)), + user_signing_key: Arc::new(Mutex::new(None)), + } + } + + pub(crate) async fn new(user_id: UserId) -> Self { let master = Signing::new(); let public_key = master.cross_signing_key(user_id.clone(), KeyUsage::Master); @@ -356,7 +369,7 @@ impl PrivateCrossSigningIdentity { } } - fn mark_as_shared(&self) { + pub fn mark_as_shared(&self) { self.shared.store(true, Ordering::SeqCst) } @@ -364,7 +377,7 @@ impl PrivateCrossSigningIdentity { self.shared.load(Ordering::SeqCst) } - async fn pickle(&self, pickle_key: &[u8]) -> PickledCrossSigningIdentity { + pub async fn pickle(&self, pickle_key: &[u8]) -> PickledCrossSigningIdentity { let master_key = if let Some(m) = self.master_key.lock().await.as_ref() { Some(m.pickle(pickle_key).await) } else { @@ -392,7 +405,7 @@ impl PrivateCrossSigningIdentity { } } - async fn from_pickle(pickle: PickledCrossSigningIdentity, pickle_key: &[u8]) -> Self { + pub async fn from_pickle(pickle: PickledCrossSigningIdentity, pickle_key: &[u8]) -> Self { let master = if let Some(m) = pickle.master_key { Some(MasterSigning::from_pickle(m, pickle_key)) } else { @@ -420,7 +433,7 @@ impl PrivateCrossSigningIdentity { } } - async fn as_upload_request(&self) -> UploadRequest<'_> { + pub(crate) async fn as_upload_request(&self) -> UploadSigningKeysRequest { let master_key = self .master_key .lock() @@ -445,8 +458,7 @@ impl PrivateCrossSigningIdentity { .cloned() .map(|k| k.public_key.into()); - UploadRequest { - auth: None, + UploadSigningKeysRequest { master_key, user_signing_key, self_signing_key, diff --git a/matrix_sdk_crypto/src/requests.rs b/matrix_sdk_crypto/src/requests.rs index 3a6ba4d9..5b70c744 100644 --- a/matrix_sdk_crypto/src/requests.rs +++ b/matrix_sdk_crypto/src/requests.rs @@ -20,6 +20,8 @@ use matrix_sdk_common::{ claim_keys::Response as KeysClaimResponse, get_keys::Response as KeysQueryResponse, upload_keys::{Request as KeysUploadRequest, Response as KeysUploadResponse}, + upload_signing_keys::Response as SigningKeysUploadResponse, + CrossSigningKey, }, to_device::{send_event_to_device::Response as ToDeviceResponse, DeviceIdOrAllDevices}, }, @@ -56,6 +58,21 @@ impl ToDeviceRequest { } } +/// Request that will publish a cross signing identity. +/// +/// This uploads the public cross signing key triplet. +#[derive(Debug, Clone)] +pub struct UploadSigningKeysRequest { + /// The user's master key. + pub master_key: Option, + /// The user's self-signing key. Must be signed with the accompanied master, or by the + /// user's most recently uploaded master key if no master key is included in the request. + pub self_signing_key: Option, + /// The user's user-signing key. Must be signed with the accompanied master, or by the + /// user's most recently uploaded master key if no master key is included in the request. + pub user_signing_key: Option, +} + /// Customized version of `ruma_client_api::r0::keys::get_keys::Request`, without any references. #[derive(Clone, Debug)] pub struct KeysQueryRequest { @@ -141,6 +158,9 @@ pub enum IncomingResponse<'a> { /// The key claiming requests, giving us new one-time keys of other users so /// new Olm sessions can be created. KeysClaim(&'a KeysClaimResponse), + /// The cross signing keys upload response, marking our private cross + /// signing identity as shared. + SigningKeysUpload(&'a SigningKeysUploadResponse), } impl<'a> From<&'a KeysUploadResponse> for IncomingResponse<'a> {