diff --git a/matrix_sdk_crypto/src/verification/mod.rs b/matrix_sdk_crypto/src/verification/mod.rs index d5c3a206..023e6352 100644 --- a/matrix_sdk_crypto/src/verification/mod.rs +++ b/matrix_sdk_crypto/src/verification/mod.rs @@ -26,7 +26,7 @@ use crate::{Account, Device}; #[allow(dead_code)] mod sas; -#[derive(Clone)] +#[derive(Clone, Debug)] struct SasIds { account: Account, other_device: Device, diff --git a/matrix_sdk_crypto/src/verification/sas.rs b/matrix_sdk_crypto/src/verification/sas.rs index f101f1a5..01d498a0 100644 --- a/matrix_sdk_crypto/src/verification/sas.rs +++ b/matrix_sdk_crypto/src/verification/sas.rs @@ -20,6 +20,7 @@ use olm_rs::sas::OlmSas; use matrix_sdk_common::events::{ key::verification::{ accept::AcceptEventContent, + cancel::CancelCode, key::KeyEventContent, mac::MacEventContent, start::{MSasV1Content, MSasV1ContentOptions, StartEventContent}, @@ -35,6 +36,7 @@ use super::{get_decimal, get_emoji, get_mac_content, receive_mac_event, SasIds}; use crate::{Account, Device}; #[derive(Clone)] +/// Short authentication string object. struct Sas { inner: Arc>, account: Account, @@ -42,14 +44,26 @@ struct Sas { } impl Sas { + /// Get our own user id. fn user_id(&self) -> &UserId { self.account.user_id() } + /// Get our own device id. fn device_id(&self) -> &DeviceId { self.account.device_id() } + /// Start a new SAS auth flow with the given device. + /// + /// # Arguments + /// + /// * `account` - Our own account. + /// + /// * `other_device` - The other device which we are going to verify. + /// + /// Returns the new `Sas` object and a `StartEventContent` that needs to be + /// sent out through the server to the other device. fn start(account: Account, other_device: Device) -> (Sas, StartEventContent) { let (inner, content) = InnerSas::start(account.clone(), other_device.clone()); @@ -62,6 +76,16 @@ impl Sas { (sas, content) } + /// Create a new Sas object from a m.key.verification.start request. + /// + /// # Arguments + /// + /// * `account` - Our own account. + /// + /// * `other_device` - The other device which we are going to verify. + /// + /// * `event` - The m.key.verification.start event that was sent to us by + /// the other side. fn from_start_event( account: Account, other_device: Device, @@ -126,6 +150,7 @@ enum InnerSas { Confirmed(SasState), MacReceived(SasState), Done(SasState), + Canceled(SasState), } impl InnerSas { @@ -140,11 +165,10 @@ impl InnerSas { other_device: Device, event: &ToDeviceEvent, ) -> InnerSas { - InnerSas::Started(SasState::::from_start_event( - account, - other_device, - event, - )) + match SasState::::from_start_event(account, other_device, event) { + Ok(s) => InnerSas::Started(s), + Err(s) => InnerSas::Canceled(s), + } } fn accept(&self) -> Option { @@ -304,21 +328,31 @@ struct SasState { state: Arc, } +impl std::fmt::Debug for SasState { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("SasState") + .field("ids", &self.ids) + .field("flow_id", &self.verification_flow_id) + .field("state", &self.state) + .finish() + } +} + /// The initial SAS state. -#[derive(Clone)] +#[derive(Clone, Debug)] struct Created { protocol_definitions: MSasV1ContentOptions, } /// The initial SAS state if the other side started the SAS verification. -#[derive(Clone)] +#[derive(Clone, Debug)] struct Started { protocol_definitions: MSasV1Content, } /// The SAS state we're going to be in after the other side accepted our /// verification start event. -#[derive(Clone)] +#[derive(Clone, Debug)] struct Accepted { accepted_protocols: Arc, commitment: String, @@ -328,7 +362,7 @@ struct Accepted { /// other participant. /// /// From now on we can show the short auth string to the user. -#[derive(Clone)] +#[derive(Clone, Debug)] struct KeyReceived { we_started: bool, accepted_protocols: Arc, @@ -337,7 +371,7 @@ struct KeyReceived { /// The SAS state we're going to be in after the user has confirmed that the /// short auth string matches. We still need to receive a MAC event from the /// other side. -#[derive(Clone)] +#[derive(Clone, Debug)] struct Confirmed { accepted_protocols: Arc, } @@ -345,7 +379,7 @@ struct Confirmed { /// The SAS state we're going to be in after we receive a MAC event from the /// other side. Our own user still needs to confirm that the short auth string /// matches. -#[derive(Clone)] +#[derive(Clone, Debug)] struct MacReceived { we_started: bool, verified_devices: Arc>>, @@ -356,12 +390,18 @@ struct MacReceived { /// /// We can now mark the device in our verified devices lits as verified and sign /// the master keys in the verified devices list. -#[derive(Clone)] +#[derive(Clone, Debug)] struct Done { verified_devices: Arc>>, verified_master_keys: Arc>, } +#[derive(Clone, Debug)] +struct Canceled { + cancel_code: CancelCode, + reason: &'static str, +} + impl SasState { /// Get our own user id. pub fn user_id(&self) -> &UserId { @@ -462,28 +502,64 @@ impl SasState { account: Account, other_device: Device, event: &ToDeviceEvent, - ) -> SasState { - // TODO check if we support the suggested protocols and cancel if we - // don't - let content = if let StartEventContent::MSasV1(content) = &event.content { - content + ) -> Result, SasState> { + if let StartEventContent::MSasV1(content) = &event.content { + if !content + .key_agreement_protocols + .contains(&KeyAgreementProtocol::Curve25519HkdfSha256) + || !content + .message_authentication_codes + .contains(&MessageAuthenticationCode::HkdfHmacSha256) + || !content.hashes.contains(&HashAlgorithm::Sha256) + || (!content + .short_authentication_string + .contains(&ShortAuthenticationString::Decimal) + && !content + .short_authentication_string + .contains(&ShortAuthenticationString::Emoji)) + { + Err(SasState { + inner: Arc::new(Mutex::new(OlmSas::new())), + + ids: SasIds { + account, + other_device, + }, + verification_flow_id: Arc::new(content.transaction_id.clone()), + + state: Arc::new(Canceled::new(CancelCode::UnknownMethod)), + }) + } else { + Ok(SasState { + inner: Arc::new(Mutex::new(OlmSas::new())), + + ids: SasIds { + account, + other_device, + }, + + verification_flow_id: Arc::new(content.transaction_id.clone()), + + state: Arc::new(Started { + protocol_definitions: content.clone(), + }), + }) + } } else { - panic!("Invalid sas version") - }; + Err(SasState { + inner: Arc::new(Mutex::new(OlmSas::new())), - SasState { - inner: Arc::new(Mutex::new(OlmSas::new())), + ids: SasIds { + account, + other_device, + }, - ids: SasIds { - account, - other_device, - }, + // TODO we can't get to the transaction id currently since it's + // behind the an enum. + verification_flow_id: Arc::new("".to_owned()), - verification_flow_id: Arc::new(content.transaction_id.clone()), - - state: Arc::new(Started { - protocol_definitions: content.clone(), - }), + state: Arc::new(Canceled::new(CancelCode::UnknownMethod)), + }) } } @@ -772,6 +848,34 @@ impl SasState { } } +impl Canceled { + fn new(code: CancelCode) -> Canceled { + let reason = match code { + CancelCode::Accepted => { + "A m.key.verification.request was accepted by a different device." + } + CancelCode::InvalidMessage => "The received message was invalid.", + CancelCode::KeyMismatch => "The expected key did not match the verified one", + CancelCode::Timeout => "The verification process timed out.", + CancelCode::UnexpectedMessage => "The device received an unexpected message.", + CancelCode::UnknownMethod => { + "The device does not know how to handle the requested method." + } + CancelCode::UnknownTransaction => { + "The device does not know about the given transaction ID." + } + CancelCode::User => "The user cancelled the verification.", + CancelCode::UserMismatch => "The expected user did not match the verified user", + _ => unimplemented!(), + }; + + Canceled { + cancel_code: code, + reason, + } + } +} + #[cfg(test)] mod test { use std::convert::TryFrom; @@ -836,7 +940,7 @@ mod test { let bob_sas = SasState::::from_start_event(bob.clone(), alice_device, &event); - (alice_sas, bob_sas) + (alice_sas, bob_sas.unwrap()) } #[tokio::test]