crypto: Connect the cross signing to the main state machine.

master
Damir Jelić 2020-10-19 16:03:01 +02:00
parent 0c1d33d43f
commit 728d80ed06
4 changed files with 90 additions and 13 deletions

View File

@ -17,6 +17,7 @@ use std::path::Path;
use std::{collections::BTreeMap, mem, sync::Arc}; use std::{collections::BTreeMap, mem, sync::Arc};
use dashmap::DashMap; use dashmap::DashMap;
use matrix_sdk_common::locks::Mutex;
use tracing::{debug, error, info, instrument, trace, warn}; use tracing::{debug, error, info, instrument, trace, warn};
use matrix_sdk_common::{ use matrix_sdk_common::{
@ -50,9 +51,9 @@ use crate::{
key_request::KeyRequestMachine, key_request::KeyRequestMachine,
olm::{ olm::{
Account, EncryptionSettings, ExportedRoomKey, GroupSessionKey, IdentityKeys, Account, EncryptionSettings, ExportedRoomKey, GroupSessionKey, IdentityKeys,
InboundGroupSession, ReadOnlyAccount, InboundGroupSession, PrivateCrossSigningIdentity, ReadOnlyAccount,
}, },
requests::{IncomingResponse, OutgoingRequest}, requests::{IncomingResponse, OutgoingRequest, UploadSigningKeysRequest},
session_manager::{GroupSessionManager, SessionManager}, session_manager::{GroupSessionManager, SessionManager},
store::{CryptoStore, MemoryStore, Result as StoreResult, Store}, store::{CryptoStore, MemoryStore, Result as StoreResult, Store},
verification::{Sas, VerificationMachine}, verification::{Sas, VerificationMachine},
@ -69,6 +70,11 @@ pub struct OlmMachine {
device_id: Arc<Box<DeviceId>>, device_id: Arc<Box<DeviceId>>,
/// Our underlying Olm Account holding our identity keys. /// Our underlying Olm Account holding our identity keys.
account: Account, 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<Mutex<PrivateCrossSigningIdentity>>,
/// Store for the encryption keys. /// Store for the encryption keys.
/// Persists all the encryption keys so a client can resume the session /// Persists all the encryption keys so a client can resume the session
/// without the need to create new keys. /// without the need to create new keys.
@ -114,7 +120,13 @@ impl OlmMachine {
let device_id: DeviceIdBox = device_id.into(); let device_id: DeviceIdBox = device_id.into();
let account = ReadOnlyAccount::new(&user_id, &device_id); 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( fn new_helper(
@ -122,6 +134,7 @@ impl OlmMachine {
device_id: DeviceIdBox, device_id: DeviceIdBox,
store: Box<dyn CryptoStore>, store: Box<dyn CryptoStore>,
account: ReadOnlyAccount, account: ReadOnlyAccount,
user_identity: PrivateCrossSigningIdentity,
) -> Self { ) -> Self {
let user_id = Arc::new(user_id.clone()); let user_id = Arc::new(user_id.clone());
@ -169,6 +182,7 @@ impl OlmMachine {
verification_machine, verification_machine,
key_request_machine, key_request_machine,
identity_manager, 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. /// Create a new machine with the default crypto store.
@ -311,11 +330,36 @@ impl OlmMachine {
IncomingResponse::ToDevice(_) => { IncomingResponse::ToDevice(_) => {
self.mark_to_device_request_as_sent(&request_id).await?; self.mark_to_device_request_as_sent(&request_id).await?;
} }
IncomingResponse::SigningKeysUpload(_) => {
self.receive_cross_signing_upload_response().await?;
}
}; };
Ok(()) 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. /// Should device or one-time keys be uploaded to the server.
/// ///
/// This needs to be checked periodically, ideally after every sync request. /// This needs to be checked periodically, ideally after every sync request.

View File

@ -32,6 +32,7 @@ pub use group_sessions::{
pub(crate) use group_sessions::{GroupSessionKey, OutboundGroupSession}; pub(crate) use group_sessions::{GroupSessionKey, OutboundGroupSession};
pub use olm_rs::{account::IdentityKeys, PicklingMode}; pub use olm_rs::{account::IdentityKeys, PicklingMode};
pub use session::{PickledSession, Session, SessionPickle}; pub use session::{PickledSession, Session, SessionPickle};
pub(crate) use signing::PrivateCrossSigningIdentity;
pub(crate) use utility::Utility; pub(crate) use utility::Utility;
#[cfg(test)] #[cfg(test)]

View File

@ -32,12 +32,15 @@ use zeroize::Zeroizing;
use olm_rs::{errors::OlmUtilityError, pk::OlmPkSigning, utility::OlmUtility}; use olm_rs::{errors::OlmUtilityError, pk::OlmPkSigning, utility::OlmUtility};
use matrix_sdk_common::{ use matrix_sdk_common::{
api::r0::keys::{upload_signing_keys::Request as UploadRequest, CrossSigningKey, KeyUsage}, api::r0::keys::{CrossSigningKey, KeyUsage},
identifiers::UserId, identifiers::UserId,
locks::Mutex, locks::Mutex,
}; };
use crate::identities::{MasterPubkey, SelfSigningPubkey, UserSigningPubkey}; use crate::{
identities::{MasterPubkey, SelfSigningPubkey, UserSigningPubkey},
requests::UploadSigningKeysRequest,
};
fn encode<T: AsRef<[u8]>>(input: T) -> String { fn encode<T: AsRef<[u8]>>(input: T) -> String {
encode_config(input, URL_SAFE_NO_PAD) encode_config(input, URL_SAFE_NO_PAD)
@ -320,7 +323,17 @@ impl Signing {
} }
impl PrivateCrossSigningIdentity { 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 master = Signing::new();
let public_key = master.cross_signing_key(user_id.clone(), KeyUsage::Master); 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) self.shared.store(true, Ordering::SeqCst)
} }
@ -364,7 +377,7 @@ impl PrivateCrossSigningIdentity {
self.shared.load(Ordering::SeqCst) 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() { let master_key = if let Some(m) = self.master_key.lock().await.as_ref() {
Some(m.pickle(pickle_key).await) Some(m.pickle(pickle_key).await)
} else { } 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 { let master = if let Some(m) = pickle.master_key {
Some(MasterSigning::from_pickle(m, pickle_key)) Some(MasterSigning::from_pickle(m, pickle_key))
} else { } 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 let master_key = self
.master_key .master_key
.lock() .lock()
@ -445,8 +458,7 @@ impl PrivateCrossSigningIdentity {
.cloned() .cloned()
.map(|k| k.public_key.into()); .map(|k| k.public_key.into());
UploadRequest { UploadSigningKeysRequest {
auth: None,
master_key, master_key,
user_signing_key, user_signing_key,
self_signing_key, self_signing_key,

View File

@ -20,6 +20,8 @@ use matrix_sdk_common::{
claim_keys::Response as KeysClaimResponse, claim_keys::Response as KeysClaimResponse,
get_keys::Response as KeysQueryResponse, get_keys::Response as KeysQueryResponse,
upload_keys::{Request as KeysUploadRequest, Response as KeysUploadResponse}, 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}, 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<CrossSigningKey>,
/// 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<CrossSigningKey>,
/// 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<CrossSigningKey>,
}
/// Customized version of `ruma_client_api::r0::keys::get_keys::Request`, without any references. /// Customized version of `ruma_client_api::r0::keys::get_keys::Request`, without any references.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct KeysQueryRequest { 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 /// The key claiming requests, giving us new one-time keys of other users so
/// new Olm sessions can be created. /// new Olm sessions can be created.
KeysClaim(&'a KeysClaimResponse), 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> { impl<'a> From<&'a KeysUploadResponse> for IncomingResponse<'a> {