crypto: Connect the cross signing to the main state machine.
parent
0c1d33d43f
commit
728d80ed06
|
@ -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.
|
||||||
|
|
|
@ -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)]
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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> {
|
||||||
|
|
Loading…
Reference in New Issue