crypto: Upload signatures after verification is done.

master
Damir Jelić 2020-10-27 16:39:23 +01:00
parent 30a78bb1d6
commit 5c530cf9ee
8 changed files with 198 additions and 42 deletions

View File

@ -1616,6 +1616,15 @@ impl Client {
.unwrap(); .unwrap();
} }
} }
OutgoingRequests::SignatureUpload(request) => {
// TODO remove this unwrap.
if let Ok(resp) = self.send(request.clone()).await {
self.base_client
.mark_request_as_sent(&r.request_id(), &resp)
.await
.unwrap();
}
}
} }
} }
} }

View File

@ -38,13 +38,19 @@ impl Sas {
/// Confirm that the short auth strings match on both sides. /// Confirm that the short auth strings match on both sides.
pub async fn confirm(&self) -> Result<()> { pub async fn confirm(&self) -> Result<()> {
if let Some(req) = self.inner.confirm().await? { let (to_device, signature) = self.inner.confirm().await?;
if let Some(req) = to_device {
let txn_id_string = req.txn_id_string(); let txn_id_string = req.txn_id_string();
let request = ToDeviceRequest::new(req.event_type, &txn_id_string, req.messages); let request = ToDeviceRequest::new(req.event_type, &txn_id_string, req.messages);
self.http_client.send(request).await?; self.http_client.send(request).await?;
} }
if let Some(s) = signature {
self.http_client.send(s).await?;
}
Ok(()) Ok(())
} }

View File

@ -351,6 +351,9 @@ impl OlmMachine {
IncomingResponse::SigningKeysUpload(_) => { IncomingResponse::SigningKeysUpload(_) => {
self.receive_cross_signing_upload_response().await?; self.receive_cross_signing_upload_response().await?;
} }
IncomingResponse::SignatureUpload(_) => {
self.verification_machine.mark_request_as_sent(request_id);
}
}; };
Ok(()) Ok(())
@ -1785,6 +1788,7 @@ pub(crate) mod test {
.confirm() .confirm()
.await .await
.unwrap() .unwrap()
.0
.map(|r| request_to_event(bob.user_id(), &r)) .map(|r| request_to_event(bob.user_id(), &r))
.unwrap(); .unwrap();
alice.handle_verification_event(&mut event).await; alice.handle_verification_event(&mut event).await;
@ -1796,6 +1800,7 @@ pub(crate) mod test {
.confirm() .confirm()
.await .await
.unwrap() .unwrap()
.0
.map(|r| request_to_event(alice.user_id(), &r)) .map(|r| request_to_event(alice.user_id(), &r))
.unwrap(); .unwrap();

View File

@ -14,9 +14,8 @@
mod pk_signing; mod pk_signing;
use matrix_sdk_common::encryption::DeviceKeys;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_json::{Error as JsonError, Value}; use serde_json::Error as JsonError;
use std::{ use std::{
collections::BTreeMap, collections::BTreeMap,
sync::{ sync::{
@ -27,7 +26,8 @@ use std::{
use matrix_sdk_common::{ use matrix_sdk_common::{
api::r0::keys::{upload_signatures::Request as SignatureUploadRequest, KeyUsage}, api::r0::keys::{upload_signatures::Request as SignatureUploadRequest, KeyUsage},
identifiers::UserId, encryption::DeviceKeys,
identifiers::{DeviceKeyAlgorithm, DeviceKeyId, UserId},
locks::Mutex, locks::Mutex,
}; };
@ -108,14 +108,17 @@ impl PrivateCrossSigningIdentity {
pub(crate) async fn sign_user( pub(crate) async fn sign_user(
&self, &self,
user_identity: &UserIdentity, user_identity: &UserIdentity,
) -> Result<BTreeMap<UserId, BTreeMap<String, Value>>, SignatureError> { ) -> Result<SignatureUploadRequest, SignatureError> {
self.user_signing_key let signed_keys = self
.user_signing_key
.lock() .lock()
.await .await
.as_ref() .as_ref()
.ok_or(SignatureError::MissingSigningKey)? .ok_or(SignatureError::MissingSigningKey)?
.sign_user(&user_identity) .sign_user(&user_identity)
.await .await?;
Ok(SignatureUploadRequest { signed_keys })
} }
/// Sign the given device keys with this identity. /// Sign the given device keys with this identity.
@ -190,7 +193,11 @@ impl PrivateCrossSigningIdentity {
.signatures .signatures
.entry(account.user_id().to_owned()) .entry(account.user_id().to_owned())
.or_insert_with(BTreeMap::new) .or_insert_with(BTreeMap::new)
.insert(format!("ed25519:{}", account.device_id()), signature); .insert(
DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, account.device_id())
.to_string(),
signature,
);
let master = MasterSigning { let master = MasterSigning {
inner: master, inner: master,

View File

@ -20,6 +20,9 @@ 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_signatures::{
Request as SignatureUploadRequest, Response as SignatureUploadResponse,
},
upload_signing_keys::Response as SigningKeysUploadResponse, upload_signing_keys::Response as SigningKeysUploadResponse,
CrossSigningKey, CrossSigningKey,
}, },
@ -114,6 +117,9 @@ pub enum OutgoingRequests {
/// things, the main use is key requests/forwards and interactive device /// things, the main use is key requests/forwards and interactive device
/// verification. /// verification.
ToDeviceRequest(ToDeviceRequest), ToDeviceRequest(ToDeviceRequest),
/// Signature upload request, this request is used after a successful device
/// or user verification is done.
SignatureUpload(SignatureUploadRequest),
} }
#[cfg(test)] #[cfg(test)]
@ -144,6 +150,12 @@ impl From<ToDeviceRequest> for OutgoingRequests {
} }
} }
impl From<SignatureUploadRequest> for OutgoingRequests {
fn from(request: SignatureUploadRequest) -> Self {
OutgoingRequests::SignatureUpload(request)
}
}
/// Enum over all the incoming responses we need to receive. /// Enum over all the incoming responses we need to receive.
#[derive(Debug)] #[derive(Debug)]
pub enum IncomingResponse<'a> { pub enum IncomingResponse<'a> {
@ -161,6 +173,9 @@ pub enum IncomingResponse<'a> {
/// The cross signing keys upload response, marking our private cross /// The cross signing keys upload response, marking our private cross
/// signing identity as shared. /// signing identity as shared.
SigningKeysUpload(&'a SigningKeysUploadResponse), SigningKeysUpload(&'a SigningKeysUploadResponse),
/// The cross signing keys upload response, marking our private cross
/// signing identity as shared.
SignatureUpload(&'a SignatureUploadResponse),
} }
impl<'a> From<&'a KeysUploadResponse> for IncomingResponse<'a> { impl<'a> From<&'a KeysUploadResponse> for IncomingResponse<'a> {
@ -187,6 +202,12 @@ impl<'a> From<&'a KeysClaimResponse> for IncomingResponse<'a> {
} }
} }
impl<'a> From<&'a SignatureUploadResponse> for IncomingResponse<'a> {
fn from(response: &'a SignatureUploadResponse) -> Self {
IncomingResponse::SignatureUpload(response)
}
}
/// Outgoing request type, holds the unique ID of the request and the actual /// Outgoing request type, holds the unique ID of the request and the actual
/// request. /// request.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]

View File

@ -25,7 +25,7 @@ use matrix_sdk_common::{
uuid::Uuid, uuid::Uuid,
}; };
use super::sas::{content_to_request, Sas}; use super::sas::{content_to_request, Sas, VerificationResult};
use crate::{ use crate::{
olm::PrivateCrossSigningIdentity, olm::PrivateCrossSigningIdentity,
requests::{OutgoingRequest, ToDeviceRequest}, requests::{OutgoingRequest, ToDeviceRequest},
@ -206,7 +206,9 @@ impl VerificationMachine {
self.receive_event_helper(&s, event); self.receive_event_helper(&s, event);
if s.is_done() { if s.is_done() {
if let Some(r) = s.mark_as_done().await? { match s.mark_as_done().await? {
VerificationResult::Ok => (),
VerificationResult::Cancel(r) => {
self.outgoing_to_device_messages.insert( self.outgoing_to_device_messages.insert(
r.txn_id, r.txn_id,
OutgoingRequest { OutgoingRequest {
@ -215,6 +217,18 @@ impl VerificationMachine {
}, },
); );
} }
VerificationResult::SignatureUpload(r) => {
let request_id = Uuid::new_v4();
self.outgoing_to_device_messages.insert(
request_id,
OutgoingRequest {
request_id,
request: Arc::new(r.into()),
},
);
}
}
} }
}; };
} }
@ -352,13 +366,13 @@ mod test {
let mut event = wrap_any_to_device_content( let mut event = wrap_any_to_device_content(
alice.user_id(), alice.user_id(),
get_content_from_request(&alice.confirm().await.unwrap().unwrap()), get_content_from_request(&alice.confirm().await.unwrap().0.unwrap()),
); );
bob.receive_event(&mut event); bob.receive_event(&mut event);
let mut event = wrap_any_to_device_content( let mut event = wrap_any_to_device_content(
bob.user_id(), bob.user_id(),
get_content_from_request(&bob.confirm().await.unwrap().unwrap()), get_content_from_request(&bob.confirm().await.unwrap().0.unwrap()),
); );
alice.receive_event(&mut event); alice.receive_event(&mut event);

View File

@ -16,7 +16,7 @@ mod machine;
mod sas; mod sas;
pub use machine::VerificationMachine; pub use machine::VerificationMachine;
pub use sas::Sas; pub use sas::{Sas, VerificationResult};
#[cfg(test)] #[cfg(test)]
pub(crate) mod test { pub(crate) mod test {

View File

@ -19,9 +19,10 @@ mod sas_state;
use std::time::Instant; use std::time::Instant;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use tracing::{info, trace, warn}; use tracing::{error, info, trace, warn};
use matrix_sdk_common::{ use matrix_sdk_common::{
api::r0::keys::upload_signatures::Request as SignatureUploadRequest,
events::{ events::{
key::verification::{ key::verification::{
accept::AcceptEventContent, cancel::CancelCode, mac::MacEventContent, accept::AcceptEventContent, cancel::CancelCode, mac::MacEventContent,
@ -33,6 +34,7 @@ use matrix_sdk_common::{
}; };
use crate::{ use crate::{
error::SignatureError,
identities::{LocalTrust, ReadOnlyDevice, UserIdentities}, identities::{LocalTrust, ReadOnlyDevice, UserIdentities},
olm::PrivateCrossSigningIdentity, olm::PrivateCrossSigningIdentity,
store::{Changes, CryptoStore, CryptoStoreError, DeviceChanges}, store::{Changes, CryptoStore, CryptoStoreError, DeviceChanges},
@ -44,6 +46,17 @@ use sas_state::{
Accepted, Canceled, Confirmed, Created, Done, KeyReceived, MacReceived, SasState, Started, Accepted, Canceled, Confirmed, Created, Done, KeyReceived, MacReceived, SasState, Started,
}; };
#[derive(Debug)]
/// A result of a verification flow.
pub enum VerificationResult {
/// The verification succeeded, nothing needs to be done.
Ok,
/// The verification was canceled.
Cancel(ToDeviceRequest),
/// The verification is done and has signatures that need to be uploaded.
SignatureUpload(SignatureUploadRequest),
}
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
/// Short authentication string object. /// Short authentication string object.
pub struct Sas { pub struct Sas {
@ -185,7 +198,9 @@ impl Sas {
/// Does nothing if we're not in a state where we can confirm the short auth /// Does nothing if we're not in a state where we can confirm the short auth
/// string, otherwise returns a `MacEventContent` that needs to be sent to /// string, otherwise returns a `MacEventContent` that needs to be sent to
/// the server. /// the server.
pub async fn confirm(&self) -> Result<Option<ToDeviceRequest>, CryptoStoreError> { pub async fn confirm(
&self,
) -> Result<(Option<ToDeviceRequest>, Option<SignatureUploadRequest>), CryptoStoreError> {
let (content, done) = { let (content, done) = {
let mut guard = self.inner.lock().unwrap(); let mut guard = self.inner.lock().unwrap();
let sas: InnerSas = (*guard).clone(); let sas: InnerSas = (*guard).clone();
@ -195,27 +210,52 @@ impl Sas {
(content, guard.is_done()) (content, guard.is_done())
}; };
let cancel = if done { let mac_request = content
// Pass on the signature upload request here as well. .map(|c| self.content_to_request(AnyToDeviceEventContent::KeyVerificationMac(c)));
self.mark_as_done().await?
if done {
match self.mark_as_done().await? {
VerificationResult::Cancel(r) => Ok((Some(r), None)),
VerificationResult::Ok => Ok((mac_request, None)),
VerificationResult::SignatureUpload(r) => Ok((mac_request, Some(r))),
}
} else {
Ok((mac_request, None))
}
}
pub(crate) async fn mark_as_done(&self) -> Result<VerificationResult, CryptoStoreError> {
if let Some(device) = self.mark_device_as_verified().await? {
let identity = self.mark_identity_as_verified().await?;
// We only sign devices of our own user here.
let signature_request = if device.user_id() == self.user_id() {
match self.private_identity.sign_device(&device).await {
Ok(r) => Some(r),
Err(SignatureError::MissingSigningKey) => {
warn!(
"Can't sign the device keys for {} {}, \
no private user signing key found",
device.user_id(),
device.device_id(),
);
None
}
Err(e) => {
error!(
"Error signing device keys for {} {} {:?}",
device.user_id(),
device.device_id(),
e
);
None
}
}
} else { } else {
None None
}; };
if cancel.is_some() {
Ok(cancel)
} else {
Ok(content.map(|c| {
let content = AnyToDeviceEventContent::KeyVerificationMac(c);
self.content_to_request(content)
}))
}
}
pub(crate) async fn mark_as_done(&self) -> Result<Option<ToDeviceRequest>, CryptoStoreError> {
if let Some(device) = self.mark_device_as_verified().await? {
let identity = self.mark_identity_as_verified().await?;
let mut changes = Changes { let mut changes = Changes {
devices: DeviceChanges { devices: DeviceChanges {
changed: vec![device], changed: vec![device],
@ -224,14 +264,68 @@ impl Sas {
..Default::default() ..Default::default()
}; };
if let Some(i) = identity { let identity_signature_request = if let Some(i) = identity {
changes.identities.changed.push(i); // We only sign other users here.
let request = if let Some(i) = i.other() {
// Signing can fail if the user signing key is missing.
match self.private_identity.sign_user(&i).await {
Ok(r) => Some(r),
Err(SignatureError::MissingSigningKey) => {
warn!(
"Can't sign the public cross signing keys for {}, \
no private user signing key found",
i.user_id()
);
None
}
Err(e) => {
error!(
"Error signing the public cross signing keys for {} {:?}",
i.user_id(),
e
);
None
}
} }
self.store.save_changes(changes).await?;
Ok(None)
} else { } else {
Ok(self.cancel()) None
};
changes.identities.changed.push(i);
request
} else {
None
};
// If there are two signature upload requests, merge them. Otherwise
// use the one we have or None.
//
// Realistically at most one reuqest will be used but let's make
// this future proof.
let merged_request = if let Some(mut r) = signature_request {
if let Some(user_request) = identity_signature_request {
r.signed_keys.extend(user_request.signed_keys);
Some(r)
} else {
Some(r)
}
} else if let Some(r) = identity_signature_request {
Some(r)
} else {
None
};
// TODO store the request as well.
self.store.save_changes(changes).await?;
Ok(merged_request
.map(|r| VerificationResult::SignatureUpload(r))
.unwrap_or(VerificationResult::Ok))
} else {
Ok(self
.cancel()
.map(|r| VerificationResult::Cancel(r))
.unwrap_or(VerificationResult::Ok))
} }
} }
@ -857,13 +951,13 @@ mod test {
let mut event = wrap_any_to_device_content( let mut event = wrap_any_to_device_content(
alice.user_id(), alice.user_id(),
get_content_from_request(&alice.confirm().await.unwrap().unwrap()), get_content_from_request(&alice.confirm().await.unwrap().0.unwrap()),
); );
bob.receive_event(&mut event); bob.receive_event(&mut event);
let mut event = wrap_any_to_device_content( let mut event = wrap_any_to_device_content(
bob.user_id(), bob.user_id(),
get_content_from_request(&bob.confirm().await.unwrap().unwrap()), get_content_from_request(&bob.confirm().await.unwrap().0.unwrap()),
); );
alice.receive_event(&mut event); alice.receive_event(&mut event);