From b0ac9d3320db0e2738fc324c40c7254c26ee0fa6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Thu, 10 Dec 2020 17:49:28 +0100 Subject: [PATCH] crypto: WIP change the types of the sas sturcts to allow in-room verifications. --- matrix_sdk_crypto/src/identities/device.rs | 12 ++- matrix_sdk_crypto/src/verification/machine.rs | 29 ++--- .../src/verification/requests.rs | 69 ++++++++---- .../src/verification/sas/event_enums.rs | 76 +++++++++++++ .../src/verification/sas/helpers.rs | 11 +- .../src/verification/sas/inner_sas.rs | 35 ++++-- matrix_sdk_crypto/src/verification/sas/mod.rs | 78 ++++++++++++-- .../src/verification/sas/sas_state.rs | 101 ++++++++++++++---- 8 files changed, 331 insertions(+), 80 deletions(-) create mode 100644 matrix_sdk_crypto/src/verification/sas/event_enums.rs diff --git a/matrix_sdk_crypto/src/identities/device.rs b/matrix_sdk_crypto/src/identities/device.rs index 69e766c1..705b90be 100644 --- a/matrix_sdk_crypto/src/identities/device.rs +++ b/matrix_sdk_crypto/src/identities/device.rs @@ -42,6 +42,7 @@ use tracing::warn; use crate::{ olm::{InboundGroupSession, PrivateCrossSigningIdentity, Session}, store::{Changes, DeviceChanges}, + OutgoingRequest, OutgoingRequests, }; #[cfg(test)] use crate::{OlmMachine, ReadOnlyAccount}; @@ -91,9 +92,16 @@ impl Device { /// /// Returns a `Sas` object and to-device request that needs to be sent out. pub async fn start_verification(&self) -> StoreResult<(Sas, ToDeviceRequest)> { - self.verification_machine + let (sas, request) = self + .verification_machine .start_sas(self.inner.clone()) - .await + .await?; + + if let OutgoingRequests::ToDeviceRequest(r) = request { + Ok((sas, r)) + } else { + panic!("Invalid verification request type"); + } } /// Get the Olm sessions that belong to this device. diff --git a/matrix_sdk_crypto/src/verification/machine.rs b/matrix_sdk_crypto/src/verification/machine.rs index 4cfb04f9..0e185418 100644 --- a/matrix_sdk_crypto/src/verification/machine.rs +++ b/matrix_sdk_crypto/src/verification/machine.rs @@ -30,14 +30,14 @@ use matrix_sdk_common::{ use super::{ requests::VerificationRequest, - sas::{content_to_request, Sas, VerificationResult}, + sas::{content_to_request, OutgoingContent, Sas, VerificationResult}, }; use crate::{ olm::PrivateCrossSigningIdentity, requests::{OutgoingRequest, ToDeviceRequest}, store::{CryptoStore, CryptoStoreError}, - ReadOnlyAccount, ReadOnlyDevice, + OutgoingRequests, ReadOnlyAccount, ReadOnlyDevice, }; #[derive(Clone, Debug)] @@ -46,6 +46,7 @@ pub struct VerificationMachine { private_identity: Arc>, pub(crate) store: Arc>, verifications: Arc>, + room_verifications: Arc>, requests: Arc>, outgoing_to_device_messages: Arc>, } @@ -63,13 +64,14 @@ impl VerificationMachine { verifications: DashMap::new().into(), requests: DashMap::new().into(), outgoing_to_device_messages: DashMap::new().into(), + room_verifications: DashMap::new().into(), } } pub async fn start_sas( &self, device: ReadOnlyDevice, - ) -> Result<(Sas, ToDeviceRequest), CryptoStoreError> { + ) -> Result<(Sas, OutgoingRequests), CryptoStoreError> { let identity = self.store.get_user_identity(device.user_id()).await?; let private_identity = self.private_identity.lock().await.clone(); @@ -81,14 +83,17 @@ impl VerificationMachine { identity, ); - let request = content_to_request( - device.user_id(), - device.device_id(), - AnyToDeviceEventContent::KeyVerificationStart(content), - ); + let request: OutgoingRequests = match content { + OutgoingContent::Room(c) => todo!(), + OutgoingContent::ToDevice(c) => { + let request = content_to_request(device.user_id(), device.device_id(), c); - self.verifications - .insert(sas.flow_id().to_string(), sas.clone()); + self.verifications + .insert(sas.flow_id().to_string(), sas.clone()); + + request.into() + } + }; Ok((sas, request)) } @@ -170,9 +175,9 @@ impl VerificationMachine { ); let request = VerificationRequest::from_request_event( + self.account.clone(), + self.store.clone(), room_id, - self.account.user_id(), - self.account.device_id(), &m.sender, &m.event_id, r, diff --git a/matrix_sdk_crypto/src/verification/requests.rs b/matrix_sdk_crypto/src/verification/requests.rs index 8e201fda..2d27af43 100644 --- a/matrix_sdk_crypto/src/verification/requests.rs +++ b/matrix_sdk_crypto/src/verification/requests.rs @@ -19,26 +19,38 @@ use std::sync::{Arc, Mutex}; use matrix_sdk_common::{ api::r0::message::send_message_event::Response as RoomMessageResponse, events::{ - key::verification::{ready::ReadyEventContent, Relation, VerificationMethod}, + key::verification::{ + ready::ReadyEventContent, start::StartEventContent, Relation, VerificationMethod, + }, room::message::KeyVerificationRequestEventContent, }, identifiers::{DeviceId, DeviceIdBox, EventId, RoomId, UserId}, }; +use crate::{ + olm::{PrivateCrossSigningIdentity, ReadOnlyAccount}, + store::CryptoStore, + ReadOnlyDevice, Sas, UserIdentities, UserIdentity, +}; + +use super::sas::{OutgoingContent, StartContent}; + const SUPPORTED_METHODS: &[VerificationMethod] = &[VerificationMethod::MSasV1]; #[derive(Clone, Debug)] /// TODO pub struct VerificationRequest { inner: Arc>, + account: ReadOnlyAccount, + store: Arc>, room_id: Arc, } impl VerificationRequest { pub(crate) fn from_request_event( + account: ReadOnlyAccount, + store: Arc>, room_id: &RoomId, - own_user_id: &UserId, - own_device_id: &DeviceId, sender: &UserId, event_id: &EventId, content: &KeyVerificationRequestEventContent, @@ -46,13 +58,15 @@ impl VerificationRequest { Self { inner: Arc::new(Mutex::new(InnerRequest::Requested( RequestState::from_request_event( - own_user_id, - own_device_id, + account.user_id(), + account.device_id(), sender, event_id, content, ), ))), + account, + store, room_id: room_id.clone().into(), } } @@ -74,7 +88,6 @@ enum InnerRequest { Sent(RequestState), Requested(RequestState), Ready(RequestState), - Accepted(RequestState), Passive(RequestState), } @@ -82,7 +95,7 @@ impl InnerRequest { fn accept(&mut self) -> Option { if let InnerRequest::Requested(s) = self { let (state, content) = s.clone().accept(); - *self = InnerRequest::Accepted(state); + *self = InnerRequest::Ready(state); Some(content) } else { @@ -200,13 +213,12 @@ impl RequestState { } } - fn accept(self) -> (RequestState, ReadyEventContent) { - // TODO let the user pick a method here. + fn accept(self) -> (RequestState, ReadyEventContent) { let state = RequestState { own_user_id: self.own_user_id, own_device_id: self.own_device_id.clone(), other_user_id: self.other_user_id, - state: Accepted { + state: Ready { methods: self.state.methods.clone(), other_device_id: self.state.other_device_id.clone(), flow_id: self.state.flow_id.clone(), @@ -238,17 +250,34 @@ struct Ready { pub flow_id: EventId, } -#[derive(Clone, Debug)] -struct Accepted { - /// The verification methods that were accepted - pub methods: Vec, +impl RequestState { + fn into_started_sas( + self, + account: ReadOnlyAccount, + private_identity: PrivateCrossSigningIdentity, + other_device: ReadOnlyDevice, + other_identity: UserIdentity, + ) -> Sas { + todo!() + // Sas::from_start_event(account, private_identity, other_device, other_identity, event) + } - /// The device id of the device that responded to the verification request. - pub other_device_id: DeviceIdBox, - - /// The event id of the `m.key.verification.request` event which acts as an - /// unique id identifying this verification flow. - pub flow_id: EventId, + fn start_sas( + self, + store: Arc>, + account: ReadOnlyAccount, + private_identity: PrivateCrossSigningIdentity, + other_device: ReadOnlyDevice, + other_identity: Option, + ) -> (Sas, OutgoingContent) { + Sas::start( + account, + private_identity, + other_device, + store, + other_identity, + ) + } } #[derive(Clone, Debug)] diff --git a/matrix_sdk_crypto/src/verification/sas/event_enums.rs b/matrix_sdk_crypto/src/verification/sas/event_enums.rs new file mode 100644 index 00000000..e34ccd6d --- /dev/null +++ b/matrix_sdk_crypto/src/verification/sas/event_enums.rs @@ -0,0 +1,76 @@ +// Copyright 2020 The Matrix.org Foundation C.I.C. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![allow(dead_code)] + +use std::convert::TryInto; + +use matrix_sdk_common::{ + events::{ + key::verification::start::{StartEventContent, StartToDeviceEventContent}, + AnyMessageEventContent, AnyToDeviceEventContent, MessageEvent, ToDeviceEvent, + }, + CanonicalJsonValue, +}; + +#[derive(Clone, Debug)] +pub enum StartContent { + ToDevice(StartToDeviceEventContent), + Room(StartEventContent), +} + +impl StartContent { + pub fn to_canonical_json(self) -> CanonicalJsonValue { + let content = match self { + StartContent::Room(c) => serde_json::to_value(c), + StartContent::ToDevice(c) => serde_json::to_value(c), + }; + + content + .expect("Can't serialize content") + .try_into() + .expect("Can't canonicalize content") + } +} + +impl From for StartContent { + fn from(content: StartEventContent) -> Self { + StartContent::Room(content) + } +} + +impl From for StartContent { + fn from(content: StartToDeviceEventContent) -> Self { + StartContent::ToDevice(content) + } +} + +#[derive(Clone, Debug)] +pub enum OutgoingContent { + Room(AnyMessageEventContent), + ToDevice(AnyToDeviceEventContent), +} + +impl From for OutgoingContent { + fn from(content: StartContent) -> Self { + match content { + StartContent::Room(c) => { + OutgoingContent::Room(AnyMessageEventContent::KeyVerificationStart(c)) + } + StartContent::ToDevice(c) => { + OutgoingContent::ToDevice(AnyToDeviceEventContent::KeyVerificationStart(c)) + } + } + } +} diff --git a/matrix_sdk_crypto/src/verification/sas/helpers.rs b/matrix_sdk_crypto/src/verification/sas/helpers.rs index 99d81280..388fd6c2 100644 --- a/matrix_sdk_crypto/src/verification/sas/helpers.rs +++ b/matrix_sdk_crypto/src/verification/sas/helpers.rs @@ -38,7 +38,7 @@ use crate::{ ReadOnlyAccount, ToDeviceRequest, }; -use super::sas_state::FlowId; +use super::{event_enums::StartContent, sas_state::FlowId}; #[derive(Clone, Debug)] pub struct SasIds { @@ -57,15 +57,12 @@ pub struct SasIds { /// /// * `content` - The `m.key.verification.start` event content that started the /// interactive verification process. -pub fn calculate_commitment(public_key: &str, content: &StartToDeviceEventContent) -> String { - let json_content: CanonicalJsonValue = serde_json::to_value(content) - .expect("Can't serialize content") - .try_into() - .expect("Can't canonicalize content"); +pub fn calculate_commitment(public_key: &str, content: impl Into) -> String { + let content = content.into().to_canonical_json(); encode( Sha256::new() - .chain(&format!("{}{}", public_key, json_content)) + .chain(&format!("{}{}", public_key, content)) .finalize(), ) } diff --git a/matrix_sdk_crypto/src/verification/sas/inner_sas.rs b/matrix_sdk_crypto/src/verification/sas/inner_sas.rs index 034e4734..4eb1bc0d 100644 --- a/matrix_sdk_crypto/src/verification/sas/inner_sas.rs +++ b/matrix_sdk_crypto/src/verification/sas/inner_sas.rs @@ -17,12 +17,15 @@ use std::time::Instant; use std::sync::Arc; -use matrix_sdk_common::events::{ - key::verification::{ - accept::AcceptToDeviceEventContent, cancel::CancelCode, mac::MacToDeviceEventContent, - start::StartToDeviceEventContent, +use matrix_sdk_common::{ + events::{ + key::verification::{ + accept::AcceptToDeviceEventContent, cancel::CancelCode, mac::MacToDeviceEventContent, + start::StartToDeviceEventContent, + }, + AnyToDeviceEvent, AnyToDeviceEventContent, ToDeviceEvent, }, - AnyToDeviceEvent, AnyToDeviceEventContent, ToDeviceEvent, + identifiers::EventId, }; use crate::{ @@ -30,9 +33,12 @@ use crate::{ ReadOnlyAccount, }; -use super::sas_state::{ - Accepted, Canceled, Confirmed, Created, Done, FlowId, KeyReceived, MacReceived, SasState, - Started, +use super::{ + event_enums::OutgoingContent, + sas_state::{ + Accepted, Canceled, Confirmed, Created, Done, FlowId, KeyReceived, MacReceived, SasState, + Started, + }, }; #[derive(Clone, Debug)] @@ -52,12 +58,23 @@ impl InnerSas { account: ReadOnlyAccount, other_device: ReadOnlyDevice, other_identity: Option, - ) -> (InnerSas, StartToDeviceEventContent) { + ) -> (InnerSas, OutgoingContent) { let sas = SasState::::new(account, other_device, other_identity); let content = sas.as_content(); (InnerSas::Created(sas), content) } + pub fn start_in_room( + event_id: EventId, + account: ReadOnlyAccount, + other_device: ReadOnlyDevice, + other_identity: Option, + ) -> (InnerSas, OutgoingContent) { + let sas = SasState::::new_in_room(event_id, account, other_device, other_identity); + let content = sas.as_content(); + (InnerSas::Created(sas), content) + } + pub fn from_start_event( account: ReadOnlyAccount, other_device: ReadOnlyDevice, diff --git a/matrix_sdk_crypto/src/verification/sas/mod.rs b/matrix_sdk_crypto/src/verification/sas/mod.rs index 6d9f3ffe..3f531b17 100644 --- a/matrix_sdk_crypto/src/verification/sas/mod.rs +++ b/matrix_sdk_crypto/src/verification/sas/mod.rs @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +mod event_enums; mod helpers; mod inner_sas; mod sas_state; @@ -46,6 +47,8 @@ pub use helpers::content_to_request; use inner_sas::InnerSas; pub use sas_state::FlowId; +pub use event_enums::{OutgoingContent, StartContent}; + #[derive(Debug)] /// A result of a verification flow. pub enum VerificationResult { @@ -106,6 +109,30 @@ impl Sas { self.inner.lock().unwrap().set_creation_time(time) } + fn start_helper( + inner_sas: InnerSas, + content: OutgoingContent, + account: ReadOnlyAccount, + private_identity: PrivateCrossSigningIdentity, + other_device: ReadOnlyDevice, + store: Arc>, + other_identity: Option, + ) -> (Sas, OutgoingContent) { + let flow_id = inner_sas.verification_flow_id(); + + let sas = Sas { + inner: Arc::new(Mutex::new(inner_sas)), + account, + private_identity, + store, + other_device, + flow_id, + other_identity, + }; + + (sas, content) + } + /// Start a new SAS auth flow with the given device. /// /// # Arguments @@ -122,25 +149,60 @@ impl Sas { other_device: ReadOnlyDevice, store: Arc>, other_identity: Option, - ) -> (Sas, StartToDeviceEventContent) { + ) -> (Sas, OutgoingContent) { let (inner, content) = InnerSas::start( account.clone(), other_device.clone(), other_identity.clone(), ); - let flow_id = inner.verification_flow_id(); - let sas = Sas { - inner: Arc::new(Mutex::new(inner)), + Self::start_helper( + inner, + content, account, private_identity, - store, other_device, - flow_id, + store, other_identity, - }; + ) + } - (sas, content) + /// Start a new SAS auth flow with the given device inside the given room. + /// + /// # 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. + #[allow(dead_code)] + pub(crate) fn start_in_room( + flow_id: EventId, + room_id: RoomId, + account: ReadOnlyAccount, + private_identity: PrivateCrossSigningIdentity, + other_device: ReadOnlyDevice, + store: Arc>, + other_identity: Option, + ) -> (Sas, OutgoingContent) { + let (inner, content) = InnerSas::start_in_room( + flow_id, + account.clone(), + other_device.clone(), + other_identity.clone(), + ); + + Self::start_helper( + inner, + content, + account, + private_identity, + other_device, + store, + other_identity, + ) } /// Create a new Sas object from a m.key.verification.start request. diff --git a/matrix_sdk_crypto/src/verification/sas/sas_state.rs b/matrix_sdk_crypto/src/verification/sas/sas_state.rs index 817bb247..44e23f3b 100644 --- a/matrix_sdk_crypto/src/verification/sas/sas_state.rs +++ b/matrix_sdk_crypto/src/verification/sas/sas_state.rs @@ -30,19 +30,25 @@ use matrix_sdk_common::{ cancel::{CancelCode, CancelToDeviceEventContent}, key::KeyToDeviceEventContent, mac::MacToDeviceEventContent, - start::{MSasV1Content, MSasV1ContentInit, StartMethod, StartToDeviceEventContent}, - HashAlgorithm, KeyAgreementProtocol, MessageAuthenticationCode, + start::{ + MSasV1Content, MSasV1ContentInit, StartEventContent, StartMethod, + StartToDeviceEventContent, + }, + HashAlgorithm, KeyAgreementProtocol, MessageAuthenticationCode, Relation, ShortAuthenticationString, VerificationMethod, }, - AnyToDeviceEventContent, ToDeviceEvent, + AnyMessageEventContent, AnyToDeviceEventContent, ToDeviceEvent, }, - identifiers::{DeviceId, RoomId, UserId}, + identifiers::{DeviceId, EventId, UserId}, uuid::Uuid, }; use tracing::error; -use super::helpers::{ - calculate_commitment, get_decimal, get_emoji, get_mac_content, receive_mac_event, SasIds, +use super::{ + event_enums::{OutgoingContent, StartContent}, + helpers::{ + calculate_commitment, get_decimal, get_emoji, get_mac_content, receive_mac_event, SasIds, + }, }; use crate::{ @@ -68,7 +74,7 @@ const MAX_EVENT_TIMEOUT: Duration = Duration::from_secs(60); #[derive(Clone, Debug)] pub enum FlowId { ToDevice(String), - InRoom(RoomId), + InRoom(EventId), } impl FlowId { @@ -200,7 +206,7 @@ pub struct Started { #[derive(Clone, Debug)] pub struct Accepted { accepted_protocols: Arc, - start_content: Arc, + start_content: Arc, commitment: String, } @@ -310,13 +316,45 @@ impl SasState { /// * `account` - Our own account. /// /// * `other_device` - The other device which we are going to verify. + /// + /// * `other_identity` - The identity of the other user if one exists. pub fn new( account: ReadOnlyAccount, other_device: ReadOnlyDevice, other_identity: Option, ) -> SasState { - let verification_flow_id = Uuid::new_v4().to_string(); + let flow_id = FlowId::ToDevice(Uuid::new_v4().to_string()); + Self::new_helper(flow_id, account, other_device, other_identity) + } + /// Create a new SAS in-room verification flow. + /// + /// # Arguments + /// + /// * `event_id` - The event id of the `m.key.verification.request` event + /// that started the verification flow. + /// + /// * `account` - Our own account. + /// + /// * `other_device` - The other device which we are going to verify. + /// + /// * `other_identity` - The identity of the other user if one exists. + pub fn new_in_room( + event_id: EventId, + account: ReadOnlyAccount, + other_device: ReadOnlyDevice, + other_identity: Option, + ) -> SasState { + let flow_id = FlowId::InRoom(event_id); + Self::new_helper(flow_id, account, other_device, other_identity) + } + + fn new_helper( + flow_id: FlowId, + account: ReadOnlyAccount, + other_device: ReadOnlyDevice, + other_identity: Option, + ) -> SasState { SasState { inner: Arc::new(Mutex::new(OlmSas::new())), ids: SasIds { @@ -324,7 +362,7 @@ impl SasState { other_device, other_identity, }, - verification_flow_id: FlowId::ToDevice(verification_flow_id).into(), + verification_flow_id: flow_id.into(), creation_time: Arc::new(Instant::now()), last_event_time: Arc::new(Instant::now()), @@ -340,18 +378,34 @@ impl SasState { } } + pub fn as_start_content(&self) -> StartContent { + match self.verification_flow_id.as_ref() { + FlowId::ToDevice(s) => StartContent::ToDevice(StartToDeviceEventContent { + transaction_id: self.verification_flow_id.to_string(), + from_device: self.device_id().into(), + method: StartMethod::MSasV1( + MSasV1Content::new(self.state.protocol_definitions.clone()) + .expect("Invalid initial protocol definitions."), + ), + }), + FlowId::InRoom(e) => StartContent::Room(StartEventContent { + from_device: self.device_id().into(), + method: StartMethod::MSasV1( + MSasV1Content::new(self.state.protocol_definitions.clone()) + .expect("Invalid initial protocol definitions."), + ), + relation: Relation { + event_id: e.clone(), + }, + }), + } + } + /// Get the content for the start event. /// /// The content needs to be sent to the other device. - pub fn as_content(&self) -> StartToDeviceEventContent { - StartToDeviceEventContent { - transaction_id: self.verification_flow_id.to_string(), - from_device: self.device_id().into(), - method: StartMethod::MSasV1( - MSasV1Content::new(self.state.protocol_definitions.clone()) - .expect("Invalid initial protocol definitions."), - ), - } + pub fn as_content(&self) -> OutgoingContent { + self.as_start_content().into() } /// Receive a m.key.verification.accept event, changing the state into @@ -372,7 +426,7 @@ impl SasState { let accepted_protocols = AcceptedProtocols::try_from(content.clone()).map_err(|c| self.clone().cancel(c))?; - let start_content = self.as_content().into(); + let start_content = self.as_start_content().into(); Ok(SasState { inner: self.inner, @@ -416,7 +470,7 @@ impl SasState { let sas = OlmSas::new(); let pubkey = sas.public_key(); - let commitment = calculate_commitment(&pubkey, &event.content); + let commitment = calculate_commitment(&pubkey, event.content.clone()); error!( "Calculated commitment for pubkey {} and content {:?} {}", @@ -565,7 +619,10 @@ impl SasState { self.check_event(&event.sender, &event.content.transaction_id) .map_err(|c| self.clone().cancel(c))?; - let commitment = calculate_commitment(&event.content.key, &self.state.start_content); + let commitment = calculate_commitment( + &event.content.key, + self.state.start_content.as_ref().clone(), + ); if self.state.commitment != commitment { Err(self.cancel(CancelCode::InvalidMessage))