From 5babd71341fc5ec3b0d03e23a4f4c08e80e43fae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Wed, 9 Dec 2020 17:16:03 +0100 Subject: [PATCH 01/20] crypto: Copy the relates to field to the unencrypted content when encrypting --- .../src/olm/group_sessions/outbound.rs | 39 ++++++++++++++----- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/matrix_sdk_crypto/src/olm/group_sessions/outbound.rs b/matrix_sdk_crypto/src/olm/group_sessions/outbound.rs index adecc4e1..700563df 100644 --- a/matrix_sdk_crypto/src/olm/group_sessions/outbound.rs +++ b/matrix_sdk_crypto/src/olm/group_sessions/outbound.rs @@ -13,7 +13,14 @@ // limitations under the License. use dashmap::{DashMap, DashSet}; -use matrix_sdk_common::{api::r0::to_device::DeviceIdOrAllDevices, uuid::Uuid}; +use matrix_sdk_common::{ + api::r0::to_device::DeviceIdOrAllDevices, + events::room::{ + encrypted::{MegolmV1AesSha2Content, MegolmV1AesSha2ContentInit}, + message::Relation, + }, + uuid::Uuid, +}; use std::{ cmp::min, fmt, @@ -240,19 +247,31 @@ impl OutboundGroupSession { "type": content.event_type(), }); + let relates_to: Option = json_content + .get("content") + .map(|c| { + c.get("m.relates_to") + .cloned() + .map(|r| serde_json::from_value(r).ok()) + }) + .flatten() + .flatten(); + let plaintext = json_content.to_string(); let ciphertext = self.encrypt_helper(plaintext).await; - EncryptedEventContent::MegolmV1AesSha2( - matrix_sdk_common::events::room::encrypted::MegolmV1AesSha2ContentInit { - ciphertext, - sender_key: self.account_identity_keys.curve25519().to_owned(), - session_id: self.session_id().to_owned(), - device_id: (&*self.device_id).to_owned(), - } - .into(), - ) + let mut encrypted_content: MegolmV1AesSha2Content = MegolmV1AesSha2ContentInit { + ciphertext, + sender_key: self.account_identity_keys.curve25519().to_owned(), + session_id: self.session_id().to_owned(), + device_id: (&*self.device_id).to_owned(), + } + .into(); + + encrypted_content.relates_to = relates_to; + + EncryptedEventContent::MegolmV1AesSha2(encrypted_content) } /// Check if the session has expired and if it should be rotated. From 7198b0daba29c22af0be67f758fc247d1b3800b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Wed, 9 Dec 2020 17:18:23 +0100 Subject: [PATCH 02/20] crypto: WIP key verification request handling. --- matrix_sdk/examples/emoji_verification.rs | 44 ++- matrix_sdk/src/client.rs | 14 + matrix_sdk/src/lib.rs | 2 + matrix_sdk/src/verification_request.rs | 41 +++ matrix_sdk_common/Cargo.toml | 2 +- matrix_sdk_crypto/src/lib.rs | 2 +- matrix_sdk_crypto/src/machine.rs | 18 +- matrix_sdk_crypto/src/verification/machine.rs | 64 ++++- matrix_sdk_crypto/src/verification/mod.rs | 2 + .../src/verification/requests.rs | 262 ++++++++++++++++++ matrix_sdk_crypto/src/verification/sas/mod.rs | 4 +- .../src/verification/sas/sas_state.rs | 9 +- 12 files changed, 439 insertions(+), 25 deletions(-) create mode 100644 matrix_sdk/src/verification_request.rs create mode 100644 matrix_sdk_crypto/src/verification/requests.rs diff --git a/matrix_sdk/examples/emoji_verification.rs b/matrix_sdk/examples/emoji_verification.rs index c903ec19..04f1375c 100644 --- a/matrix_sdk/examples/emoji_verification.rs +++ b/matrix_sdk/examples/emoji_verification.rs @@ -1,9 +1,20 @@ -use std::{env, io, process::exit}; +use std::{ + env, io, + process::exit, + sync::{ + atomic::{AtomicBool, Ordering}, + Arc, + }, +}; use url::Url; use matrix_sdk::{ - self, events::AnyToDeviceEvent, identifiers::UserId, Client, ClientConfig, LoopCtrl, Sas, - SyncSettings, + self, + events::{ + room::message::MessageEventContent, AnySyncMessageEvent, AnySyncRoomEvent, AnyToDeviceEvent, + }, + identifiers::UserId, + Client, ClientConfig, LoopCtrl, Sas, SyncSettings, }; async fn wait_for_confirmation(client: Client, sas: Sas) { @@ -68,10 +79,13 @@ async fn login( .await?; let client_ref = &client; + let initial_sync = Arc::new(AtomicBool::from(true)); + let initial_ref = &initial_sync; client .sync_with_callback(SyncSettings::new(), |response| async move { let client = &client_ref; + let initial = &initial_ref; for event in &response.to_device.events { let e = event @@ -118,6 +132,30 @@ async fn login( } } + if !initial.load(Ordering::SeqCst) { + for (_room_id, room_info) in response.rooms.join { + for event in room_info.timeline.events { + if let Ok(AnySyncRoomEvent::Message(AnySyncMessageEvent::RoomMessage(m))) = + event.deserialize() + { + if let MessageEventContent::VerificationRequest(_) = &m.content { + let request = client + .get_verification_request(&m.event_id) + .await + .expect("Request object wasn't created"); + + request + .accept() + .await + .expect("Can't accept verification request"); + } + } + } + } + } + + initial.store(false, Ordering::SeqCst); + LoopCtrl::Continue }) .await; diff --git a/matrix_sdk/src/client.rs b/matrix_sdk/src/client.rs index e199e2df..3d695be4 100644 --- a/matrix_sdk/src/client.rs +++ b/matrix_sdk/src/client.rs @@ -118,6 +118,7 @@ use matrix_sdk_common::{ use crate::{ http_client::{client_with_config, HttpClient, HttpSend}, + verification_request::VerificationRequest, Error, EventEmitter, OutgoingRequest, Result, }; @@ -1894,6 +1895,19 @@ impl Client { }) } + /// Get a `VerificationRequest` object with the given flow id. + #[cfg(feature = "encryption")] + #[cfg_attr(feature = "docs", doc(cfg(encryption)))] + pub async fn get_verification_request(&self, flow_id: &EventId) -> Option { + let olm = self.base_client.olm_machine().await?; + + olm.get_verification_request(flow_id) + .map(|r| VerificationRequest { + inner: r, + client: self.clone(), + }) + } + /// Get a specific device of a user. /// /// # Arguments diff --git a/matrix_sdk/src/lib.rs b/matrix_sdk/src/lib.rs index 3cb65f27..2c18b393 100644 --- a/matrix_sdk/src/lib.rs +++ b/matrix_sdk/src/lib.rs @@ -88,6 +88,8 @@ mod http_client; mod device; #[cfg(feature = "encryption")] mod sas; +#[cfg(feature = "encryption")] +mod verification_request; pub use client::{Client, ClientConfig, LoopCtrl, SyncSettings}; #[cfg(feature = "encryption")] diff --git a/matrix_sdk/src/verification_request.rs b/matrix_sdk/src/verification_request.rs new file mode 100644 index 00000000..88c5ddd0 --- /dev/null +++ b/matrix_sdk/src/verification_request.rs @@ -0,0 +1,41 @@ +// 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. + +use matrix_sdk_base::{ + crypto::VerificationRequest as BaseVerificationRequest, events::AnyMessageEventContent, +}; + +use crate::{Client, Result}; + +/// An object controling the interactive verification flow. +#[derive(Debug, Clone)] +pub struct VerificationRequest { + pub(crate) inner: BaseVerificationRequest, + pub(crate) client: Client, +} + +impl VerificationRequest { + /// Accept the interactive verification flow. + pub async fn accept(&self) -> Result<()> { + if let Some(content) = self.inner.accept() { + let content = AnyMessageEventContent::KeyVerificationReady(content); + + self.client + .room_send(self.inner.room_id(), content, None) + .await?; + } + + Ok(()) + } +} diff --git a/matrix_sdk_common/Cargo.toml b/matrix_sdk_common/Cargo.toml index d3c6dcb7..2067d1f4 100644 --- a/matrix_sdk_common/Cargo.toml +++ b/matrix_sdk_common/Cargo.toml @@ -21,7 +21,7 @@ js_int = "0.1.9" [dependencies.ruma] version = "0.0.1" git = "https://github.com/ruma/ruma" -rev = "e8882fe8142d7b55ed4c8ccc6150946945f9e237" +rev = "1a4e9aa20abff4786dbb91a19fb72c1dfa4410a7" features = ["client-api", "unstable-pre-spec"] [target.'cfg(not(target_arch = "wasm32"))'.dependencies] diff --git a/matrix_sdk_crypto/src/lib.rs b/matrix_sdk_crypto/src/lib.rs index f37cf97e..fd31a94a 100644 --- a/matrix_sdk_crypto/src/lib.rs +++ b/matrix_sdk_crypto/src/lib.rs @@ -53,4 +53,4 @@ pub(crate) use olm::ReadOnlyAccount; pub use requests::{ IncomingResponse, KeysQueryRequest, OutgoingRequest, OutgoingRequests, ToDeviceRequest, }; -pub use verification::Sas; +pub use verification::{Sas, VerificationRequest}; diff --git a/matrix_sdk_crypto/src/machine.rs b/matrix_sdk_crypto/src/machine.rs index 2cdab741..b290bf54 100644 --- a/matrix_sdk_crypto/src/machine.rs +++ b/matrix_sdk_crypto/src/machine.rs @@ -36,7 +36,8 @@ use matrix_sdk_common::{ ToDeviceEvent, }, identifiers::{ - DeviceId, DeviceIdBox, DeviceKeyAlgorithm, EventEncryptionAlgorithm, RoomId, UserId, + DeviceId, DeviceIdBox, DeviceKeyAlgorithm, EventEncryptionAlgorithm, EventId, RoomId, + UserId, }, js_int::UInt, locks::Mutex, @@ -44,8 +45,6 @@ use matrix_sdk_common::{ Raw, }; -#[cfg(feature = "sqlite_cryptostore")] -use crate::store::sqlite::SqliteStore; use crate::{ error::{EventError, MegolmError, MegolmResult, OlmError, OlmResult}, identities::{Device, IdentityManager, UserDevices}, @@ -64,6 +63,8 @@ use crate::{ verification::{Sas, VerificationMachine}, ToDeviceRequest, }; +#[cfg(feature = "sqlite_cryptostore")] +use crate::{store::sqlite::SqliteStore, verification::VerificationRequest}; /// State machine implementation of the Olm/Megolm encryption protocol used for /// Matrix end to end encryption. @@ -767,6 +768,11 @@ impl OlmMachine { self.verification_machine.get_sas(flow_id) } + /// Get a verification request object with the given flow id. + pub fn get_verification_request(&self, flow_id: &EventId) -> Option { + self.verification_machine.get_request(flow_id) + } + async fn update_one_time_key_count(&self, key_count: &BTreeMap) { self.account.update_uploaded_key_count(key_count).await; } @@ -924,6 +930,12 @@ impl OlmMachine { // TODO set the encryption info on the event (is it verified, was it // decrypted, sender key...) + if let Ok(e) = decrypted_event.deserialize() { + self.verification_machine + .receive_room_event(room_id, &e) + .await?; + } + Ok(decrypted_event) } diff --git a/matrix_sdk_crypto/src/verification/machine.rs b/matrix_sdk_crypto/src/verification/machine.rs index c81344c3..5efa5b5d 100644 --- a/matrix_sdk_crypto/src/verification/machine.rs +++ b/matrix_sdk_crypto/src/verification/machine.rs @@ -16,16 +16,23 @@ use std::sync::Arc; use dashmap::DashMap; -use matrix_sdk_common::locks::Mutex; -use tracing::{trace, warn}; +use tracing::{info, trace, warn}; use matrix_sdk_common::{ - events::{AnyToDeviceEvent, AnyToDeviceEventContent}, - identifiers::{DeviceId, UserId}, + events::{ + room::message::MessageEventContent, AnySyncMessageEvent, AnySyncRoomEvent, + AnyToDeviceEvent, AnyToDeviceEventContent, + }, + identifiers::{DeviceId, EventId, RoomId, UserId}, + locks::Mutex, uuid::Uuid, }; -use super::sas::{content_to_request, Sas, VerificationResult}; +use super::{ + requests::VerificationRequest, + sas::{content_to_request, Sas, VerificationResult}, +}; + use crate::{ olm::PrivateCrossSigningIdentity, requests::{OutgoingRequest, ToDeviceRequest}, @@ -39,6 +46,7 @@ pub struct VerificationMachine { private_identity: Arc>, pub(crate) store: Arc>, verifications: Arc>, + requests: Arc>, outgoing_to_device_messages: Arc>, } @@ -52,8 +60,9 @@ impl VerificationMachine { account, private_identity: identity, store, - verifications: Arc::new(DashMap::new()), - outgoing_to_device_messages: Arc::new(DashMap::new()), + verifications: DashMap::new().into(), + requests: DashMap::new().into(), + outgoing_to_device_messages: DashMap::new().into(), } } @@ -84,6 +93,11 @@ impl VerificationMachine { Ok((sas, request)) } + pub fn get_request(&self, flow_id: &EventId) -> Option { + #[allow(clippy::map_clone)] + self.requests.get(flow_id).map(|s| s.clone()) + } + pub fn get_sas(&self, transaction_id: &str) -> Option { #[allow(clippy::map_clone)] self.verifications.get(transaction_id).map(|s| s.clone()) @@ -106,7 +120,7 @@ impl VerificationMachine { self.outgoing_to_device_messages.insert(request_id, request); } - fn receive_event_helper(&self, sas: &Sas, event: &mut AnyToDeviceEvent) { + fn receive_event_helper(&self, sas: &Sas, event: &AnyToDeviceEvent) { if let Some(c) = sas.receive_event(event) { self.queue_up_content(sas.other_user_id(), sas.other_device_id(), c); } @@ -141,10 +155,40 @@ impl VerificationMachine { } } - pub async fn receive_event( + pub async fn receive_room_event( &self, - event: &mut AnyToDeviceEvent, + room_id: &RoomId, + event: &AnySyncRoomEvent, ) -> Result<(), CryptoStoreError> { + match event { + AnySyncRoomEvent::Message(AnySyncMessageEvent::RoomMessage(m)) => { + if let MessageEventContent::VerificationRequest(r) = &m.content { + if self.account.user_id() == &r.to { + info!( + "Received a new verification request from {} {}", + m.sender, r.from_device + ); + + let request = VerificationRequest::from_request_event( + room_id, + self.account.user_id(), + self.account.device_id(), + &m.sender, + &m.event_id, + r, + ); + + self.requests.insert(m.event_id.clone(), request); + } + } + } + _ => (), + } + + Ok(()) + } + + pub async fn receive_event(&self, event: &AnyToDeviceEvent) -> Result<(), CryptoStoreError> { trace!("Received a key verification event {:?}", event); match event { diff --git a/matrix_sdk_crypto/src/verification/mod.rs b/matrix_sdk_crypto/src/verification/mod.rs index d93dc8ec..0e170a35 100644 --- a/matrix_sdk_crypto/src/verification/mod.rs +++ b/matrix_sdk_crypto/src/verification/mod.rs @@ -13,9 +13,11 @@ // limitations under the License. mod machine; +mod requests; mod sas; pub use machine::VerificationMachine; +pub use requests::VerificationRequest; pub use sas::{Sas, VerificationResult}; #[cfg(test)] diff --git a/matrix_sdk_crypto/src/verification/requests.rs b/matrix_sdk_crypto/src/verification/requests.rs new file mode 100644 index 00000000..8e201fda --- /dev/null +++ b/matrix_sdk_crypto/src/verification/requests.rs @@ -0,0 +1,262 @@ +// 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::sync::{Arc, Mutex}; + +use matrix_sdk_common::{ + api::r0::message::send_message_event::Response as RoomMessageResponse, + events::{ + key::verification::{ready::ReadyEventContent, Relation, VerificationMethod}, + room::message::KeyVerificationRequestEventContent, + }, + identifiers::{DeviceId, DeviceIdBox, EventId, RoomId, UserId}, +}; + +const SUPPORTED_METHODS: &[VerificationMethod] = &[VerificationMethod::MSasV1]; + +#[derive(Clone, Debug)] +/// TODO +pub struct VerificationRequest { + inner: Arc>, + room_id: Arc, +} + +impl VerificationRequest { + pub(crate) fn from_request_event( + room_id: &RoomId, + own_user_id: &UserId, + own_device_id: &DeviceId, + sender: &UserId, + event_id: &EventId, + content: &KeyVerificationRequestEventContent, + ) -> Self { + Self { + inner: Arc::new(Mutex::new(InnerRequest::Requested( + RequestState::from_request_event( + own_user_id, + own_device_id, + sender, + event_id, + content, + ), + ))), + room_id: room_id.clone().into(), + } + } + + /// The room id where the verification is happening. + pub fn room_id(&self) -> &RoomId { + &self.room_id + } + + /// Accept the verification request. + pub fn accept(&self) -> Option { + self.inner.lock().unwrap().accept() + } +} + +#[derive(Debug)] +enum InnerRequest { + Created(RequestState), + Sent(RequestState), + Requested(RequestState), + Ready(RequestState), + Accepted(RequestState), + Passive(RequestState), +} + +impl InnerRequest { + fn accept(&mut self) -> Option { + if let InnerRequest::Requested(s) = self { + let (state, content) = s.clone().accept(); + *self = InnerRequest::Accepted(state); + + Some(content) + } else { + None + } + } +} + +#[derive(Clone, Debug)] +struct RequestState { + /// Our own user id. + pub own_user_id: UserId, + + /// Our own device id. + pub own_device_id: DeviceIdBox, + + /// The id of the user which is participating in this verification request. + pub other_user_id: UserId, + + /// The verification request state we are in. + state: S, +} + +#[derive(Clone, Debug)] +struct Created {} + +impl RequestState { + fn as_content(&self) -> KeyVerificationRequestEventContent { + KeyVerificationRequestEventContent { + body: format!( + "{} is requesting to verify your key, but your client does not \ + support in-chat key verification. You will need to use legacy \ + key verification to verify keys.", + self.own_user_id + ), + methods: SUPPORTED_METHODS.to_vec(), + from_device: self.own_device_id.clone(), + to: self.other_user_id.clone(), + } + } + + fn into_sent(self, response: &RoomMessageResponse) -> RequestState { + RequestState { + own_user_id: self.own_user_id, + own_device_id: self.own_device_id, + other_user_id: self.other_user_id, + state: Sent { + methods: SUPPORTED_METHODS.to_vec(), + flow_id: response.event_id.clone(), + } + .into(), + } + } +} + +#[derive(Clone, Debug)] +struct Sent { + /// The verification methods supported by the sender. + pub methods: Vec, + + /// The event id of our `m.key.verification.request` event which acts as an + /// unique id identifying this verification flow. + pub flow_id: EventId, +} + +impl RequestState { + fn into_ready(self, _sender: &UserId, content: &ReadyEventContent) -> RequestState { + // TODO check the flow id, and that the methods match what we suggested. + RequestState { + own_user_id: self.own_user_id, + own_device_id: self.own_device_id, + other_user_id: self.other_user_id, + state: Ready { + methods: content.methods.to_owned(), + other_device_id: content.from_device.clone(), + flow_id: self.state.flow_id.clone(), + } + .into(), + } + } +} + +#[derive(Clone, Debug)] +struct Requested { + /// The verification methods supported by the sender. + pub methods: Vec, + + /// The event id of the `m.key.verification.request` event which acts as an + /// unique id identifying this verification flow. + pub flow_id: EventId, + + /// The device id of the device that responded to the verification request. + pub other_device_id: DeviceIdBox, +} + +impl RequestState { + fn from_request_event( + own_user_id: &UserId, + own_device_id: &DeviceId, + sender: &UserId, + event_id: &EventId, + content: &KeyVerificationRequestEventContent, + ) -> RequestState { + // TODO only create this if we suport the methods + RequestState { + own_user_id: own_user_id.clone(), + own_device_id: own_device_id.into(), + other_user_id: sender.clone(), + state: Requested { + methods: content.methods.clone(), + flow_id: event_id.clone(), + other_device_id: content.from_device.clone(), + } + .into(), + } + } + + fn accept(self) -> (RequestState, ReadyEventContent) { + // TODO let the user pick a method here. + 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 { + methods: self.state.methods.clone(), + other_device_id: self.state.other_device_id.clone(), + flow_id: self.state.flow_id.clone(), + }, + }; + + let content = ReadyEventContent { + from_device: self.own_device_id, + methods: self.state.methods, + relation: Relation { + event_id: self.state.flow_id, + }, + }; + + (state, content) + } +} + +#[derive(Clone, Debug)] +struct Ready { + /// The verification methods supported by the sender. + pub methods: Vec, + + /// 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, +} + +#[derive(Clone, Debug)] +struct Accepted { + /// The verification methods that were accepted + pub methods: Vec, + + /// 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, +} + +#[derive(Clone, Debug)] +struct Passive { + /// 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, +} diff --git a/matrix_sdk_crypto/src/verification/sas/mod.rs b/matrix_sdk_crypto/src/verification/sas/mod.rs index d6f1322f..6ee7f2bb 100644 --- a/matrix_sdk_crypto/src/verification/sas/mod.rs +++ b/matrix_sdk_crypto/src/verification/sas/mod.rs @@ -514,7 +514,7 @@ impl Sas { pub(crate) fn receive_event( &self, - event: &mut AnyToDeviceEvent, + event: &AnyToDeviceEvent, ) -> Option { let mut guard = self.inner.lock().unwrap(); let sas: InnerSas = (*guard).clone(); @@ -628,7 +628,7 @@ impl InnerSas { fn receive_event( self, - event: &mut AnyToDeviceEvent, + event: &AnyToDeviceEvent, ) -> (InnerSas, Option) { match event { AnyToDeviceEvent::KeyVerificationAccept(e) => { diff --git a/matrix_sdk_crypto/src/verification/sas/sas_state.rs b/matrix_sdk_crypto/src/verification/sas/sas_state.rs index ab136cae..8ddd0e9c 100644 --- a/matrix_sdk_crypto/src/verification/sas/sas_state.rs +++ b/matrix_sdk_crypto/src/verification/sas/sas_state.rs @@ -14,7 +14,6 @@ use std::{ convert::TryFrom, - mem, sync::{Arc, Mutex}, time::{Duration, Instant}, }; @@ -498,14 +497,14 @@ impl SasState { /// anymore. pub fn into_key_received( self, - event: &mut ToDeviceEvent, + event: &ToDeviceEvent, ) -> Result, SasState> { self.check_event(&event.sender, &event.content.transaction_id) .map_err(|c| self.clone().cancel(c))?; let accepted_protocols = AcceptedProtocols::default(); - let their_pubkey = mem::take(&mut event.content.key); + let their_pubkey = event.content.key.clone(); self.inner .lock() @@ -539,7 +538,7 @@ impl SasState { /// anymore. pub fn into_key_received( self, - event: &mut ToDeviceEvent, + event: &ToDeviceEvent, ) -> Result, SasState> { self.check_event(&event.sender, &event.content.transaction_id) .map_err(|c| self.clone().cancel(c))?; @@ -549,7 +548,7 @@ impl SasState { if self.state.commitment != commitment { Err(self.cancel(CancelCode::InvalidMessage)) } else { - let their_pubkey = mem::take(&mut event.content.key); + let their_pubkey = event.content.key.clone(); self.inner .lock() From b9ddbb11afa56795b9bbbddf24224c10d54798d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Thu, 10 Dec 2020 14:07:47 +0100 Subject: [PATCH 03/20] crypto: Move the inner sas struct into a separate module. --- .../src/verification/sas/inner_sas.rs | 267 ++++++++++++++++++ matrix_sdk_crypto/src/verification/sas/mod.rs | 246 +--------------- 2 files changed, 275 insertions(+), 238 deletions(-) create mode 100644 matrix_sdk_crypto/src/verification/sas/inner_sas.rs diff --git a/matrix_sdk_crypto/src/verification/sas/inner_sas.rs b/matrix_sdk_crypto/src/verification/sas/inner_sas.rs new file mode 100644 index 00000000..6a566990 --- /dev/null +++ b/matrix_sdk_crypto/src/verification/sas/inner_sas.rs @@ -0,0 +1,267 @@ +// 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. + +#[cfg(test)] +use std::time::Instant; + +use std::sync::Arc; + +use matrix_sdk_common::events::{ + key::verification::{ + accept::AcceptToDeviceEventContent, cancel::CancelCode, mac::MacToDeviceEventContent, + start::StartToDeviceEventContent, + }, + AnyToDeviceEvent, AnyToDeviceEventContent, ToDeviceEvent, +}; + +use crate::{ + identities::{ReadOnlyDevice, UserIdentities}, + ReadOnlyAccount, +}; + +use super::sas_state::{ + Accepted, Canceled, Confirmed, Created, Done, KeyReceived, MacReceived, SasState, Started, +}; + +#[derive(Clone, Debug)] +pub enum InnerSas { + Created(SasState), + Started(SasState), + Accepted(SasState), + KeyRecieved(SasState), + Confirmed(SasState), + MacReceived(SasState), + Done(SasState), + Canceled(SasState), +} + +impl InnerSas { + pub fn start( + account: ReadOnlyAccount, + other_device: ReadOnlyDevice, + other_identity: Option, + ) -> (InnerSas, StartToDeviceEventContent) { + let sas = SasState::::new(account, other_device, other_identity); + let content = sas.as_content(); + (InnerSas::Created(sas), content) + } + + pub fn from_start_event( + account: ReadOnlyAccount, + other_device: ReadOnlyDevice, + event: &ToDeviceEvent, + other_identity: Option, + ) -> Result { + match SasState::::from_start_event(account, other_device, event, other_identity) { + Ok(s) => Ok(InnerSas::Started(s)), + Err(s) => Err(s.as_content()), + } + } + + pub fn accept(&self) -> Option { + if let InnerSas::Started(s) = self { + Some(s.as_content()) + } else { + None + } + } + + #[cfg(test)] + #[allow(dead_code)] + pub fn set_creation_time(&mut self, time: Instant) { + match self { + InnerSas::Created(s) => s.set_creation_time(time), + InnerSas::Started(s) => s.set_creation_time(time), + InnerSas::Canceled(s) => s.set_creation_time(time), + InnerSas::Accepted(s) => s.set_creation_time(time), + InnerSas::KeyRecieved(s) => s.set_creation_time(time), + InnerSas::Confirmed(s) => s.set_creation_time(time), + InnerSas::MacReceived(s) => s.set_creation_time(time), + InnerSas::Done(s) => s.set_creation_time(time), + } + } + + pub fn cancel(self, code: CancelCode) -> (InnerSas, Option) { + let sas = match self { + InnerSas::Created(s) => s.cancel(code), + InnerSas::Started(s) => s.cancel(code), + InnerSas::Accepted(s) => s.cancel(code), + InnerSas::KeyRecieved(s) => s.cancel(code), + InnerSas::MacReceived(s) => s.cancel(code), + _ => return (self, None), + }; + + let content = sas.as_content(); + + (InnerSas::Canceled(sas), Some(content)) + } + + pub fn confirm(self) -> (InnerSas, Option) { + match self { + InnerSas::KeyRecieved(s) => { + let sas = s.confirm(); + let content = sas.as_content(); + (InnerSas::Confirmed(sas), Some(content)) + } + InnerSas::MacReceived(s) => { + let sas = s.confirm(); + let content = sas.as_content(); + (InnerSas::Done(sas), Some(content)) + } + _ => (self, None), + } + } + + pub fn receive_event( + self, + event: &AnyToDeviceEvent, + ) -> (InnerSas, Option) { + match event { + AnyToDeviceEvent::KeyVerificationAccept(e) => { + if let InnerSas::Created(s) = self { + match s.into_accepted(e) { + Ok(s) => { + let content = s.as_content(); + ( + InnerSas::Accepted(s), + Some(AnyToDeviceEventContent::KeyVerificationKey(content)), + ) + } + Err(s) => { + let content = s.as_content(); + (InnerSas::Canceled(s), Some(content)) + } + } + } else { + (self, None) + } + } + AnyToDeviceEvent::KeyVerificationKey(e) => match self { + InnerSas::Accepted(s) => match s.into_key_received(e) { + Ok(s) => (InnerSas::KeyRecieved(s), None), + Err(s) => { + let content = s.as_content(); + (InnerSas::Canceled(s), Some(content)) + } + }, + InnerSas::Started(s) => match s.into_key_received(e) { + Ok(s) => { + let content = s.as_content(); + ( + InnerSas::KeyRecieved(s), + Some(AnyToDeviceEventContent::KeyVerificationKey(content)), + ) + } + Err(s) => { + let content = s.as_content(); + (InnerSas::Canceled(s), Some(content)) + } + }, + _ => (self, None), + }, + AnyToDeviceEvent::KeyVerificationMac(e) => match self { + InnerSas::KeyRecieved(s) => match s.into_mac_received(e) { + Ok(s) => (InnerSas::MacReceived(s), None), + Err(s) => { + let content = s.as_content(); + (InnerSas::Canceled(s), Some(content)) + } + }, + InnerSas::Confirmed(s) => match s.into_done(e) { + Ok(s) => (InnerSas::Done(s), None), + Err(s) => { + let content = s.as_content(); + (InnerSas::Canceled(s), Some(content)) + } + }, + _ => (self, None), + }, + _ => (self, None), + } + } + + pub fn can_be_presented(&self) -> bool { + match self { + InnerSas::KeyRecieved(_) => true, + InnerSas::MacReceived(_) => true, + _ => false, + } + } + + pub fn is_done(&self) -> bool { + matches!(self, InnerSas::Done(_)) + } + + pub fn is_canceled(&self) -> bool { + matches!(self, InnerSas::Canceled(_)) + } + + pub fn timed_out(&self) -> bool { + match self { + InnerSas::Created(s) => s.timed_out(), + InnerSas::Started(s) => s.timed_out(), + InnerSas::Canceled(s) => s.timed_out(), + InnerSas::Accepted(s) => s.timed_out(), + InnerSas::KeyRecieved(s) => s.timed_out(), + InnerSas::Confirmed(s) => s.timed_out(), + InnerSas::MacReceived(s) => s.timed_out(), + InnerSas::Done(s) => s.timed_out(), + } + } + + pub fn verification_flow_id(&self) -> Arc { + match self { + InnerSas::Created(s) => s.verification_flow_id.clone(), + InnerSas::Started(s) => s.verification_flow_id.clone(), + InnerSas::Canceled(s) => s.verification_flow_id.clone(), + InnerSas::Accepted(s) => s.verification_flow_id.clone(), + InnerSas::KeyRecieved(s) => s.verification_flow_id.clone(), + InnerSas::Confirmed(s) => s.verification_flow_id.clone(), + InnerSas::MacReceived(s) => s.verification_flow_id.clone(), + InnerSas::Done(s) => s.verification_flow_id.clone(), + } + } + + pub fn emoji(&self) -> Option> { + match self { + InnerSas::KeyRecieved(s) => Some(s.get_emoji()), + InnerSas::MacReceived(s) => Some(s.get_emoji()), + _ => None, + } + } + + pub fn decimals(&self) -> Option<(u16, u16, u16)> { + match self { + InnerSas::KeyRecieved(s) => Some(s.get_decimal()), + InnerSas::MacReceived(s) => Some(s.get_decimal()), + _ => None, + } + } + + pub fn verified_devices(&self) -> Option> { + if let InnerSas::Done(s) = self { + Some(s.verified_devices()) + } else { + None + } + } + + pub fn verified_identities(&self) -> Option> { + if let InnerSas::Done(s) = self { + Some(s.verified_identities()) + } else { + None + } + } +} diff --git a/matrix_sdk_crypto/src/verification/sas/mod.rs b/matrix_sdk_crypto/src/verification/sas/mod.rs index 6ee7f2bb..025f0fa7 100644 --- a/matrix_sdk_crypto/src/verification/sas/mod.rs +++ b/matrix_sdk_crypto/src/verification/sas/mod.rs @@ -13,6 +13,7 @@ // limitations under the License. mod helpers; +mod inner_sas; mod sas_state; #[cfg(test)] @@ -25,8 +26,8 @@ use matrix_sdk_common::{ api::r0::keys::upload_signatures::Request as SignatureUploadRequest, events::{ key::verification::{ - accept::AcceptToDeviceEventContent, cancel::CancelCode, mac::MacToDeviceEventContent, - start::StartToDeviceEventContent, + cancel::CancelCode, + start::{StartEventContent, StartToDeviceEventContent}, }, AnyToDeviceEvent, AnyToDeviceEventContent, ToDeviceEvent, }, @@ -42,9 +43,7 @@ use crate::{ }; pub use helpers::content_to_request; -use sas_state::{ - Accepted, Canceled, Confirmed, Created, Done, KeyReceived, MacReceived, SasState, Started, -}; +use inner_sas::InnerSas; #[derive(Debug)] /// A result of a verification flow. @@ -537,238 +536,6 @@ impl Sas { } } -#[derive(Clone, Debug)] -enum InnerSas { - Created(SasState), - Started(SasState), - Accepted(SasState), - KeyRecieved(SasState), - Confirmed(SasState), - MacReceived(SasState), - Done(SasState), - Canceled(SasState), -} - -impl InnerSas { - fn start( - account: ReadOnlyAccount, - other_device: ReadOnlyDevice, - other_identity: Option, - ) -> (InnerSas, StartToDeviceEventContent) { - let sas = SasState::::new(account, other_device, other_identity); - let content = sas.as_content(); - (InnerSas::Created(sas), content) - } - - fn from_start_event( - account: ReadOnlyAccount, - other_device: ReadOnlyDevice, - event: &ToDeviceEvent, - other_identity: Option, - ) -> Result { - match SasState::::from_start_event(account, other_device, event, other_identity) { - Ok(s) => Ok(InnerSas::Started(s)), - Err(s) => Err(s.as_content()), - } - } - - fn accept(&self) -> Option { - if let InnerSas::Started(s) = self { - Some(s.as_content()) - } else { - None - } - } - - #[cfg(test)] - #[allow(dead_code)] - fn set_creation_time(&mut self, time: Instant) { - match self { - InnerSas::Created(s) => s.set_creation_time(time), - InnerSas::Started(s) => s.set_creation_time(time), - InnerSas::Canceled(s) => s.set_creation_time(time), - InnerSas::Accepted(s) => s.set_creation_time(time), - InnerSas::KeyRecieved(s) => s.set_creation_time(time), - InnerSas::Confirmed(s) => s.set_creation_time(time), - InnerSas::MacReceived(s) => s.set_creation_time(time), - InnerSas::Done(s) => s.set_creation_time(time), - } - } - - fn cancel(self, code: CancelCode) -> (InnerSas, Option) { - let sas = match self { - InnerSas::Created(s) => s.cancel(code), - InnerSas::Started(s) => s.cancel(code), - InnerSas::Accepted(s) => s.cancel(code), - InnerSas::KeyRecieved(s) => s.cancel(code), - InnerSas::MacReceived(s) => s.cancel(code), - _ => return (self, None), - }; - - let content = sas.as_content(); - - (InnerSas::Canceled(sas), Some(content)) - } - - fn confirm(self) -> (InnerSas, Option) { - match self { - InnerSas::KeyRecieved(s) => { - let sas = s.confirm(); - let content = sas.as_content(); - (InnerSas::Confirmed(sas), Some(content)) - } - InnerSas::MacReceived(s) => { - let sas = s.confirm(); - let content = sas.as_content(); - (InnerSas::Done(sas), Some(content)) - } - _ => (self, None), - } - } - - fn receive_event( - self, - event: &AnyToDeviceEvent, - ) -> (InnerSas, Option) { - match event { - AnyToDeviceEvent::KeyVerificationAccept(e) => { - if let InnerSas::Created(s) = self { - match s.into_accepted(e) { - Ok(s) => { - let content = s.as_content(); - ( - InnerSas::Accepted(s), - Some(AnyToDeviceEventContent::KeyVerificationKey(content)), - ) - } - Err(s) => { - let content = s.as_content(); - (InnerSas::Canceled(s), Some(content)) - } - } - } else { - (self, None) - } - } - AnyToDeviceEvent::KeyVerificationKey(e) => match self { - InnerSas::Accepted(s) => match s.into_key_received(e) { - Ok(s) => (InnerSas::KeyRecieved(s), None), - Err(s) => { - let content = s.as_content(); - (InnerSas::Canceled(s), Some(content)) - } - }, - InnerSas::Started(s) => match s.into_key_received(e) { - Ok(s) => { - let content = s.as_content(); - ( - InnerSas::KeyRecieved(s), - Some(AnyToDeviceEventContent::KeyVerificationKey(content)), - ) - } - Err(s) => { - let content = s.as_content(); - (InnerSas::Canceled(s), Some(content)) - } - }, - _ => (self, None), - }, - AnyToDeviceEvent::KeyVerificationMac(e) => match self { - InnerSas::KeyRecieved(s) => match s.into_mac_received(e) { - Ok(s) => (InnerSas::MacReceived(s), None), - Err(s) => { - let content = s.as_content(); - (InnerSas::Canceled(s), Some(content)) - } - }, - InnerSas::Confirmed(s) => match s.into_done(e) { - Ok(s) => (InnerSas::Done(s), None), - Err(s) => { - let content = s.as_content(); - (InnerSas::Canceled(s), Some(content)) - } - }, - _ => (self, None), - }, - _ => (self, None), - } - } - - fn can_be_presented(&self) -> bool { - match self { - InnerSas::KeyRecieved(_) => true, - InnerSas::MacReceived(_) => true, - _ => false, - } - } - - fn is_done(&self) -> bool { - matches!(self, InnerSas::Done(_)) - } - - fn is_canceled(&self) -> bool { - matches!(self, InnerSas::Canceled(_)) - } - - fn timed_out(&self) -> bool { - match self { - InnerSas::Created(s) => s.timed_out(), - InnerSas::Started(s) => s.timed_out(), - InnerSas::Canceled(s) => s.timed_out(), - InnerSas::Accepted(s) => s.timed_out(), - InnerSas::KeyRecieved(s) => s.timed_out(), - InnerSas::Confirmed(s) => s.timed_out(), - InnerSas::MacReceived(s) => s.timed_out(), - InnerSas::Done(s) => s.timed_out(), - } - } - - fn verification_flow_id(&self) -> Arc { - match self { - InnerSas::Created(s) => s.verification_flow_id.clone(), - InnerSas::Started(s) => s.verification_flow_id.clone(), - InnerSas::Canceled(s) => s.verification_flow_id.clone(), - InnerSas::Accepted(s) => s.verification_flow_id.clone(), - InnerSas::KeyRecieved(s) => s.verification_flow_id.clone(), - InnerSas::Confirmed(s) => s.verification_flow_id.clone(), - InnerSas::MacReceived(s) => s.verification_flow_id.clone(), - InnerSas::Done(s) => s.verification_flow_id.clone(), - } - } - - fn emoji(&self) -> Option> { - match self { - InnerSas::KeyRecieved(s) => Some(s.get_emoji()), - InnerSas::MacReceived(s) => Some(s.get_emoji()), - _ => None, - } - } - - fn decimals(&self) -> Option<(u16, u16, u16)> { - match self { - InnerSas::KeyRecieved(s) => Some(s.get_decimal()), - InnerSas::MacReceived(s) => Some(s.get_decimal()), - _ => None, - } - } - - fn verified_devices(&self) -> Option> { - if let InnerSas::Done(s) = self { - Some(s.verified_devices()) - } else { - None - } - } - - fn verified_identities(&self) -> Option> { - if let InnerSas::Done(s) = self { - Some(s.verified_identities()) - } else { - None - } - } -} - #[cfg(test)] mod test { use std::{convert::TryFrom, sync::Arc}; @@ -785,7 +552,10 @@ mod test { ReadOnlyAccount, ReadOnlyDevice, }; - use super::{Accepted, Created, Sas, SasState, Started}; + use super::{ + sas_state::{Accepted, Created, SasState, Started}, + Sas, + }; fn alice_id() -> UserId { UserId::try_from("@alice:example.org").unwrap() From 1bb5b42b1d959fc944d73728fb00ef65940292c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Thu, 10 Dec 2020 14:53:43 +0100 Subject: [PATCH 04/20] crypto: Prepare the sas structs to handle in-room verifications. --- matrix_sdk_crypto/src/machine.rs | 2 +- matrix_sdk_crypto/src/verification/machine.rs | 6 +- .../src/verification/sas/helpers.rs | 17 ++-- .../src/verification/sas/inner_sas.rs | 5 +- matrix_sdk_crypto/src/verification/sas/mod.rs | 7 +- .../src/verification/sas/sas_state.rs | 81 +++++++++++++------ 6 files changed, 80 insertions(+), 38 deletions(-) diff --git a/matrix_sdk_crypto/src/machine.rs b/matrix_sdk_crypto/src/machine.rs index b290bf54..f4755f4d 100644 --- a/matrix_sdk_crypto/src/machine.rs +++ b/matrix_sdk_crypto/src/machine.rs @@ -1825,7 +1825,7 @@ pub(crate) mod test { let mut event = request_to_event(alice.user_id(), &request); bob.handle_verification_event(&mut event).await; - let bob_sas = bob.get_verification(alice_sas.flow_id()).unwrap(); + let bob_sas = bob.get_verification(alice_sas.flow_id().as_str()).unwrap(); assert!(alice_sas.emoji().is_none()); assert!(bob_sas.emoji().is_none()); diff --git a/matrix_sdk_crypto/src/verification/machine.rs b/matrix_sdk_crypto/src/verification/machine.rs index 5efa5b5d..4cfb04f9 100644 --- a/matrix_sdk_crypto/src/verification/machine.rs +++ b/matrix_sdk_crypto/src/verification/machine.rs @@ -88,7 +88,7 @@ impl VerificationMachine { ); self.verifications - .insert(sas.flow_id().to_owned(), sas.clone()); + .insert(sas.flow_id().to_string(), sas.clone()); Ok((sas, request)) } @@ -367,7 +367,7 @@ mod test { async fn full_flow() { let (alice_machine, bob) = setup_verification_machine().await; - let alice = alice_machine.get_sas(bob.flow_id()).unwrap(); + let alice = alice_machine.get_sas(bob.flow_id().as_str()).unwrap(); let mut event = alice .accept() @@ -428,7 +428,7 @@ mod test { #[tokio::test] async fn timing_out() { let (alice_machine, bob) = setup_verification_machine().await; - let alice = alice_machine.get_sas(bob.flow_id()).unwrap(); + let alice = alice_machine.get_sas(bob.flow_id().as_str()).unwrap(); assert!(!alice.timed_out()); assert!(alice_machine.outgoing_to_device_messages.is_empty()); diff --git a/matrix_sdk_crypto/src/verification/sas/helpers.rs b/matrix_sdk_crypto/src/verification/sas/helpers.rs index 37974d14..99d81280 100644 --- a/matrix_sdk_crypto/src/verification/sas/helpers.rs +++ b/matrix_sdk_crypto/src/verification/sas/helpers.rs @@ -38,6 +38,8 @@ use crate::{ ReadOnlyAccount, ToDeviceRequest, }; +use super::sas_state::FlowId; + #[derive(Clone, Debug)] pub struct SasIds { pub account: ReadOnlyAccount, @@ -298,12 +300,12 @@ fn extra_mac_info_send(ids: &SasIds, flow_id: &str) -> String { /// # Panics /// /// This will panic if the public key of the other side wasn't set. -pub fn get_mac_content(sas: &OlmSas, ids: &SasIds, flow_id: &str) -> MacToDeviceEventContent { +pub fn get_mac_content(sas: &OlmSas, ids: &SasIds, flow_id: &FlowId) -> MacToDeviceEventContent { let mut mac: BTreeMap = BTreeMap::new(); let key_id = DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, ids.account.device_id()); let key = ids.account.identity_keys().ed25519(); - let info = extra_mac_info_send(ids, flow_id); + let info = extra_mac_info_send(ids, flow_id.as_str()); mac.insert( key_id.to_string(), @@ -319,10 +321,13 @@ pub fn get_mac_content(sas: &OlmSas, ids: &SasIds, flow_id: &str) -> MacToDevice .calculate_mac(&keys.join(","), &format!("{}KEY_IDS", &info)) .expect("Can't calculate SAS MAC"); - MacToDeviceEventContent { - transaction_id: flow_id.to_owned(), - keys, - mac, + match flow_id { + FlowId::ToDevice(s) => MacToDeviceEventContent { + transaction_id: s.to_string(), + keys, + mac, + }, + _ => todo!(), } } diff --git a/matrix_sdk_crypto/src/verification/sas/inner_sas.rs b/matrix_sdk_crypto/src/verification/sas/inner_sas.rs index 6a566990..034e4734 100644 --- a/matrix_sdk_crypto/src/verification/sas/inner_sas.rs +++ b/matrix_sdk_crypto/src/verification/sas/inner_sas.rs @@ -31,7 +31,8 @@ use crate::{ }; use super::sas_state::{ - Accepted, Canceled, Confirmed, Created, Done, KeyReceived, MacReceived, SasState, Started, + Accepted, Canceled, Confirmed, Created, Done, FlowId, KeyReceived, MacReceived, SasState, + Started, }; #[derive(Clone, Debug)] @@ -220,7 +221,7 @@ impl InnerSas { } } - pub fn verification_flow_id(&self) -> Arc { + pub fn verification_flow_id(&self) -> Arc { match self { InnerSas::Created(s) => s.verification_flow_id.clone(), InnerSas::Started(s) => s.verification_flow_id.clone(), diff --git a/matrix_sdk_crypto/src/verification/sas/mod.rs b/matrix_sdk_crypto/src/verification/sas/mod.rs index 025f0fa7..6d9f3ffe 100644 --- a/matrix_sdk_crypto/src/verification/sas/mod.rs +++ b/matrix_sdk_crypto/src/verification/sas/mod.rs @@ -31,7 +31,7 @@ use matrix_sdk_common::{ }, AnyToDeviceEvent, AnyToDeviceEventContent, ToDeviceEvent, }, - identifiers::{DeviceId, UserId}, + identifiers::{DeviceId, EventId, RoomId, UserId}, }; use crate::{ @@ -44,6 +44,7 @@ use crate::{ pub use helpers::content_to_request; use inner_sas::InnerSas; +pub use sas_state::FlowId; #[derive(Debug)] /// A result of a verification flow. @@ -65,7 +66,7 @@ pub struct Sas { private_identity: PrivateCrossSigningIdentity, other_device: ReadOnlyDevice, other_identity: Option, - flow_id: Arc, + flow_id: Arc, } impl Sas { @@ -95,7 +96,7 @@ impl Sas { } /// Get the unique ID that identifies this SAS verification flow. - pub fn flow_id(&self) -> &str { + pub fn flow_id(&self) -> &FlowId { &self.flow_id } diff --git a/matrix_sdk_crypto/src/verification/sas/sas_state.rs b/matrix_sdk_crypto/src/verification/sas/sas_state.rs index 8ddd0e9c..817bb247 100644 --- a/matrix_sdk_crypto/src/verification/sas/sas_state.rs +++ b/matrix_sdk_crypto/src/verification/sas/sas_state.rs @@ -36,7 +36,7 @@ use matrix_sdk_common::{ }, AnyToDeviceEventContent, ToDeviceEvent, }, - identifiers::{DeviceId, UserId}, + identifiers::{DeviceId, RoomId, UserId}, uuid::Uuid, }; use tracing::error; @@ -65,6 +65,28 @@ const MAX_AGE: Duration = Duration::from_secs(60 * 5); // The max time a SAS object will wait for a new event to arrive. const MAX_EVENT_TIMEOUT: Duration = Duration::from_secs(60); +#[derive(Clone, Debug)] +pub enum FlowId { + ToDevice(String), + InRoom(RoomId), +} + +impl FlowId { + pub fn to_string(&self) -> String { + match self { + FlowId::InRoom(r) => r.to_string(), + FlowId::ToDevice(t) => t.to_string(), + } + } + + pub fn as_str(&self) -> &str { + match self { + FlowId::InRoom(r) => r.as_str(), + FlowId::ToDevice(t) => t.as_str(), + } + } +} + /// Struct containing the protocols that were agreed to be used for the SAS /// flow. #[derive(Clone, Debug)] @@ -143,7 +165,7 @@ pub struct SasState { /// /// This will be the transaction id for to-device events and the relates_to /// field for in-room events. - pub verification_flow_id: Arc, + pub verification_flow_id: Arc, /// The SAS state we're in. state: Arc, @@ -268,7 +290,7 @@ impl SasState { } fn check_event(&self, sender: &UserId, flow_id: &str) -> Result<(), CancelCode> { - if *flow_id != *self.verification_flow_id { + if *flow_id != *self.verification_flow_id.as_str() { Err(CancelCode::UnknownTransaction) } else if sender != self.ids.other_device.user_id() { Err(CancelCode::UserMismatch) @@ -302,7 +324,7 @@ impl SasState { other_device, other_identity, }, - verification_flow_id: verification_flow_id.into(), + verification_flow_id: FlowId::ToDevice(verification_flow_id).into(), creation_time: Arc::new(Instant::now()), last_event_time: Arc::new(Instant::now()), @@ -413,7 +435,7 @@ impl SasState { creation_time: Arc::new(Instant::now()), last_event_time: Arc::new(Instant::now()), - verification_flow_id: event.content.transaction_id.as_str().into(), + verification_flow_id: FlowId::ToDevice(event.content.transaction_id.clone()).into(), state: Arc::new(Started { protocol_definitions: content.clone(), @@ -452,7 +474,7 @@ impl SasState { other_identity, }, - verification_flow_id: event.content.transaction_id.as_str().into(), + verification_flow_id: FlowId::ToDevice(event.content.transaction_id.clone()).into(), state: Arc::new(Canceled::new(CancelCode::UnknownMethod)), }) } @@ -575,9 +597,14 @@ impl SasState { /// /// The content needs to be automatically sent to the other side. pub fn as_content(&self) -> KeyToDeviceEventContent { - KeyToDeviceEventContent { - transaction_id: self.verification_flow_id.to_string(), - key: self.inner.lock().unwrap().public_key(), + match &*self.verification_flow_id { + FlowId::ToDevice(s) => KeyToDeviceEventContent { + transaction_id: s.to_string(), + key: self.inner.lock().unwrap().public_key(), + }, + FlowId::InRoom(r) => { + todo!("In-room verifications aren't implemented") + } } } } @@ -588,9 +615,12 @@ impl SasState { /// The content needs to be automatically sent to the other side if and only /// if we_started is false. pub fn as_content(&self) -> KeyToDeviceEventContent { - KeyToDeviceEventContent { - transaction_id: self.verification_flow_id.to_string(), - key: self.inner.lock().unwrap().public_key(), + match self.verification_flow_id.as_ref() { + FlowId::ToDevice(s) => KeyToDeviceEventContent { + transaction_id: s.to_string(), + key: self.inner.lock().unwrap().public_key(), + }, + _ => todo!(), } } @@ -603,7 +633,7 @@ impl SasState { &self.inner.lock().unwrap(), &self.ids, &self.state.their_pubkey, - &self.verification_flow_id, + self.verification_flow_id.as_str(), self.state.we_started, ) } @@ -617,7 +647,7 @@ impl SasState { &self.inner.lock().unwrap(), &self.ids, &self.state.their_pubkey, - &self.verification_flow_id, + self.verification_flow_id.as_str(), self.state.we_started, ) } @@ -639,7 +669,7 @@ impl SasState { let (devices, master_keys) = receive_mac_event( &self.inner.lock().unwrap(), &self.ids, - &self.verification_flow_id, + self.verification_flow_id.as_str(), event, ) .map_err(|c| self.clone().cancel(c))?; @@ -695,7 +725,7 @@ impl SasState { let (devices, master_keys) = receive_mac_event( &self.inner.lock().unwrap(), &self.ids, - &self.verification_flow_id, + &self.verification_flow_id.as_str(), event, ) .map_err(|c| self.clone().cancel(c))?; @@ -754,7 +784,7 @@ impl SasState { &self.inner.lock().unwrap(), &self.ids, &self.state.their_pubkey, - &self.verification_flow_id, + &self.verification_flow_id.as_str(), self.state.we_started, ) } @@ -768,7 +798,7 @@ impl SasState { &self.inner.lock().unwrap(), &self.ids, &self.state.their_pubkey, - &self.verification_flow_id, + &self.verification_flow_id.as_str(), self.state.we_started, ) } @@ -828,11 +858,16 @@ impl Canceled { impl SasState { pub fn as_content(&self) -> AnyToDeviceEventContent { - AnyToDeviceEventContent::KeyVerificationCancel(CancelToDeviceEventContent { - transaction_id: self.verification_flow_id.to_string(), - reason: self.state.reason.to_string(), - code: self.state.cancel_code.clone(), - }) + match self.verification_flow_id.as_ref() { + FlowId::ToDevice(s) => { + AnyToDeviceEventContent::KeyVerificationCancel(CancelToDeviceEventContent { + transaction_id: self.verification_flow_id.to_string(), + reason: self.state.reason.to_string(), + code: self.state.cancel_code.clone(), + }) + } + _ => todo!(), + } } } 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 05/20] 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)) From 7570cf5ac28425b664e8fd578fe7e9028bec5e94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Fri, 11 Dec 2020 15:42:49 +0100 Subject: [PATCH 06/20] crypto: WIP genrealize the sas so it can handle in-room and to-device events. --- matrix_sdk_crypto/src/verification/machine.rs | 73 ++++++++++++------- .../src/verification/requests.rs | 68 +++++++++++++++-- .../src/verification/sas/event_enums.rs | 41 +++++++++-- .../src/verification/sas/inner_sas.rs | 35 +++++---- matrix_sdk_crypto/src/verification/sas/mod.rs | 32 +++++--- .../src/verification/sas/sas_state.rs | 48 ++++++++---- 6 files changed, 219 insertions(+), 78 deletions(-) diff --git a/matrix_sdk_crypto/src/verification/machine.rs b/matrix_sdk_crypto/src/verification/machine.rs index 0e185418..ccdcf61e 100644 --- a/matrix_sdk_crypto/src/verification/machine.rs +++ b/matrix_sdk_crypto/src/verification/machine.rs @@ -112,17 +112,23 @@ impl VerificationMachine { &self, recipient: &UserId, recipient_device: &DeviceId, - content: AnyToDeviceEventContent, + content: OutgoingContent, ) { - let request = content_to_request(recipient, recipient_device, content); - let request_id = request.txn_id; + match content { + OutgoingContent::ToDevice(c) => { + let request = content_to_request(recipient, recipient_device, c); + let request_id = request.txn_id; - let request = OutgoingRequest { - request_id, - request: Arc::new(request.into()), - }; + let request = OutgoingRequest { + request_id, + request: Arc::new(request.into()), + }; - self.outgoing_to_device_messages.insert(request_id, request); + self.outgoing_to_device_messages.insert(request_id, request); + } + + OutgoingContent::Room(c) => todo!(), + } } fn receive_event_helper(&self, sas: &Sas, event: &AnyToDeviceEvent) { @@ -165,29 +171,41 @@ impl VerificationMachine { room_id: &RoomId, event: &AnySyncRoomEvent, ) -> Result<(), CryptoStoreError> { - match event { - AnySyncRoomEvent::Message(AnySyncMessageEvent::RoomMessage(m)) => { - if let MessageEventContent::VerificationRequest(r) = &m.content { - if self.account.user_id() == &r.to { - info!( - "Received a new verification request from {} {}", - m.sender, r.from_device - ); + if let AnySyncRoomEvent::Message(m) = event { + match m { + AnySyncMessageEvent::RoomMessage(m) => { + if let MessageEventContent::VerificationRequest(r) = &m.content { + if self.account.user_id() == &r.to { + info!( + "Received a new verification request from {} {}", + m.sender, r.from_device + ); - let request = VerificationRequest::from_request_event( - self.account.clone(), - self.store.clone(), - room_id, - &m.sender, - &m.event_id, - r, - ); + let request = VerificationRequest::from_request_event( + self.account.clone(), + self.private_identity.lock().await.clone(), + self.store.clone(), + room_id, + &m.sender, + &m.event_id, + r, + ); - self.requests.insert(m.event_id.clone(), request); + self.requests.insert(m.event_id.clone(), request); + } } } + AnySyncMessageEvent::KeyVerificationStart(e) => { + info!( + "Received a new verification start event from {} {}", + e.sender, e.content.from_device + ); + + if let Some((_, request)) = self.requests.remove(&e.content.relation.event_id) { + } + } + _ => (), } - _ => (), } Ok(()) @@ -215,7 +233,8 @@ impl VerificationMachine { private_identity, d, self.store.clone(), - e, + &e.sender, + e.content.clone(), self.store.get_user_identity(&e.sender).await?, ) { Ok(s) => { diff --git a/matrix_sdk_crypto/src/verification/requests.rs b/matrix_sdk_crypto/src/verification/requests.rs index 2d27af43..5b3102e8 100644 --- a/matrix_sdk_crypto/src/verification/requests.rs +++ b/matrix_sdk_crypto/src/verification/requests.rs @@ -23,6 +23,7 @@ use matrix_sdk_common::{ ready::ReadyEventContent, start::StartEventContent, Relation, VerificationMethod, }, room::message::KeyVerificationRequestEventContent, + MessageEvent, }, identifiers::{DeviceId, DeviceIdBox, EventId, RoomId, UserId}, }; @@ -42,6 +43,7 @@ const SUPPORTED_METHODS: &[VerificationMethod] = &[VerificationMethod::MSasV1]; pub struct VerificationRequest { inner: Arc>, account: ReadOnlyAccount, + private_cross_signing_identity: PrivateCrossSigningIdentity, store: Arc>, room_id: Arc, } @@ -49,6 +51,7 @@ pub struct VerificationRequest { impl VerificationRequest { pub(crate) fn from_request_event( account: ReadOnlyAccount, + private_cross_signing_identity: PrivateCrossSigningIdentity, store: Arc>, room_id: &RoomId, sender: &UserId, @@ -66,6 +69,7 @@ impl VerificationRequest { ), ))), account, + private_cross_signing_identity, store, room_id: room_id.clone().into(), } @@ -80,6 +84,25 @@ impl VerificationRequest { pub fn accept(&self) -> Option { self.inner.lock().unwrap().accept() } + + pub(crate) fn into_started_sas( + &self, + event: &MessageEvent, + device: ReadOnlyDevice, + user_identity: Option, + ) -> Result { + match &*self.inner.lock().unwrap() { + InnerRequest::Ready(s) => s.into_started_sas( + event, + self.store.clone(), + self.account.clone(), + self.private_cross_signing_identity.clone(), + device, + user_identity, + ), + _ => todo!(), + } + } } #[derive(Debug)] @@ -102,6 +125,29 @@ impl InnerRequest { None } } + + fn into_started_sas( + &mut self, + event: &MessageEvent, + store: Arc>, + account: ReadOnlyAccount, + private_identity: PrivateCrossSigningIdentity, + other_device: ReadOnlyDevice, + other_identity: Option, + ) -> Result, OutgoingContent> { + if let InnerRequest::Ready(s) = self { + Ok(Some(s.into_started_sas( + event, + store, + account, + private_identity, + other_device, + other_identity, + )?)) + } else { + Ok(None) + } + } } #[derive(Clone, Debug)] @@ -252,14 +298,23 @@ struct Ready { impl RequestState { fn into_started_sas( - self, + &self, + event: &MessageEvent, + store: Arc>, 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) + other_identity: Option, + ) -> Result { + Sas::from_start_event( + account, + private_identity, + other_device, + store, + &event.sender, + event.content.clone(), + other_identity, + ) } fn start_sas( @@ -289,3 +344,6 @@ struct Passive { /// unique id identifying this verification flow. pub flow_id: EventId, } + +#[derive(Clone, Debug)] +struct Started {} diff --git a/matrix_sdk_crypto/src/verification/sas/event_enums.rs b/matrix_sdk_crypto/src/verification/sas/event_enums.rs index e34ccd6d..830b2939 100644 --- a/matrix_sdk_crypto/src/verification/sas/event_enums.rs +++ b/matrix_sdk_crypto/src/verification/sas/event_enums.rs @@ -18,12 +18,17 @@ use std::convert::TryInto; use matrix_sdk_common::{ events::{ - key::verification::start::{StartEventContent, StartToDeviceEventContent}, + key::verification::{ + start::{StartEventContent, StartMethod, StartToDeviceEventContent}, + KeyAgreementProtocol, + }, AnyMessageEventContent, AnyToDeviceEventContent, MessageEvent, ToDeviceEvent, }, CanonicalJsonValue, }; +use super::FlowId; + #[derive(Clone, Debug)] pub enum StartContent { ToDevice(StartToDeviceEventContent), @@ -31,6 +36,20 @@ pub enum StartContent { } impl StartContent { + pub fn method(&self) -> &StartMethod { + match self { + StartContent::ToDevice(c) => &c.method, + StartContent::Room(c) => &c.method, + } + } + + pub fn flow_id(&self) -> FlowId { + match self { + StartContent::ToDevice(c) => FlowId::ToDevice(c.transaction_id.clone()), + StartContent::Room(c) => FlowId::InRoom(c.relation.event_id.clone()), + } + } + pub fn to_canonical_json(self) -> CanonicalJsonValue { let content = match self { StartContent::Room(c) => serde_json::to_value(c), @@ -65,12 +84,20 @@ pub enum OutgoingContent { 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)) - } + StartContent::Room(c) => AnyMessageEventContent::KeyVerificationStart(c).into(), + StartContent::ToDevice(c) => AnyToDeviceEventContent::KeyVerificationStart(c).into(), } } } + +impl From for OutgoingContent { + fn from(content: AnyToDeviceEventContent) -> Self { + OutgoingContent::ToDevice(content) + } +} + +impl From for OutgoingContent { + fn from(content: AnyMessageEventContent) -> Self { + OutgoingContent::Room(content) + } +} diff --git a/matrix_sdk_crypto/src/verification/sas/inner_sas.rs b/matrix_sdk_crypto/src/verification/sas/inner_sas.rs index 4eb1bc0d..cf2a2476 100644 --- a/matrix_sdk_crypto/src/verification/sas/inner_sas.rs +++ b/matrix_sdk_crypto/src/verification/sas/inner_sas.rs @@ -20,12 +20,14 @@ use std::sync::Arc; use matrix_sdk_common::{ events::{ key::verification::{ - accept::AcceptToDeviceEventContent, cancel::CancelCode, mac::MacToDeviceEventContent, - start::StartToDeviceEventContent, + accept::AcceptToDeviceEventContent, + cancel::CancelCode, + mac::MacToDeviceEventContent, + start::{StartEventContent, StartToDeviceEventContent}, }, - AnyToDeviceEvent, AnyToDeviceEventContent, ToDeviceEvent, + AnyToDeviceEvent, AnyToDeviceEventContent, MessageEvent, ToDeviceEvent, }, - identifiers::EventId, + identifiers::{EventId, UserId}, }; use crate::{ @@ -39,6 +41,7 @@ use super::{ Accepted, Canceled, Confirmed, Created, Done, FlowId, KeyReceived, MacReceived, SasState, Started, }, + StartContent, }; #[derive(Clone, Debug)] @@ -78,10 +81,17 @@ impl InnerSas { pub fn from_start_event( account: ReadOnlyAccount, other_device: ReadOnlyDevice, - event: &ToDeviceEvent, + sender: &UserId, + content: impl Into, other_identity: Option, - ) -> Result { - match SasState::::from_start_event(account, other_device, event, other_identity) { + ) -> Result { + match SasState::::from_start_event( + account, + other_device, + other_identity, + &sender, + content, + ) { Ok(s) => Ok(InnerSas::Started(s)), Err(s) => Err(s.as_content()), } @@ -110,7 +120,7 @@ impl InnerSas { } } - pub fn cancel(self, code: CancelCode) -> (InnerSas, Option) { + pub fn cancel(self, code: CancelCode) -> (InnerSas, Option) { let sas = match self { InnerSas::Created(s) => s.cancel(code), InnerSas::Started(s) => s.cancel(code), @@ -141,10 +151,7 @@ impl InnerSas { } } - pub fn receive_event( - self, - event: &AnyToDeviceEvent, - ) -> (InnerSas, Option) { + pub fn receive_event(self, event: &AnyToDeviceEvent) -> (InnerSas, Option) { match event { AnyToDeviceEvent::KeyVerificationAccept(e) => { if let InnerSas::Created(s) = self { @@ -153,7 +160,7 @@ impl InnerSas { let content = s.as_content(); ( InnerSas::Accepted(s), - Some(AnyToDeviceEventContent::KeyVerificationKey(content)), + Some(AnyToDeviceEventContent::KeyVerificationKey(content).into()), ) } Err(s) => { @@ -178,7 +185,7 @@ impl InnerSas { let content = s.as_content(); ( InnerSas::KeyRecieved(s), - Some(AnyToDeviceEventContent::KeyVerificationKey(content)), + Some(AnyToDeviceEventContent::KeyVerificationKey(content).into()), ) } Err(s) => { diff --git a/matrix_sdk_crypto/src/verification/sas/mod.rs b/matrix_sdk_crypto/src/verification/sas/mod.rs index 3f531b17..590f0de7 100644 --- a/matrix_sdk_crypto/src/verification/sas/mod.rs +++ b/matrix_sdk_crypto/src/verification/sas/mod.rs @@ -30,7 +30,7 @@ use matrix_sdk_common::{ cancel::CancelCode, start::{StartEventContent, StartToDeviceEventContent}, }, - AnyToDeviceEvent, AnyToDeviceEventContent, ToDeviceEvent, + AnyToDeviceEvent, AnyToDeviceEventContent, MessageEvent, ToDeviceEvent, }, identifiers::{DeviceId, EventId, RoomId, UserId}, }; @@ -220,15 +220,18 @@ impl Sas { private_identity: PrivateCrossSigningIdentity, other_device: ReadOnlyDevice, store: Arc>, - event: &ToDeviceEvent, + sender: &UserId, + content: impl Into, other_identity: Option, - ) -> Result { + ) -> Result { let inner = InnerSas::from_start_event( account.clone(), other_device.clone(), - event, + &sender, + content, other_identity.clone(), )?; + let flow_id = inner.verification_flow_id(); Ok(Sas { @@ -520,7 +523,13 @@ impl Sas { let (sas, content) = sas.cancel(CancelCode::User); *guard = sas; - content.map(|c| self.content_to_request(c)) + content.map(|c| { + if let OutgoingContent::ToDevice(c) = c { + self.content_to_request(c) + } else { + todo!() + } + }) } pub(crate) fn cancel_if_timed_out(&self) -> Option { @@ -531,7 +540,13 @@ impl Sas { let sas: InnerSas = (*guard).clone(); let (sas, content) = sas.cancel(CancelCode::Timeout); *guard = sas; - content.map(|c| self.content_to_request(c)) + content.map(|c| { + if let OutgoingContent::ToDevice(c) = c { + self.content_to_request(c) + } else { + todo!() + } + }) } else { None } @@ -574,10 +589,7 @@ impl Sas { self.inner.lock().unwrap().decimals() } - pub(crate) fn receive_event( - &self, - event: &AnyToDeviceEvent, - ) -> Option { + pub(crate) fn receive_event(&self, event: &AnyToDeviceEvent) -> Option { let mut guard = self.inner.lock().unwrap(); let sas: InnerSas = (*guard).clone(); let (sas, content) = sas.receive_event(event); diff --git a/matrix_sdk_crypto/src/verification/sas/sas_state.rs b/matrix_sdk_crypto/src/verification/sas/sas_state.rs index 44e23f3b..3f1d7373 100644 --- a/matrix_sdk_crypto/src/verification/sas/sas_state.rs +++ b/matrix_sdk_crypto/src/verification/sas/sas_state.rs @@ -37,7 +37,7 @@ use matrix_sdk_common::{ HashAlgorithm, KeyAgreementProtocol, MessageAuthenticationCode, Relation, ShortAuthenticationString, VerificationMethod, }, - AnyMessageEventContent, AnyToDeviceEventContent, ToDeviceEvent, + AnyMessageEventContent, AnyToDeviceEventContent, MessageEvent, ToDeviceEvent, }, identifiers::{DeviceId, EventId, UserId}, uuid::Uuid, @@ -447,7 +447,7 @@ impl SasState { } impl SasState { - /// Create a new SAS verification flow from a m.key.verification.start + /// Create a new SAS verification flow from an in-room m.key.verification.start /// event. /// /// This will put us in the `started` state. @@ -463,18 +463,35 @@ impl SasState { pub fn from_start_event( account: ReadOnlyAccount, other_device: ReadOnlyDevice, - event: &ToDeviceEvent, other_identity: Option, + sender: &UserId, + content: impl Into, ) -> Result, SasState> { - if let StartMethod::MSasV1(content) = &event.content.method { + Self::from_start_helper( + account, + other_device, + other_identity, + sender, + &content.into(), + ) + } + + fn from_start_helper( + account: ReadOnlyAccount, + other_device: ReadOnlyDevice, + other_identity: Option, + sender: &UserId, + content: &StartContent, + ) -> Result, SasState> { + if let StartMethod::MSasV1(method_content) = content.method() { let sas = OlmSas::new(); let pubkey = sas.public_key(); - let commitment = calculate_commitment(&pubkey, event.content.clone()); + let commitment = calculate_commitment(&pubkey, content.clone()); error!( "Calculated commitment for pubkey {} and content {:?} {}", - pubkey, event.content, commitment + pubkey, content, commitment ); let sas = SasState { @@ -489,25 +506,25 @@ impl SasState { creation_time: Arc::new(Instant::now()), last_event_time: Arc::new(Instant::now()), - verification_flow_id: FlowId::ToDevice(event.content.transaction_id.clone()).into(), + verification_flow_id: content.flow_id().into(), state: Arc::new(Started { - protocol_definitions: content.clone(), + protocol_definitions: method_content.clone(), commitment, }), }; - if !content + if !method_content .key_agreement_protocols .contains(&KeyAgreementProtocol::Curve25519HkdfSha256) - || !content + || !method_content .message_authentication_codes .contains(&MessageAuthenticationCode::HkdfHmacSha256) - || !content.hashes.contains(&HashAlgorithm::Sha256) - || (!content + || !method_content.hashes.contains(&HashAlgorithm::Sha256) + || (!method_content .short_authentication_string .contains(&ShortAuthenticationString::Decimal) - && !content + && !method_content .short_authentication_string .contains(&ShortAuthenticationString::Emoji)) { @@ -528,7 +545,7 @@ impl SasState { other_identity, }, - verification_flow_id: FlowId::ToDevice(event.content.transaction_id.clone()).into(), + verification_flow_id: content.flow_id().into(), state: Arc::new(Canceled::new(CancelCode::UnknownMethod)), }) } @@ -914,7 +931,7 @@ impl Canceled { } impl SasState { - pub fn as_content(&self) -> AnyToDeviceEventContent { + pub fn as_content(&self) -> OutgoingContent { match self.verification_flow_id.as_ref() { FlowId::ToDevice(s) => { AnyToDeviceEventContent::KeyVerificationCancel(CancelToDeviceEventContent { @@ -922,6 +939,7 @@ impl SasState { reason: self.state.reason.to_string(), code: self.state.cancel_code.clone(), }) + .into() } _ => todo!(), } From 5105629c0889b96226b4cc09ecaaaa6c1d8a9b41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Fri, 11 Dec 2020 16:13:58 +0100 Subject: [PATCH 07/20] crypto: WIP handle in-room start events. --- matrix_sdk_crypto/src/verification/machine.rs | 28 ++++++++++++++++++- .../src/verification/requests.rs | 11 +++----- 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/matrix_sdk_crypto/src/verification/machine.rs b/matrix_sdk_crypto/src/verification/machine.rs index ccdcf61e..ad2e49f3 100644 --- a/matrix_sdk_crypto/src/verification/machine.rs +++ b/matrix_sdk_crypto/src/verification/machine.rs @@ -46,7 +46,7 @@ pub struct VerificationMachine { private_identity: Arc>, pub(crate) store: Arc>, verifications: Arc>, - room_verifications: Arc>, + room_verifications: Arc>, requests: Arc>, outgoing_to_device_messages: Arc>, } @@ -202,6 +202,32 @@ impl VerificationMachine { ); if let Some((_, request)) = self.requests.remove(&e.content.relation.event_id) { + if let Some(d) = self + .store + .get_device(&e.sender, &e.content.from_device) + .await? + { + match request.into_started_sas( + e, + d, + self.store.get_user_identity(&e.sender).await?, + ) { + Ok(s) => { + // TODO we need to queue up the accept event + // here. + let accept_event = s.accept(); + self.room_verifications + .insert(e.content.relation.event_id.clone(), s); + } + Err(c) => { + warn!( + "Can't start key verification with {} {}, canceling: {:?}", + e.sender, e.content.from_device, c + ); + // self.queue_up_content(&e.sender, &e.content.from_device, c) + } + } + } } } _ => (), diff --git a/matrix_sdk_crypto/src/verification/requests.rs b/matrix_sdk_crypto/src/verification/requests.rs index 5b3102e8..ffffa55e 100644 --- a/matrix_sdk_crypto/src/verification/requests.rs +++ b/matrix_sdk_crypto/src/verification/requests.rs @@ -23,7 +23,7 @@ use matrix_sdk_common::{ ready::ReadyEventContent, start::StartEventContent, Relation, VerificationMethod, }, room::message::KeyVerificationRequestEventContent, - MessageEvent, + MessageEvent, SyncMessageEvent, }, identifiers::{DeviceId, DeviceIdBox, EventId, RoomId, UserId}, }; @@ -87,7 +87,7 @@ impl VerificationRequest { pub(crate) fn into_started_sas( &self, - event: &MessageEvent, + event: &SyncMessageEvent, device: ReadOnlyDevice, user_identity: Option, ) -> Result { @@ -128,7 +128,7 @@ impl InnerRequest { fn into_started_sas( &mut self, - event: &MessageEvent, + event: &SyncMessageEvent, store: Arc>, account: ReadOnlyAccount, private_identity: PrivateCrossSigningIdentity, @@ -299,7 +299,7 @@ struct Ready { impl RequestState { fn into_started_sas( &self, - event: &MessageEvent, + event: &SyncMessageEvent, store: Arc>, account: ReadOnlyAccount, private_identity: PrivateCrossSigningIdentity, @@ -344,6 +344,3 @@ struct Passive { /// unique id identifying this verification flow. pub flow_id: EventId, } - -#[derive(Clone, Debug)] -struct Started {} From b6e28e2280ab356b4b6127ea7fc5183e156525c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Tue, 15 Dec 2020 16:35:54 +0100 Subject: [PATCH 08/20] crypto: WIP more work on in-room verifications now up to accepting them. --- matrix_sdk/src/client.rs | 32 +++++- matrix_sdk/src/device.rs | 24 ++--- matrix_sdk/src/sas.rs | 34 +++---- matrix_sdk_crypto/src/identities/device.rs | 4 +- matrix_sdk_crypto/src/lib.rs | 3 +- matrix_sdk_crypto/src/machine.rs | 4 + matrix_sdk_crypto/src/requests.rs | 73 +++++++++++++- matrix_sdk_crypto/src/verification/machine.rs | 58 +++++++++-- .../src/verification/requests.rs | 25 ++--- .../src/verification/sas/event_enums.rs | 44 ++++++--- .../src/verification/sas/inner_sas.rs | 15 ++- matrix_sdk_crypto/src/verification/sas/mod.rs | 23 ++++- .../src/verification/sas/sas_state.rs | 98 ++++++++++++------- 13 files changed, 320 insertions(+), 117 deletions(-) diff --git a/matrix_sdk/src/client.rs b/matrix_sdk/src/client.rs index 3d695be4..d3c94028 100644 --- a/matrix_sdk/src/client.rs +++ b/matrix_sdk/src/client.rs @@ -45,7 +45,7 @@ use matrix_sdk_base::{BaseClient, BaseClientConfig, Room, Session, StateStore}; #[cfg(feature = "encryption")] use matrix_sdk_base::crypto::{ decrypt_key_export, encrypt_key_export, olm::InboundGroupSession, store::CryptoStoreError, - AttachmentEncryptor, OutgoingRequests, ToDeviceRequest, + AttachmentEncryptor, OutgoingRequests, RoomMessageRequest, ToDeviceRequest, }; /// Enum controlling if a loop running callbacks should continue or abort. @@ -1160,6 +1160,17 @@ impl Client { Ok(()) } + async fn room_send_helper( + &self, + request: &RoomMessageRequest, + ) -> Result { + let content = request.content.clone(); + let txn_id = request.txn_id.into(); + let room_id = &request.room_id; + + self.room_send(&room_id, content, Some(txn_id)).await + } + /// Send a room message to the homeserver. /// /// Returns the parsed response from the server. @@ -1458,7 +1469,10 @@ impl Client { } #[cfg(feature = "encryption")] - async fn send_to_device(&self, request: &ToDeviceRequest) -> Result { + pub(crate) async fn send_to_device( + &self, + request: &ToDeviceRequest, + ) -> Result { let txn_id_string = request.txn_id_string(); let request = RumaToDeviceRequest::new( request.event_type.clone(), @@ -1737,6 +1751,14 @@ impl Client { .unwrap(); } } + OutgoingRequests::RoomMessage(request) => { + if let Ok(resp) = self.room_send_helper(request).await { + self.base_client + .mark_request_as_sent(&r.request_id(), &resp) + .await + .unwrap(); + } + } } } } @@ -1891,7 +1913,7 @@ impl Client { .await .map(|sas| Sas { inner: sas, - http_client: self.http_client.clone(), + client: self.clone(), }) } @@ -1953,7 +1975,7 @@ impl Client { Ok(device.map(|d| Device { inner: d, - http_client: self.http_client.clone(), + client: self.clone(), })) } @@ -2070,7 +2092,7 @@ impl Client { Ok(UserDevices { inner: devices, - http_client: self.http_client.clone(), + client: self.clone(), }) } diff --git a/matrix_sdk/src/device.rs b/matrix_sdk/src/device.rs index 776b9f0e..39274896 100644 --- a/matrix_sdk/src/device.rs +++ b/matrix_sdk/src/device.rs @@ -18,18 +18,15 @@ use matrix_sdk_base::crypto::{ store::CryptoStoreError, Device as BaseDevice, LocalTrust, ReadOnlyDevice, UserDevices as BaseUserDevices, }; -use matrix_sdk_common::{ - api::r0::to_device::send_event_to_device::Request as ToDeviceRequest, - identifiers::{DeviceId, DeviceIdBox}, -}; +use matrix_sdk_common::identifiers::{DeviceId, DeviceIdBox}; -use crate::{error::Result, http_client::HttpClient, Sas}; +use crate::{error::Result, Client, Sas}; #[derive(Clone, Debug)] /// A device represents a E2EE capable client of an user. pub struct Device { pub(crate) inner: BaseDevice, - pub(crate) http_client: HttpClient, + pub(crate) client: Client, } impl Deref for Device { @@ -66,14 +63,11 @@ impl Device { /// ``` pub async fn start_verification(&self) -> Result { let (sas, request) = self.inner.start_verification().await?; - let txn_id_string = request.txn_id_string(); - let request = ToDeviceRequest::new(request.event_type, &txn_id_string, request.messages); - - self.http_client.send(request).await?; + self.client.send_to_device(&request).await?; Ok(Sas { inner: sas, - http_client: self.http_client.clone(), + client: self.client.clone(), }) } @@ -102,7 +96,7 @@ impl Device { #[derive(Debug)] pub struct UserDevices { pub(crate) inner: BaseUserDevices, - pub(crate) http_client: HttpClient, + pub(crate) client: Client, } impl UserDevices { @@ -110,7 +104,7 @@ impl UserDevices { pub fn get(&self, device_id: &DeviceId) -> Option { self.inner.get(device_id).map(|d| Device { inner: d, - http_client: self.http_client.clone(), + client: self.client.clone(), }) } @@ -121,11 +115,11 @@ impl UserDevices { /// Iterator over all the devices of the user devices. pub fn devices(&self) -> impl Iterator + '_ { - let client = self.http_client.clone(); + let client = self.client.clone(); self.inner.devices().map(move |d| Device { inner: d, - http_client: client.clone(), + client: client.clone(), }) } } diff --git a/matrix_sdk/src/sas.rs b/matrix_sdk/src/sas.rs index 3db1df47..16da6156 100644 --- a/matrix_sdk/src/sas.rs +++ b/matrix_sdk/src/sas.rs @@ -12,26 +12,27 @@ // See the License for the specific language governing permissions and // limitations under the License. -use matrix_sdk_base::crypto::{ReadOnlyDevice, Sas as BaseSas}; -use matrix_sdk_common::api::r0::to_device::send_event_to_device::Request as ToDeviceRequest; +use matrix_sdk_base::crypto::{OutgoingVerificationRequest, ReadOnlyDevice, Sas as BaseSas}; -use crate::{error::Result, http_client::HttpClient}; +use crate::{error::Result, Client}; /// An object controling the interactive verification flow. #[derive(Debug, Clone)] pub struct Sas { pub(crate) inner: BaseSas, - pub(crate) http_client: HttpClient, + pub(crate) client: Client, } impl Sas { /// Accept the interactive verification flow. pub async fn accept(&self) -> Result<()> { if let Some(req) = self.inner.accept() { - let txn_id_string = req.txn_id_string(); - let request = ToDeviceRequest::new(req.event_type, &txn_id_string, req.messages); - - self.http_client.send(request).await?; + match req { + OutgoingVerificationRequest::ToDevice(r) => { + self.client.send_to_device(&r).await?; + } + OutgoingVerificationRequest::InRoom(_) => todo!(), + } } Ok(()) } @@ -40,15 +41,12 @@ impl Sas { pub async fn confirm(&self) -> Result<()> { let (to_device, signature) = self.inner.confirm().await?; - if let Some(req) = to_device { - let txn_id_string = req.txn_id_string(); - let request = ToDeviceRequest::new(req.event_type, &txn_id_string, req.messages); - - self.http_client.send(request).await?; + if let Some(request) = to_device { + self.client.send_to_device(&request).await?; } if let Some(s) = signature { - self.http_client.send(s).await?; + self.client.send(s).await?; } Ok(()) @@ -56,12 +54,10 @@ impl Sas { /// Cancel the interactive verification flow. pub async fn cancel(&self) -> Result<()> { - if let Some(req) = self.inner.cancel() { - let txn_id_string = req.txn_id_string(); - let request = ToDeviceRequest::new(req.event_type, &txn_id_string, req.messages); - - self.http_client.send(request).await?; + if let Some(request) = self.inner.cancel() { + self.client.send_to_device(&request).await?; } + Ok(()) } diff --git a/matrix_sdk_crypto/src/identities/device.rs b/matrix_sdk_crypto/src/identities/device.rs index 705b90be..377a7d45 100644 --- a/matrix_sdk_crypto/src/identities/device.rs +++ b/matrix_sdk_crypto/src/identities/device.rs @@ -42,7 +42,7 @@ use tracing::warn; use crate::{ olm::{InboundGroupSession, PrivateCrossSigningIdentity, Session}, store::{Changes, DeviceChanges}, - OutgoingRequest, OutgoingRequests, + OutgoingRequest, OutgoingRequests, OutgoingVerificationRequest, }; #[cfg(test)] use crate::{OlmMachine, ReadOnlyAccount}; @@ -97,7 +97,7 @@ impl Device { .start_sas(self.inner.clone()) .await?; - if let OutgoingRequests::ToDeviceRequest(r) = request { + if let OutgoingVerificationRequest::ToDevice(r) = request { Ok((sas, r)) } else { panic!("Invalid verification request type"); diff --git a/matrix_sdk_crypto/src/lib.rs b/matrix_sdk_crypto/src/lib.rs index fd31a94a..e4262e3c 100644 --- a/matrix_sdk_crypto/src/lib.rs +++ b/matrix_sdk_crypto/src/lib.rs @@ -51,6 +51,7 @@ pub use machine::OlmMachine; pub use olm::EncryptionSettings; pub(crate) use olm::ReadOnlyAccount; pub use requests::{ - IncomingResponse, KeysQueryRequest, OutgoingRequest, OutgoingRequests, ToDeviceRequest, + IncomingResponse, KeysQueryRequest, OutgoingRequest, OutgoingRequests, + OutgoingVerificationRequest, RoomMessageRequest, ToDeviceRequest, }; pub use verification::{Sas, VerificationRequest}; diff --git a/matrix_sdk_crypto/src/machine.rs b/matrix_sdk_crypto/src/machine.rs index f4755f4d..585962a2 100644 --- a/matrix_sdk_crypto/src/machine.rs +++ b/matrix_sdk_crypto/src/machine.rs @@ -322,6 +322,7 @@ impl OlmMachine { } requests.append(&mut self.outgoing_to_device_requests()); + requests.append(&mut self.verification_machine.outgoing_room_message_requests()); requests.append(&mut self.key_request_machine.outgoing_to_device_requests()); requests @@ -360,6 +361,9 @@ impl OlmMachine { IncomingResponse::SignatureUpload(_) => { self.verification_machine.mark_request_as_sent(request_id); } + IncomingResponse::RoomMessage(_) => { + self.verification_machine.mark_request_as_sent(request_id); + } }; Ok(()) diff --git a/matrix_sdk_crypto/src/requests.rs b/matrix_sdk_crypto/src/requests.rs index fb619018..97cd4e89 100644 --- a/matrix_sdk_crypto/src/requests.rs +++ b/matrix_sdk_crypto/src/requests.rs @@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +#![allow(missing_docs)] + use std::{collections::BTreeMap, sync::Arc, time::Duration}; use matrix_sdk_common::{ @@ -26,10 +28,11 @@ use matrix_sdk_common::{ upload_signing_keys::Response as SigningKeysUploadResponse, CrossSigningKey, }, + message::send_message_event::Response as RoomMessageResponse, to_device::{send_event_to_device::Response as ToDeviceResponse, DeviceIdOrAllDevices}, }, - events::EventType, - identifiers::{DeviceIdBox, UserId}, + events::{AnyMessageEventContent, EventType}, + identifiers::{DeviceIdBox, RoomId, UserId}, uuid::Uuid, }; @@ -120,6 +123,7 @@ pub enum OutgoingRequests { /// Signature upload request, this request is used after a successful device /// or user verification is done. SignatureUpload(SignatureUploadRequest), + RoomMessage(RoomMessageRequest), } #[cfg(test)] @@ -150,6 +154,12 @@ impl From for OutgoingRequests { } } +impl From for OutgoingRequests { + fn from(request: RoomMessageRequest) -> Self { + OutgoingRequests::RoomMessage(request) + } +} + impl From for OutgoingRequests { fn from(request: SignatureUploadRequest) -> Self { OutgoingRequests::SignatureUpload(request) @@ -176,6 +186,7 @@ pub enum IncomingResponse<'a> { /// The cross signing keys upload response, marking our private cross /// signing identity as shared. SignatureUpload(&'a SignatureUploadResponse), + RoomMessage(&'a RoomMessageResponse), } impl<'a> From<&'a KeysUploadResponse> for IncomingResponse<'a> { @@ -196,6 +207,12 @@ impl<'a> From<&'a ToDeviceResponse> for IncomingResponse<'a> { } } +impl<'a> From<&'a RoomMessageResponse> for IncomingResponse<'a> { + fn from(response: &'a RoomMessageResponse) -> Self { + IncomingResponse::RoomMessage(response) + } +} + impl<'a> From<&'a KeysClaimResponse> for IncomingResponse<'a> { fn from(response: &'a KeysClaimResponse) -> Self { IncomingResponse::KeysClaim(response) @@ -230,3 +247,55 @@ impl OutgoingRequest { &self.request } } + +#[derive(Clone, Debug)] +pub struct RoomMessageRequest { + /// The room to send the event to. + pub room_id: RoomId, + + /// The transaction ID for this event. + /// + /// Clients should generate an ID unique across requests with the + /// same access token; it will be used by the server to ensure + /// idempotency of requests. + pub txn_id: Uuid, + + /// The event content to send. + pub content: AnyMessageEventContent, +} + +#[derive(Clone, Debug)] +pub enum OutgoingVerificationRequest { + ToDevice(ToDeviceRequest), + InRoom(RoomMessageRequest), +} + +impl OutgoingVerificationRequest { + pub fn request_id(&self) -> Uuid { + match self { + OutgoingVerificationRequest::ToDevice(t) => t.txn_id, + OutgoingVerificationRequest::InRoom(r) => r.txn_id, + } + } +} + +impl From for OutgoingVerificationRequest { + fn from(r: ToDeviceRequest) -> Self { + OutgoingVerificationRequest::ToDevice(r) + } +} + +impl From for OutgoingVerificationRequest { + fn from(r: RoomMessageRequest) -> Self { + OutgoingVerificationRequest::InRoom(r) + } +} + +impl From for OutgoingRequests { + fn from(request: OutgoingVerificationRequest) -> Self { + match request { + OutgoingVerificationRequest::ToDevice(r) => OutgoingRequests::ToDeviceRequest(r), + OutgoingVerificationRequest::InRoom(r) => OutgoingRequests::RoomMessage(r), + } + } +} diff --git a/matrix_sdk_crypto/src/verification/machine.rs b/matrix_sdk_crypto/src/verification/machine.rs index ad2e49f3..b2bace8b 100644 --- a/matrix_sdk_crypto/src/verification/machine.rs +++ b/matrix_sdk_crypto/src/verification/machine.rs @@ -37,7 +37,8 @@ use crate::{ olm::PrivateCrossSigningIdentity, requests::{OutgoingRequest, ToDeviceRequest}, store::{CryptoStore, CryptoStoreError}, - OutgoingRequests, ReadOnlyAccount, ReadOnlyDevice, + OutgoingRequests, OutgoingVerificationRequest, ReadOnlyAccount, ReadOnlyDevice, + RoomMessageRequest, }; #[derive(Clone, Debug)] @@ -49,6 +50,7 @@ pub struct VerificationMachine { room_verifications: Arc>, requests: Arc>, outgoing_to_device_messages: Arc>, + outgoing_room_messages: Arc>, } impl VerificationMachine { @@ -65,13 +67,14 @@ impl VerificationMachine { requests: DashMap::new().into(), outgoing_to_device_messages: DashMap::new().into(), room_verifications: DashMap::new().into(), + outgoing_room_messages: DashMap::new().into(), } } pub async fn start_sas( &self, device: ReadOnlyDevice, - ) -> Result<(Sas, OutgoingRequests), CryptoStoreError> { + ) -> Result<(Sas, OutgoingVerificationRequest), CryptoStoreError> { let identity = self.store.get_user_identity(device.user_id()).await?; let private_identity = self.private_identity.lock().await.clone(); @@ -83,8 +86,13 @@ impl VerificationMachine { identity, ); - let request: OutgoingRequests = match content { - OutgoingContent::Room(c) => todo!(), + let request = match content { + OutgoingContent::Room(r, c) => RoomMessageRequest { + room_id: r, + txn_id: Uuid::new_v4(), + content: c, + } + .into(), OutgoingContent::ToDevice(c) => { let request = content_to_request(device.user_id(), device.device_id(), c); @@ -127,7 +135,23 @@ impl VerificationMachine { self.outgoing_to_device_messages.insert(request_id, request); } - OutgoingContent::Room(c) => todo!(), + OutgoingContent::Room(r, c) => { + let request_id = Uuid::new_v4(); + + let request = OutgoingRequest { + request: Arc::new( + RoomMessageRequest { + room_id: r, + txn_id: request_id.clone(), + content: c, + } + .into(), + ), + request_id, + }; + + self.outgoing_room_messages.insert(request_id, request); + } } } @@ -138,9 +162,17 @@ impl VerificationMachine { } pub fn mark_request_as_sent(&self, uuid: &Uuid) { + self.outgoing_room_messages.remove(uuid); self.outgoing_to_device_messages.remove(uuid); } + pub fn outgoing_room_message_requests(&self) -> Vec { + self.outgoing_room_messages + .iter() + .map(|r| (*r).clone()) + .collect() + } + pub fn outgoing_to_device_requests(&self) -> Vec { #[allow(clippy::map_clone)] self.outgoing_to_device_messages @@ -215,9 +247,23 @@ impl VerificationMachine { Ok(s) => { // TODO we need to queue up the accept event // here. - let accept_event = s.accept(); + info!( + "Started a new SAS verification, \ + automatically accepting because of in-room" + ); + + let accept_request = s.accept().unwrap(); + self.room_verifications .insert(e.content.relation.event_id.clone(), s); + + self.outgoing_room_messages.insert( + accept_request.request_id(), + OutgoingRequest { + request_id: accept_request.request_id(), + request: Arc::new(accept_request.into()), + }, + ); } Err(c) => { warn!( diff --git a/matrix_sdk_crypto/src/verification/requests.rs b/matrix_sdk_crypto/src/verification/requests.rs index ffffa55e..c7f9d950 100644 --- a/matrix_sdk_crypto/src/verification/requests.rs +++ b/matrix_sdk_crypto/src/verification/requests.rs @@ -93,13 +93,15 @@ impl VerificationRequest { ) -> Result { match &*self.inner.lock().unwrap() { InnerRequest::Ready(s) => s.into_started_sas( - event, + &event.clone().into_full_event(self.room_id().clone()), self.store.clone(), self.account.clone(), self.private_cross_signing_identity.clone(), device, user_identity, ), + // TODO cancel here since we got a missmatched message or do + // nothing? _ => todo!(), } } @@ -128,7 +130,7 @@ impl InnerRequest { fn into_started_sas( &mut self, - event: &SyncMessageEvent, + event: &MessageEvent, store: Arc>, account: ReadOnlyAccount, private_identity: PrivateCrossSigningIdentity, @@ -299,7 +301,7 @@ struct Ready { impl RequestState { fn into_started_sas( &self, - event: &SyncMessageEvent, + event: &MessageEvent, store: Arc>, account: ReadOnlyAccount, private_identity: PrivateCrossSigningIdentity, @@ -312,7 +314,7 @@ impl RequestState { other_device, store, &event.sender, - event.content.clone(), + (event.room_id.clone(), event.content.clone()), other_identity, ) } @@ -325,13 +327,14 @@ impl RequestState { other_device: ReadOnlyDevice, other_identity: Option, ) -> (Sas, OutgoingContent) { - Sas::start( - account, - private_identity, - other_device, - store, - other_identity, - ) + todo!() + // Sas::start_in_room( + // account, + // private_identity, + // other_device, + // store, + // other_identity, + // ) } } diff --git a/matrix_sdk_crypto/src/verification/sas/event_enums.rs b/matrix_sdk_crypto/src/verification/sas/event_enums.rs index 830b2939..7d9d3f7c 100644 --- a/matrix_sdk_crypto/src/verification/sas/event_enums.rs +++ b/matrix_sdk_crypto/src/verification/sas/event_enums.rs @@ -19,11 +19,13 @@ use std::convert::TryInto; use matrix_sdk_common::{ events::{ key::verification::{ + accept::{AcceptEventContent, AcceptToDeviceEventContent}, start::{StartEventContent, StartMethod, StartToDeviceEventContent}, KeyAgreementProtocol, }, AnyMessageEventContent, AnyToDeviceEventContent, MessageEvent, ToDeviceEvent, }, + identifiers::RoomId, CanonicalJsonValue, }; @@ -32,28 +34,28 @@ use super::FlowId; #[derive(Clone, Debug)] pub enum StartContent { ToDevice(StartToDeviceEventContent), - Room(StartEventContent), + Room(RoomId, StartEventContent), } impl StartContent { pub fn method(&self) -> &StartMethod { match self { StartContent::ToDevice(c) => &c.method, - StartContent::Room(c) => &c.method, + StartContent::Room(_, c) => &c.method, } } pub fn flow_id(&self) -> FlowId { match self { StartContent::ToDevice(c) => FlowId::ToDevice(c.transaction_id.clone()), - StartContent::Room(c) => FlowId::InRoom(c.relation.event_id.clone()), + StartContent::Room(r, c) => FlowId::InRoom(r.clone(), c.relation.event_id.clone()), } } 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), + StartContent::Room(_, c) => serde_json::to_value(c), }; content @@ -63,9 +65,9 @@ impl StartContent { } } -impl From for StartContent { - fn from(content: StartEventContent) -> Self { - StartContent::Room(content) +impl From<(RoomId, StartEventContent)> for StartContent { + fn from(tuple: (RoomId, StartEventContent)) -> Self { + StartContent::Room(tuple.0, tuple.1) } } @@ -75,16 +77,34 @@ impl From for StartContent { } } +#[derive(Clone, Debug)] +pub enum AcceptContent { + ToDevice(AcceptToDeviceEventContent), + Room(RoomId, AcceptEventContent), +} + +impl From for AcceptContent { + fn from(content: AcceptToDeviceEventContent) -> Self { + AcceptContent::ToDevice(content) + } +} + +impl From<(RoomId, AcceptEventContent)> for AcceptContent { + fn from(content: (RoomId, AcceptEventContent)) -> Self { + AcceptContent::Room(content.0, content.1) + } +} + #[derive(Clone, Debug)] pub enum OutgoingContent { - Room(AnyMessageEventContent), + Room(RoomId, AnyMessageEventContent), ToDevice(AnyToDeviceEventContent), } impl From for OutgoingContent { fn from(content: StartContent) -> Self { match content { - StartContent::Room(c) => AnyMessageEventContent::KeyVerificationStart(c).into(), + StartContent::Room(r, c) => (r, AnyMessageEventContent::KeyVerificationStart(c)).into(), StartContent::ToDevice(c) => AnyToDeviceEventContent::KeyVerificationStart(c).into(), } } @@ -96,8 +116,8 @@ impl From for OutgoingContent { } } -impl From for OutgoingContent { - fn from(content: AnyMessageEventContent) -> Self { - OutgoingContent::Room(content) +impl From<(RoomId, AnyMessageEventContent)> for OutgoingContent { + fn from(content: (RoomId, AnyMessageEventContent)) -> Self { + OutgoingContent::Room(content.0, content.1) } } diff --git a/matrix_sdk_crypto/src/verification/sas/inner_sas.rs b/matrix_sdk_crypto/src/verification/sas/inner_sas.rs index cf2a2476..1db06ab2 100644 --- a/matrix_sdk_crypto/src/verification/sas/inner_sas.rs +++ b/matrix_sdk_crypto/src/verification/sas/inner_sas.rs @@ -27,7 +27,7 @@ use matrix_sdk_common::{ }, AnyToDeviceEvent, AnyToDeviceEventContent, MessageEvent, ToDeviceEvent, }, - identifiers::{EventId, UserId}, + identifiers::{EventId, RoomId, UserId}, }; use crate::{ @@ -36,7 +36,7 @@ use crate::{ }; use super::{ - event_enums::OutgoingContent, + event_enums::{AcceptContent, OutgoingContent}, sas_state::{ Accepted, Canceled, Confirmed, Created, Done, FlowId, KeyReceived, MacReceived, SasState, Started, @@ -69,11 +69,18 @@ impl InnerSas { pub fn start_in_room( event_id: EventId, + room_id: RoomId, account: ReadOnlyAccount, other_device: ReadOnlyDevice, other_identity: Option, ) -> (InnerSas, OutgoingContent) { - let sas = SasState::::new_in_room(event_id, account, other_device, other_identity); + let sas = SasState::::new_in_room( + room_id, + event_id, + account, + other_device, + other_identity, + ); let content = sas.as_content(); (InnerSas::Created(sas), content) } @@ -97,7 +104,7 @@ impl InnerSas { } } - pub fn accept(&self) -> Option { + pub fn accept(&self) -> Option { if let InnerSas::Started(s) = self { Some(s.as_content()) } else { diff --git a/matrix_sdk_crypto/src/verification/sas/mod.rs b/matrix_sdk_crypto/src/verification/sas/mod.rs index 590f0de7..0e17d5e5 100644 --- a/matrix_sdk_crypto/src/verification/sas/mod.rs +++ b/matrix_sdk_crypto/src/verification/sas/mod.rs @@ -20,6 +20,7 @@ mod sas_state; #[cfg(test)] use std::time::Instant; +use event_enums::AcceptContent; use std::sync::{Arc, Mutex}; use tracing::{error, info, trace, warn}; @@ -30,15 +31,18 @@ use matrix_sdk_common::{ cancel::CancelCode, start::{StartEventContent, StartToDeviceEventContent}, }, - AnyToDeviceEvent, AnyToDeviceEventContent, MessageEvent, ToDeviceEvent, + AnyMessageEventContent, AnyToDeviceEvent, AnyToDeviceEventContent, MessageEvent, + ToDeviceEvent, }, identifiers::{DeviceId, EventId, RoomId, UserId}, + uuid::Uuid, }; use crate::{ error::SignatureError, identities::{LocalTrust, ReadOnlyDevice, UserIdentities}, olm::PrivateCrossSigningIdentity, + requests::{OutgoingVerificationRequest, RoomMessageRequest}, store::{Changes, CryptoStore, CryptoStoreError, DeviceChanges}, ReadOnlyAccount, ToDeviceRequest, }; @@ -189,6 +193,7 @@ impl Sas { ) -> (Sas, OutgoingContent) { let (inner, content) = InnerSas::start_in_room( flow_id, + room_id, account.clone(), other_device.clone(), other_identity.clone(), @@ -249,10 +254,18 @@ impl Sas { /// /// This does nothing if the verification was already accepted, otherwise it /// returns an `AcceptEventContent` that needs to be sent out. - pub fn accept(&self) -> Option { - self.inner.lock().unwrap().accept().map(|c| { - let content = AnyToDeviceEventContent::KeyVerificationAccept(c); - self.content_to_request(content) + pub fn accept(&self) -> Option { + self.inner.lock().unwrap().accept().map(|c| match c { + AcceptContent::ToDevice(c) => { + let content = AnyToDeviceEventContent::KeyVerificationAccept(c); + self.content_to_request(content).into() + } + AcceptContent::Room(room_id, content) => RoomMessageRequest { + room_id, + txn_id: Uuid::new_v4(), + content: AnyMessageEventContent::KeyVerificationAccept(content), + } + .into(), }) } diff --git a/matrix_sdk_crypto/src/verification/sas/sas_state.rs b/matrix_sdk_crypto/src/verification/sas/sas_state.rs index 3f1d7373..2ae05e32 100644 --- a/matrix_sdk_crypto/src/verification/sas/sas_state.rs +++ b/matrix_sdk_crypto/src/verification/sas/sas_state.rs @@ -14,6 +14,7 @@ use std::{ convert::TryFrom, + matches, sync::{Arc, Mutex}, time::{Duration, Instant}, }; @@ -24,8 +25,8 @@ use matrix_sdk_common::{ events::{ key::verification::{ accept::{ - AcceptMethod, AcceptToDeviceEventContent, MSasV1Content as AcceptV1Content, - MSasV1ContentInit as AcceptV1ContentInit, + AcceptEventContent, AcceptMethod, AcceptToDeviceEventContent, + MSasV1Content as AcceptV1Content, MSasV1ContentInit as AcceptV1ContentInit, }, cancel::{CancelCode, CancelToDeviceEventContent}, key::KeyToDeviceEventContent, @@ -39,13 +40,13 @@ use matrix_sdk_common::{ }, AnyMessageEventContent, AnyToDeviceEventContent, MessageEvent, ToDeviceEvent, }, - identifiers::{DeviceId, EventId, UserId}, + identifiers::{DeviceId, EventId, RoomId, UserId}, uuid::Uuid, }; use tracing::error; use super::{ - event_enums::{OutgoingContent, StartContent}, + event_enums::{AcceptContent, OutgoingContent, StartContent}, helpers::{ calculate_commitment, get_decimal, get_emoji, get_mac_content, receive_mac_event, SasIds, }, @@ -74,20 +75,28 @@ const MAX_EVENT_TIMEOUT: Duration = Duration::from_secs(60); #[derive(Clone, Debug)] pub enum FlowId { ToDevice(String), - InRoom(EventId), + InRoom(RoomId, EventId), } impl FlowId { + pub fn room_id(&self) -> Option<&RoomId> { + if let FlowId::InRoom(r, _) = &self { + Some(r) + } else { + None + } + } + pub fn to_string(&self) -> String { match self { - FlowId::InRoom(r) => r.to_string(), + FlowId::InRoom(_, r) => r.to_string(), FlowId::ToDevice(t) => t.to_string(), } } pub fn as_str(&self) -> &str { match self { - FlowId::InRoom(r) => r.as_str(), + FlowId::InRoom(_, r) => r.as_str(), FlowId::ToDevice(t) => t.as_str(), } } @@ -340,12 +349,13 @@ impl SasState { /// /// * `other_identity` - The identity of the other user if one exists. pub fn new_in_room( + room_id: RoomId, event_id: EventId, account: ReadOnlyAccount, other_device: ReadOnlyDevice, other_identity: Option, ) -> SasState { - let flow_id = FlowId::InRoom(event_id); + let flow_id = FlowId::InRoom(room_id, event_id); Self::new_helper(flow_id, account, other_device, other_identity) } @@ -380,7 +390,7 @@ impl SasState { pub fn as_start_content(&self) -> StartContent { match self.verification_flow_id.as_ref() { - FlowId::ToDevice(s) => StartContent::ToDevice(StartToDeviceEventContent { + FlowId::ToDevice(_) => StartContent::ToDevice(StartToDeviceEventContent { transaction_id: self.verification_flow_id.to_string(), from_device: self.device_id().into(), method: StartMethod::MSasV1( @@ -388,16 +398,19 @@ impl SasState { .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(), + FlowId::InRoom(r, e) => StartContent::Room( + r.clone(), + 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(), + }, }, - }), + ), } } @@ -558,25 +571,40 @@ impl SasState { /// This should be sent out automatically if the SAS verification flow has /// been started because of a /// m.key.verification.request -> m.key.verification.ready flow. - pub fn as_content(&self) -> AcceptToDeviceEventContent { + pub fn as_content(&self) -> AcceptContent { let accepted_protocols = AcceptedProtocols::default(); - AcceptToDeviceEventContent { - transaction_id: self.verification_flow_id.to_string(), - method: AcceptMethod::MSasV1( - AcceptV1ContentInit { - commitment: self.state.commitment.clone(), - hash: accepted_protocols.hash, - key_agreement_protocol: accepted_protocols.key_agreement_protocol, - message_authentication_code: accepted_protocols.message_auth_code, - short_authentication_string: self - .state - .protocol_definitions - .short_authentication_string - .clone(), - } + let method = AcceptMethod::MSasV1( + AcceptV1ContentInit { + commitment: self.state.commitment.clone(), + hash: accepted_protocols.hash, + key_agreement_protocol: accepted_protocols.key_agreement_protocol, + message_authentication_code: accepted_protocols.message_auth_code, + short_authentication_string: self + .state + .protocol_definitions + .short_authentication_string + .clone(), + } + .into(), + ); + + match self.verification_flow_id.as_ref() { + FlowId::ToDevice(_) => AcceptToDeviceEventContent { + transaction_id: self.verification_flow_id.to_string(), + method, + } + .into(), + FlowId::InRoom(r, e) => ( + r.clone(), + AcceptEventContent { + method, + relation: Relation { + event_id: e.clone(), + }, + }, + ) .into(), - ), } } @@ -676,7 +704,7 @@ impl SasState { transaction_id: s.to_string(), key: self.inner.lock().unwrap().public_key(), }, - FlowId::InRoom(r) => { + FlowId::InRoom(_, r) => { todo!("In-room verifications aren't implemented") } } From d4327d4cfcc0aa2dfb3d0c59cc55bc03634b5977 Mon Sep 17 00:00:00 2001 From: Alex Black Date: Tue, 15 Dec 2020 21:15:42 +1100 Subject: [PATCH 09/20] EventEmitter: add VoIP event support (m.call.* event types) Signed-off-by: Alex Black --- matrix_sdk_base/src/client.rs | 12 +++ matrix_sdk_base/src/event_emitter/mod.rs | 57 +++++++++++ matrix_sdk_test/src/lib.rs | 2 + matrix_sdk_test/src/test_json/mod.rs | 4 +- matrix_sdk_test/src/test_json/sync.rs | 120 +++++++++++++++++++++++ 5 files changed, 194 insertions(+), 1 deletion(-) diff --git a/matrix_sdk_base/src/client.rs b/matrix_sdk_base/src/client.rs index 55142e98..3839419e 100644 --- a/matrix_sdk_base/src/client.rs +++ b/matrix_sdk_base/src/client.rs @@ -1498,6 +1498,18 @@ impl BaseClient { .on_custom_event(room, &CustomEvent::Message(e)) .await } + AnySyncMessageEvent::CallInvite(e) => { + event_emitter.on_room_call_invite(room, e).await + } + AnySyncMessageEvent::CallAnswer(e) => { + event_emitter.on_room_call_answer(room, e).await + } + AnySyncMessageEvent::CallCandidates(e) => { + event_emitter.on_room_call_candidates(room, e).await + } + AnySyncMessageEvent::CallHangup(e) => { + event_emitter.on_room_call_hangup(room, e).await + } _ => {} }, AnySyncRoomEvent::RedactedState(_event) => {} diff --git a/matrix_sdk_base/src/event_emitter/mod.rs b/matrix_sdk_base/src/event_emitter/mod.rs index 7a334f4d..8f5937a8 100644 --- a/matrix_sdk_base/src/event_emitter/mod.rs +++ b/matrix_sdk_base/src/event_emitter/mod.rs @@ -19,6 +19,10 @@ use serde_json::value::RawValue as RawJsonValue; use crate::{ events::{ + call::{ + answer::AnswerEventContent, candidates::CandidatesEventContent, + hangup::HangupEventContent, invite::InviteEventContent, + }, custom::CustomEventContent, fully_read::FullyReadEventContent, ignored_user_list::IgnoredUserListEventContent, @@ -135,6 +139,19 @@ pub trait EventEmitter: Send + Sync { _: &SyncMessageEvent, ) { } + /// Fires when `Client` receives a `RoomEvent::CallInvite` event + async fn on_room_call_invite(&self, _: SyncRoom, _: &SyncMessageEvent) {} + /// Fires when `Client` receives a `RoomEvent::CallAnswer` event + async fn on_room_call_answer(&self, _: SyncRoom, _: &SyncMessageEvent) {} + /// Fires when `Client` receives a `RoomEvent::CallCandidates` event + async fn on_room_call_candidates( + &self, + _: SyncRoom, + _: &SyncMessageEvent, + ) { + } + /// Fires when `Client` receives a `RoomEvent::CallHangup` event + async fn on_room_call_hangup(&self, _: SyncRoom, _: &SyncMessageEvent) {} /// Fires when `Client` receives a `RoomEvent::RoomRedaction` event. async fn on_room_redaction(&self, _: SyncRoom, _: &SyncRedactionEvent) {} /// Fires when `Client` receives a `RoomEvent::RoomPowerLevels` event. @@ -317,6 +334,22 @@ mod test { ) { self.0.lock().await.push("feedback".to_string()) } + async fn on_room_call_invite(&self, _: SyncRoom, _: &SyncMessageEvent) { + self.0.lock().await.push("call invite".to_string()) + } + async fn on_room_call_answer(&self, _: SyncRoom, _: &SyncMessageEvent) { + self.0.lock().await.push("call answer".to_string()) + } + async fn on_room_call_candidates( + &self, + _: SyncRoom, + _: &SyncMessageEvent, + ) { + self.0.lock().await.push("call candidates".to_string()) + } + async fn on_room_call_hangup(&self, _: SyncRoom, _: &SyncMessageEvent) { + self.0.lock().await.push("call hangup".to_string()) + } async fn on_room_redaction(&self, _: SyncRoom, _: &SyncRedactionEvent) { self.0.lock().await.push("redaction".to_string()) } @@ -601,4 +634,28 @@ mod test { ], ) } + + #[async_test] + async fn event_emitter_voip() { + let vec = Arc::new(Mutex::new(Vec::new())); + let test_vec = Arc::clone(&vec); + let emitter = Box::new(EvEmitterTest(vec)); + + let client = get_client().await; + client.add_event_emitter(emitter).await; + + let mut response = sync_response(SyncResponseFile::Voip); + client.receive_sync_response(&mut response).await.unwrap(); + + let v = test_vec.lock().await; + assert_eq!( + v.as_slice(), + [ + "call invite", + "call answer", + "call candidates", + "call hangup", + ], + ) + } } diff --git a/matrix_sdk_test/src/lib.rs b/matrix_sdk_test/src/lib.rs index c1659408..02bbd4db 100644 --- a/matrix_sdk_test/src/lib.rs +++ b/matrix_sdk_test/src/lib.rs @@ -355,6 +355,7 @@ pub enum SyncResponseFile { DefaultWithSummary, Invite, Leave, + Voip, } /// Get specific API responses for testing @@ -365,6 +366,7 @@ pub fn sync_response(kind: SyncResponseFile) -> SyncResponse { SyncResponseFile::DefaultWithSummary => &test_json::DEFAULT_SYNC_SUMMARY, SyncResponseFile::Invite => &test_json::INVITE_SYNC, SyncResponseFile::Leave => &test_json::LEAVE_SYNC, + SyncResponseFile::Voip => &test_json::VOIP_SYNC, }; let response = Response::builder() diff --git a/matrix_sdk_test/src/test_json/mod.rs b/matrix_sdk_test/src/test_json/mod.rs index d3e72df5..b03d55b7 100644 --- a/matrix_sdk_test/src/test_json/mod.rs +++ b/matrix_sdk_test/src/test_json/mod.rs @@ -16,7 +16,9 @@ pub use events::{ REACTION, REDACTED, REDACTED_INVALID, REDACTED_STATE, REDACTION, REGISTRATION_RESPONSE_ERR, ROOM_ID, ROOM_MESSAGES, TYPING, }; -pub use sync::{DEFAULT_SYNC_SUMMARY, INVITE_SYNC, LEAVE_SYNC, LEAVE_SYNC_EVENT, MORE_SYNC, SYNC}; +pub use sync::{ + DEFAULT_SYNC_SUMMARY, INVITE_SYNC, LEAVE_SYNC, LEAVE_SYNC_EVENT, MORE_SYNC, SYNC, VOIP_SYNC, +}; lazy_static! { pub static ref DEVICES: JsonValue = json!({ diff --git a/matrix_sdk_test/src/test_json/sync.rs b/matrix_sdk_test/src/test_json/sync.rs index 9bd262c1..868f94e0 100644 --- a/matrix_sdk_test/src/test_json/sync.rs +++ b/matrix_sdk_test/src/test_json/sync.rs @@ -1101,3 +1101,123 @@ lazy_static! { "next_batch": "s1380317562_757269739_1655566_503953763_334052043_1209862_55290918_65705002_101146" }); } + +lazy_static! { + pub static ref VOIP_SYNC: JsonValue = json!({ + "device_one_time_keys_count": {}, + "next_batch": "s526_47314_0_7_1_1_1_11444_1", + "device_lists": { + "changed": [ + "@example:example.org" + ], + "left": [] + }, + "rooms": { + "invite": {}, + "join": { + "!SVkFJHzfwvuaIEawgC:localhost": { + "summary": {}, + "account_data": { + "events": [] + }, + "ephemeral": { + "events": [ ] + }, + "state": { + "events": [] + }, + "timeline": { + "events": [ + { + "content": { + "call_id": "12345", + "lifetime": 60000, + "offer": { + "sdp": "v=0\r\no=- 6584580628695956864 2 IN IP4 127.0.0.1[...]", + "type": "offer" + }, + "version": 0 + }, + "event_id": "$143273582443PhrSn:example.org", + "origin_server_ts": 143273582, + "room_id": "!jEsUZKDJdhlrceRyVU:example.org", + "sender": "@example:example.org", + "type": "m.call.invite", + "unsigned": { + "age": 1234 + } + }, + { + "content": { + "answer": { + "sdp": "v=0\r\no=- 6584580628695956864 2 IN IP4 127.0.0.1[...]", + "type": "answer" + }, + "call_id": "12345", + "lifetime": 60000, + "version": 0 + }, + "event_id": "$143273582443PhrSn:example.org", + "origin_server_ts": 143273582, + "room_id": "!jEsUZKDJdhlrceRyVU:example.org", + "sender": "@example:example.org", + "type": "m.call.answer", + "unsigned": { + "age": 1234 + } + }, + { + "content": { + "call_id": "12345", + "candidates": [ + { + "candidate": "candidate:863018703 1 udp 2122260223 10.9.64.156 43670 typ host generation 0", + "sdpMLineIndex": 0, + "sdpMid": "audio" + } + ], + "version": 0 + }, + "event_id": "$143273582443PhrSn:example.org", + "origin_server_ts": 143273582, + "room_id": "!jEsUZKDJdhlrceRyVU:example.org", + "sender": "@example:example.org", + "type": "m.call.candidates", + "unsigned": { + "age": 1234 + } + }, + { + "content": { + "call_id": "12345", + "version": 0 + }, + "event_id": "$143273582443PhrSn:example.org", + "origin_server_ts": 143273582, + "room_id": "!jEsUZKDJdhlrceRyVU:example.org", + "sender": "@example:example.org", + "type": "m.call.hangup", + "unsigned": { + "age": 1234 + } + } + ], + "limited": true, + "prev_batch": "t392-516_47314_0_7_1_1_1_11444_1" + }, + "unread_notifications": { + "highlight_count": 0, + "notification_count": 11 + } + } + }, + "leave": {} + }, + "to_device": { + "events": [] + }, + "presence": { + "events": [] + } + }); +} From 79102b3390738e1cbbfbba11a0ee4d916e9d5e15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Thu, 17 Dec 2020 12:15:11 +0100 Subject: [PATCH 10/20] crypto: Make the cancelations output only CancelContents. --- matrix_sdk/examples/emoji_verification.rs | 47 +++++-- matrix_sdk/src/client.rs | 2 +- matrix_sdk/src/sas.rs | 23 +++- matrix_sdk_crypto/src/requests.rs | 18 +++ matrix_sdk_crypto/src/verification/machine.rs | 84 ++++++++++--- .../src/verification/sas/event_enums.rs | 118 +++++++++++++++++- .../src/verification/sas/helpers.rs | 50 ++++++-- .../src/verification/sas/inner_sas.rs | 96 ++++++++++---- matrix_sdk_crypto/src/verification/sas/mod.rs | 70 ++++++++--- .../src/verification/sas/sas_state.rs | 117 +++++++++++------ 10 files changed, 498 insertions(+), 127 deletions(-) diff --git a/matrix_sdk/examples/emoji_verification.rs b/matrix_sdk/examples/emoji_verification.rs index 04f1375c..62ffb9a3 100644 --- a/matrix_sdk/examples/emoji_verification.rs +++ b/matrix_sdk/examples/emoji_verification.rs @@ -135,19 +135,42 @@ async fn login( if !initial.load(Ordering::SeqCst) { for (_room_id, room_info) in response.rooms.join { for event in room_info.timeline.events { - if let Ok(AnySyncRoomEvent::Message(AnySyncMessageEvent::RoomMessage(m))) = - event.deserialize() - { - if let MessageEventContent::VerificationRequest(_) = &m.content { - let request = client - .get_verification_request(&m.event_id) - .await - .expect("Request object wasn't created"); + if let AnySyncRoomEvent::Message(event) = event.deserialize().unwrap() { + match event { + AnySyncMessageEvent::RoomMessage(m) => { + if let MessageEventContent::VerificationRequest(_) = &m.content + { + let request = client + .get_verification_request(&m.event_id) + .await + .expect("Request object wasn't created"); - request - .accept() - .await - .expect("Can't accept verification request"); + request + .accept() + .await + .expect("Can't accept verification request"); + } + } + AnySyncMessageEvent::KeyVerificationKey(e) => { + let sas = client + .get_verification(&e.content.relation.event_id.as_str()) + .await + .expect("Sas object wasn't created"); + + tokio::spawn(wait_for_confirmation((*client).clone(), sas)); + } + AnySyncMessageEvent::KeyVerificationMac(e) => { + let sas = client + .get_verification(&e.content.relation.event_id.as_str()) + .await + .expect("Sas object wasn't created"); + + if sas.is_done() { + print_result(&sas); + print_devices(&e.sender, &client).await; + } + } + _ => (), } } } diff --git a/matrix_sdk/src/client.rs b/matrix_sdk/src/client.rs index d3c94028..114275d9 100644 --- a/matrix_sdk/src/client.rs +++ b/matrix_sdk/src/client.rs @@ -1160,7 +1160,7 @@ impl Client { Ok(()) } - async fn room_send_helper( + pub(crate) async fn room_send_helper( &self, request: &RoomMessageRequest, ) -> Result { diff --git a/matrix_sdk/src/sas.rs b/matrix_sdk/src/sas.rs index 16da6156..ccbc4224 100644 --- a/matrix_sdk/src/sas.rs +++ b/matrix_sdk/src/sas.rs @@ -39,10 +39,18 @@ impl Sas { /// Confirm that the short auth strings match on both sides. pub async fn confirm(&self) -> Result<()> { - let (to_device, signature) = self.inner.confirm().await?; + let (request, signature) = self.inner.confirm().await?; - if let Some(request) = to_device { - self.client.send_to_device(&request).await?; + match request { + Some(OutgoingVerificationRequest::InRoom(r)) => { + self.client.room_send_helper(&r).await?; + } + + Some(OutgoingVerificationRequest::ToDevice(r)) => { + self.client.send_to_device(&r).await?; + } + + None => (), } if let Some(s) = signature { @@ -55,7 +63,14 @@ impl Sas { /// Cancel the interactive verification flow. pub async fn cancel(&self) -> Result<()> { if let Some(request) = self.inner.cancel() { - self.client.send_to_device(&request).await?; + match request { + OutgoingVerificationRequest::ToDevice(r) => { + self.client.send_to_device(&r).await?; + } + OutgoingVerificationRequest::InRoom(r) => { + self.client.room_send_helper(&r).await?; + } + } } Ok(()) diff --git a/matrix_sdk_crypto/src/requests.rs b/matrix_sdk_crypto/src/requests.rs index 97cd4e89..a14b46bb 100644 --- a/matrix_sdk_crypto/src/requests.rs +++ b/matrix_sdk_crypto/src/requests.rs @@ -166,6 +166,24 @@ impl From for OutgoingRequests { } } +impl From for OutgoingRequest { + fn from(r: OutgoingVerificationRequest) -> Self { + Self { + request_id: r.request_id(), + request: Arc::new(r.into()), + } + } +} + +impl From for OutgoingRequest { + fn from(r: SignatureUploadRequest) -> Self { + Self { + request_id: Uuid::new_v4(), + request: Arc::new(r.into()), + } + } +} + /// Enum over all the incoming responses we need to receive. #[derive(Debug)] pub enum IncomingResponse<'a> { diff --git a/matrix_sdk_crypto/src/verification/machine.rs b/matrix_sdk_crypto/src/verification/machine.rs index b2bace8b..fc168782 100644 --- a/matrix_sdk_crypto/src/verification/machine.rs +++ b/matrix_sdk_crypto/src/verification/machine.rs @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::sync::Arc; +use std::{convert::TryFrom, sync::Arc}; use dashmap::DashMap; @@ -20,7 +20,7 @@ use tracing::{info, trace, warn}; use matrix_sdk_common::{ events::{ - room::message::MessageEventContent, AnySyncMessageEvent, AnySyncRoomEvent, + room::message::MessageEventContent, AnyMessageEvent, AnySyncMessageEvent, AnySyncRoomEvent, AnyToDeviceEvent, AnyToDeviceEventContent, }, identifiers::{DeviceId, EventId, RoomId, UserId}, @@ -112,8 +112,19 @@ impl VerificationMachine { } pub fn get_sas(&self, transaction_id: &str) -> Option { - #[allow(clippy::map_clone)] - self.verifications.get(transaction_id).map(|s| s.clone()) + let sas = if let Ok(e) = EventId::try_from(transaction_id) { + #[allow(clippy::map_clone)] + self.room_verifications.get(&e).map(|s| s.clone()) + } else { + None + }; + + if sas.is_some() { + sas + } else { + #[allow(clippy::map_clone)] + self.verifications.get(transaction_id).map(|s| s.clone()) + } } fn queue_up_content( @@ -155,6 +166,13 @@ impl VerificationMachine { } } + #[allow(dead_code)] + fn receive_room_event_helper(&self, sas: &Sas, event: &AnyMessageEvent) { + if let Some(c) = sas.receive_room_event(event) { + self.queue_up_content(sas.other_user_id(), sas.other_device_id(), c); + } + } + fn receive_event_helper(&self, sas: &Sas, event: &AnyToDeviceEvent) { if let Some(c) = sas.receive_event(event) { self.queue_up_content(sas.other_user_id(), sas.other_device_id(), c); @@ -188,9 +206,9 @@ impl VerificationMachine { for sas in self.verifications.iter() { if let Some(r) = sas.cancel_if_timed_out() { self.outgoing_to_device_messages.insert( - r.txn_id, + r.request_id(), OutgoingRequest { - request_id: r.txn_id, + request_id: r.request_id(), request: Arc::new(r.into()), }, ); @@ -204,6 +222,12 @@ impl VerificationMachine { event: &AnySyncRoomEvent, ) -> Result<(), CryptoStoreError> { if let AnySyncRoomEvent::Message(m) = event { + // Since this are room events we will get events that we send out on + // our own as well. + if m.sender() == self.account.user_id() { + return Ok(()); + } + match m { AnySyncMessageEvent::RoomMessage(m) => { if let MessageEventContent::VerificationRequest(r) = &m.content { @@ -245,25 +269,19 @@ impl VerificationMachine { self.store.get_user_identity(&e.sender).await?, ) { Ok(s) => { - // TODO we need to queue up the accept event - // here. info!( "Started a new SAS verification, \ automatically accepting because of in-room" ); + // TODO remove this unwrap let accept_request = s.accept().unwrap(); self.room_verifications .insert(e.content.relation.event_id.clone(), s); - self.outgoing_room_messages.insert( - accept_request.request_id(), - OutgoingRequest { - request_id: accept_request.request_id(), - request: Arc::new(accept_request.into()), - }, - ); + self.outgoing_room_messages + .insert(accept_request.request_id(), accept_request.into()); } Err(c) => { warn!( @@ -276,6 +294,38 @@ impl VerificationMachine { } } } + AnySyncMessageEvent::KeyVerificationKey(e) => { + if let Some(s) = self.room_verifications.get(&e.content.relation.event_id) { + self.receive_room_event_helper( + &s, + &m.clone().into_full_event(room_id.clone()), + ) + }; + } + AnySyncMessageEvent::KeyVerificationMac(e) => { + if let Some(s) = self.room_verifications.get(&e.content.relation.event_id) { + self.receive_room_event_helper( + &s, + &m.clone().into_full_event(room_id.clone()), + ); + + if s.is_done() { + match s.mark_as_done().await? { + VerificationResult::Ok => (), + VerificationResult::Cancel(r) => { + self.outgoing_to_device_messages + .insert(r.request_id(), r.into()); + } + VerificationResult::SignatureUpload(r) => { + let request: OutgoingRequest = r.into(); + + self.outgoing_to_device_messages + .insert(request.request_id, request); + } + } + } + }; + } _ => (), } } @@ -350,9 +400,9 @@ impl VerificationMachine { VerificationResult::Ok => (), VerificationResult::Cancel(r) => { self.outgoing_to_device_messages.insert( - r.txn_id, + r.request_id(), OutgoingRequest { - request_id: r.txn_id, + request_id: r.request_id(), request: Arc::new(r.into()), }, ); diff --git a/matrix_sdk_crypto/src/verification/sas/event_enums.rs b/matrix_sdk_crypto/src/verification/sas/event_enums.rs index 7d9d3f7c..35bf3034 100644 --- a/matrix_sdk_crypto/src/verification/sas/event_enums.rs +++ b/matrix_sdk_crypto/src/verification/sas/event_enums.rs @@ -14,16 +14,18 @@ #![allow(dead_code)] -use std::convert::TryInto; +use std::{collections::BTreeMap, convert::TryInto}; use matrix_sdk_common::{ events::{ key::verification::{ accept::{AcceptEventContent, AcceptToDeviceEventContent}, + cancel::{CancelEventContent, CancelToDeviceEventContent}, + key::{KeyEventContent, KeyToDeviceEventContent}, + mac::{MacEventContent, MacToDeviceEventContent}, start::{StartEventContent, StartMethod, StartToDeviceEventContent}, - KeyAgreementProtocol, }, - AnyMessageEventContent, AnyToDeviceEventContent, MessageEvent, ToDeviceEvent, + AnyMessageEventContent, AnyToDeviceEventContent, }, identifiers::RoomId, CanonicalJsonValue, @@ -95,6 +97,96 @@ impl From<(RoomId, AcceptEventContent)> for AcceptContent { } } +pub enum KeyContent { + ToDevice(KeyToDeviceEventContent), + Room(RoomId, KeyEventContent), +} + +impl KeyContent { + pub fn flow_id(&self) -> FlowId { + match self { + KeyContent::ToDevice(c) => FlowId::ToDevice(c.transaction_id.clone()), + KeyContent::Room(r, c) => FlowId::InRoom(r.clone(), c.relation.event_id.clone()), + } + } + + pub fn public_key(&self) -> &str { + match self { + KeyContent::ToDevice(c) => &c.key, + KeyContent::Room(_, c) => &c.key, + } + } +} + +impl From for KeyContent { + fn from(content: KeyToDeviceEventContent) -> Self { + KeyContent::ToDevice(content) + } +} + +impl From<(RoomId, KeyEventContent)> for KeyContent { + fn from(content: (RoomId, KeyEventContent)) -> Self { + KeyContent::Room(content.0, content.1) + } +} + +pub enum MacContent { + ToDevice(MacToDeviceEventContent), + Room(RoomId, MacEventContent), +} + +impl MacContent { + pub fn flow_id(&self) -> FlowId { + match self { + MacContent::ToDevice(c) => FlowId::ToDevice(c.transaction_id.clone()), + MacContent::Room(r, c) => FlowId::InRoom(r.clone(), c.relation.event_id.clone()), + } + } + + pub fn mac(&self) -> &BTreeMap { + match self { + MacContent::ToDevice(c) => &c.mac, + MacContent::Room(_, c) => &c.mac, + } + } + + pub fn keys(&self) -> &str { + match self { + MacContent::ToDevice(c) => &c.keys, + MacContent::Room(_, c) => &c.keys, + } + } +} + +impl From for MacContent { + fn from(content: MacToDeviceEventContent) -> Self { + MacContent::ToDevice(content) + } +} + +impl From<(RoomId, MacEventContent)> for MacContent { + fn from(content: (RoomId, MacEventContent)) -> Self { + MacContent::Room(content.0, content.1) + } +} + +pub enum CancelContent { + ToDevice(CancelToDeviceEventContent), + Room(RoomId, CancelEventContent), +} + +impl From<(RoomId, CancelEventContent)> for CancelContent { + fn from(content: (RoomId, CancelEventContent)) -> Self { + CancelContent::Room(content.0, content.1) + } +} + +impl From for CancelContent { + fn from(content: CancelToDeviceEventContent) -> Self { + CancelContent::ToDevice(content) + } +} + #[derive(Clone, Debug)] pub enum OutgoingContent { Room(RoomId, AnyMessageEventContent), @@ -110,6 +202,26 @@ impl From for OutgoingContent { } } +impl From for OutgoingContent { + fn from(content: CancelContent) -> Self { + match content { + CancelContent::Room(r, c) => { + (r, AnyMessageEventContent::KeyVerificationCancel(c)).into() + } + CancelContent::ToDevice(c) => AnyToDeviceEventContent::KeyVerificationCancel(c).into(), + } + } +} + +impl From for OutgoingContent { + fn from(content: KeyContent) -> Self { + match content { + KeyContent::Room(r, c) => (r, AnyMessageEventContent::KeyVerificationKey(c)).into(), + KeyContent::ToDevice(c) => AnyToDeviceEventContent::KeyVerificationKey(c).into(), + } + } +} + impl From for OutgoingContent { fn from(content: AnyToDeviceEventContent) -> Self { OutgoingContent::ToDevice(content) diff --git a/matrix_sdk_crypto/src/verification/sas/helpers.rs b/matrix_sdk_crypto/src/verification/sas/helpers.rs index 388fd6c2..fd6f4bca 100644 --- a/matrix_sdk_crypto/src/verification/sas/helpers.rs +++ b/matrix_sdk_crypto/src/verification/sas/helpers.rs @@ -23,7 +23,10 @@ use matrix_sdk_common::{ api::r0::to_device::DeviceIdOrAllDevices, events::{ key::verification::{ - cancel::CancelCode, mac::MacToDeviceEventContent, start::StartToDeviceEventContent, + cancel::CancelCode, + mac::{MacEventContent, MacToDeviceEventContent}, + start::StartToDeviceEventContent, + Relation, }, AnyToDeviceEventContent, EventType, ToDeviceEvent, }, @@ -38,7 +41,10 @@ use crate::{ ReadOnlyAccount, ToDeviceRequest, }; -use super::{event_enums::StartContent, sas_state::FlowId}; +use super::{ + event_enums::{MacContent, StartContent}, + sas_state::FlowId, +}; #[derive(Clone, Debug)] pub struct SasIds { @@ -186,7 +192,8 @@ pub fn receive_mac_event( sas: &OlmSas, ids: &SasIds, flow_id: &str, - event: &ToDeviceEvent, + sender: &UserId, + content: &MacContent, ) -> Result<(Vec, Vec), CancelCode> { let mut verified_devices = Vec::new(); let mut verified_identities = Vec::new(); @@ -195,25 +202,25 @@ pub fn receive_mac_event( trace!( "Received a key.verification.mac event from {} {}", - event.sender, + sender, ids.other_device.device_id() ); - let mut keys = event.content.mac.keys().cloned().collect::>(); + let mut keys = content.mac().keys().cloned().collect::>(); keys.sort(); let keys = sas .calculate_mac(&keys.join(","), &format!("{}KEY_IDS", &info)) .expect("Can't calculate SAS MAC"); - if keys != event.content.keys { + if keys != content.keys() { return Err(CancelCode::KeyMismatch); } - for (key_id, key_mac) in &event.content.mac { + for (key_id, key_mac) in content.mac() { trace!( "Checking MAC for the key id {} from {} {}", key_id, - event.sender, + sender, ids.other_device.device_id() ); let key_id: DeviceKeyId = match key_id.as_str().try_into() { @@ -227,6 +234,12 @@ pub fn receive_mac_event( .calculate_mac(key, &format!("{}{}", info, key_id)) .expect("Can't calculate SAS MAC") { + trace!( + "Successfully verified the device key {} from {}", + key_id, + sender + ); + verified_devices.push(ids.other_device.clone()); } else { return Err(CancelCode::KeyMismatch); @@ -243,7 +256,7 @@ pub fn receive_mac_event( trace!( "Successfully verified the master key {} from {}", key_id, - event.sender + sender ); verified_identities.push(identity.clone()) } else { @@ -255,7 +268,7 @@ pub fn receive_mac_event( "Key ID {} in MAC event from {} {} doesn't belong to any device \ or user identity", key_id, - event.sender, + sender, ids.other_device.device_id() ); } @@ -297,7 +310,7 @@ fn extra_mac_info_send(ids: &SasIds, flow_id: &str) -> String { /// # Panics /// /// This will panic if the public key of the other side wasn't set. -pub fn get_mac_content(sas: &OlmSas, ids: &SasIds, flow_id: &FlowId) -> MacToDeviceEventContent { +pub fn get_mac_content(sas: &OlmSas, ids: &SasIds, flow_id: &FlowId) -> MacContent { let mut mac: BTreeMap = BTreeMap::new(); let key_id = DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, ids.account.device_id()); @@ -323,8 +336,19 @@ pub fn get_mac_content(sas: &OlmSas, ids: &SasIds, flow_id: &FlowId) -> MacToDev transaction_id: s.to_string(), keys, mac, - }, - _ => todo!(), + } + .into(), + FlowId::InRoom(r, e) => ( + r.clone(), + MacEventContent { + mac, + keys, + relation: Relation { + event_id: e.clone(), + }, + }, + ) + .into(), } } diff --git a/matrix_sdk_crypto/src/verification/sas/inner_sas.rs b/matrix_sdk_crypto/src/verification/sas/inner_sas.rs index 1db06ab2..ffebb8bc 100644 --- a/matrix_sdk_crypto/src/verification/sas/inner_sas.rs +++ b/matrix_sdk_crypto/src/verification/sas/inner_sas.rs @@ -25,7 +25,8 @@ use matrix_sdk_common::{ mac::MacToDeviceEventContent, start::{StartEventContent, StartToDeviceEventContent}, }, - AnyToDeviceEvent, AnyToDeviceEventContent, MessageEvent, ToDeviceEvent, + AnyMessageEvent, AnyMessageEventContent, AnySyncMessageEvent, AnyToDeviceEvent, + AnyToDeviceEventContent, MessageEvent, ToDeviceEvent, }, identifiers::{EventId, RoomId, UserId}, }; @@ -36,7 +37,7 @@ use crate::{ }; use super::{ - event_enums::{AcceptContent, OutgoingContent}, + event_enums::{AcceptContent, CancelContent, MacContent, OutgoingContent}, sas_state::{ Accepted, Canceled, Confirmed, Created, Done, FlowId, KeyReceived, MacReceived, SasState, Started, @@ -91,7 +92,7 @@ impl InnerSas { sender: &UserId, content: impl Into, other_identity: Option, - ) -> Result { + ) -> Result { match SasState::::from_start_event( account, other_device, @@ -127,7 +128,7 @@ impl InnerSas { } } - pub fn cancel(self, code: CancelCode) -> (InnerSas, Option) { + pub fn cancel(self, code: CancelCode) -> (InnerSas, Option) { let sas = match self { InnerSas::Created(s) => s.cancel(code), InnerSas::Started(s) => s.cancel(code), @@ -142,7 +143,7 @@ impl InnerSas { (InnerSas::Canceled(sas), Some(content)) } - pub fn confirm(self) -> (InnerSas, Option) { + pub fn confirm(self) -> (InnerSas, Option) { match self { InnerSas::KeyRecieved(s) => { let sas = s.confirm(); @@ -158,6 +159,62 @@ impl InnerSas { } } + #[allow(dead_code)] + pub fn receive_room_event( + self, + event: &AnyMessageEvent, + ) -> (InnerSas, Option) { + match event { + AnyMessageEvent::KeyVerificationKey(e) => match self { + InnerSas::Accepted(s) => { + match s.into_key_received(&e.sender, (e.room_id.clone(), e.content.clone())) { + Ok(s) => (InnerSas::KeyRecieved(s), None), + Err(s) => { + let content = s.as_content(); + (InnerSas::Canceled(s), Some(content.into())) + } + } + } + InnerSas::Started(s) => { + match s.into_key_received(&e.sender, (e.room_id.clone(), e.content.clone())) { + Ok(s) => { + let content = s.as_content(); + (InnerSas::KeyRecieved(s), Some(content.into())) + } + Err(s) => { + let content = s.as_content(); + (InnerSas::Canceled(s), Some(content.into())) + } + } + } + + _ => (self, None), + }, + AnyMessageEvent::KeyVerificationMac(e) => match self { + InnerSas::KeyRecieved(s) => { + match s.into_mac_received(&e.sender, (e.room_id.clone(), e.content.clone())) { + Ok(s) => (InnerSas::MacReceived(s), None), + Err(s) => { + let content = s.as_content(); + (InnerSas::Canceled(s), Some(content.into())) + } + } + } + InnerSas::Confirmed(s) => { + match s.into_done(&e.sender, (e.room_id.clone(), e.content.clone())) { + Ok(s) => (InnerSas::Done(s), None), + Err(s) => { + let content = s.as_content(); + (InnerSas::Canceled(s), Some(content.into())) + } + } + } + _ => (self, None), + }, + _ => (self, None), + } + } + pub fn receive_event(self, event: &AnyToDeviceEvent) -> (InnerSas, Option) { match event { AnyToDeviceEvent::KeyVerificationAccept(e) => { @@ -165,14 +222,11 @@ impl InnerSas { match s.into_accepted(e) { Ok(s) => { let content = s.as_content(); - ( - InnerSas::Accepted(s), - Some(AnyToDeviceEventContent::KeyVerificationKey(content).into()), - ) + (InnerSas::Accepted(s), Some(content.into())) } Err(s) => { let content = s.as_content(); - (InnerSas::Canceled(s), Some(content)) + (InnerSas::Canceled(s), Some(content.into())) } } } else { @@ -180,41 +234,39 @@ impl InnerSas { } } AnyToDeviceEvent::KeyVerificationKey(e) => match self { - InnerSas::Accepted(s) => match s.into_key_received(e) { + InnerSas::Accepted(s) => match s.into_key_received(&e.sender, e.content.clone()) { Ok(s) => (InnerSas::KeyRecieved(s), None), Err(s) => { let content = s.as_content(); - (InnerSas::Canceled(s), Some(content)) + (InnerSas::Canceled(s), Some(content.into())) } }, - InnerSas::Started(s) => match s.into_key_received(e) { + InnerSas::Started(s) => match s.into_key_received(&e.sender, e.content.clone()) { Ok(s) => { let content = s.as_content(); - ( - InnerSas::KeyRecieved(s), - Some(AnyToDeviceEventContent::KeyVerificationKey(content).into()), - ) + (InnerSas::KeyRecieved(s), Some(content.into())) } Err(s) => { let content = s.as_content(); - (InnerSas::Canceled(s), Some(content)) + (InnerSas::Canceled(s), Some(content.into())) } }, _ => (self, None), }, AnyToDeviceEvent::KeyVerificationMac(e) => match self { - InnerSas::KeyRecieved(s) => match s.into_mac_received(e) { + InnerSas::KeyRecieved(s) => match s.into_mac_received(&e.sender, e.content.clone()) + { Ok(s) => (InnerSas::MacReceived(s), None), Err(s) => { let content = s.as_content(); - (InnerSas::Canceled(s), Some(content)) + (InnerSas::Canceled(s), Some(content.into())) } }, - InnerSas::Confirmed(s) => match s.into_done(e) { + InnerSas::Confirmed(s) => match s.into_done(&e.sender, e.content.clone()) { Ok(s) => (InnerSas::Done(s), None), Err(s) => { let content = s.as_content(); - (InnerSas::Canceled(s), Some(content)) + (InnerSas::Canceled(s), Some(content.into())) } }, _ => (self, None), diff --git a/matrix_sdk_crypto/src/verification/sas/mod.rs b/matrix_sdk_crypto/src/verification/sas/mod.rs index 0e17d5e5..8ee330d4 100644 --- a/matrix_sdk_crypto/src/verification/sas/mod.rs +++ b/matrix_sdk_crypto/src/verification/sas/mod.rs @@ -31,8 +31,8 @@ use matrix_sdk_common::{ cancel::CancelCode, start::{StartEventContent, StartToDeviceEventContent}, }, - AnyMessageEventContent, AnyToDeviceEvent, AnyToDeviceEventContent, MessageEvent, - ToDeviceEvent, + AnyMessageEvent, AnyMessageEventContent, AnySyncMessageEvent, AnyToDeviceEvent, + AnyToDeviceEventContent, MessageEvent, ToDeviceEvent, }, identifiers::{DeviceId, EventId, RoomId, UserId}, uuid::Uuid, @@ -53,13 +53,15 @@ pub use sas_state::FlowId; pub use event_enums::{OutgoingContent, StartContent}; +use self::event_enums::CancelContent; + #[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), + Cancel(OutgoingVerificationRequest), /// The verification is done and has signatures that need to be uploaded. SignatureUpload(SignatureUploadRequest), } @@ -278,7 +280,13 @@ impl Sas { /// the server. pub async fn confirm( &self, - ) -> Result<(Option, Option), CryptoStoreError> { + ) -> Result< + ( + Option, + Option, + ), + CryptoStoreError, + > { let (content, done) = { let mut guard = self.inner.lock().unwrap(); let sas: InnerSas = (*guard).clone(); @@ -288,8 +296,17 @@ impl Sas { (content, guard.is_done()) }; - let mac_request = content - .map(|c| self.content_to_request(AnyToDeviceEventContent::KeyVerificationMac(c))); + let mac_request = content.map(|c| match c { + event_enums::MacContent::ToDevice(c) => self + .content_to_request(AnyToDeviceEventContent::KeyVerificationMac(c)) + .into(), + event_enums::MacContent::Room(r, c) => RoomMessageRequest { + room_id: r, + txn_id: Uuid::new_v4(), + content: AnyMessageEventContent::KeyVerificationMac(c), + } + .into(), + }); if done { match self.mark_as_done().await? { @@ -530,22 +547,26 @@ impl Sas { /// /// Returns None if the `Sas` object is already in a canceled state, /// otherwise it returns a request that needs to be sent out. - pub fn cancel(&self) -> Option { + pub fn cancel(&self) -> Option { let mut guard = self.inner.lock().unwrap(); let sas: InnerSas = (*guard).clone(); let (sas, content) = sas.cancel(CancelCode::User); *guard = sas; - content.map(|c| { - if let OutgoingContent::ToDevice(c) = c { - self.content_to_request(c) - } else { - todo!() + content.map(|c| match c { + CancelContent::Room(room_id, content) => RoomMessageRequest { + room_id, + txn_id: Uuid::new_v4(), + content: AnyMessageEventContent::KeyVerificationCancel(content), } + .into(), + CancelContent::ToDevice(c) => self + .content_to_request(AnyToDeviceEventContent::KeyVerificationCancel(c)) + .into(), }) } - pub(crate) fn cancel_if_timed_out(&self) -> Option { + pub(crate) fn cancel_if_timed_out(&self) -> Option { if self.is_canceled() || self.is_done() { None } else if self.timed_out() { @@ -553,12 +574,16 @@ impl Sas { let sas: InnerSas = (*guard).clone(); let (sas, content) = sas.cancel(CancelCode::Timeout); *guard = sas; - content.map(|c| { - if let OutgoingContent::ToDevice(c) = c { - self.content_to_request(c) - } else { - todo!() + content.map(|c| match c { + CancelContent::Room(room_id, content) => RoomMessageRequest { + room_id, + txn_id: Uuid::new_v4(), + content: AnyMessageEventContent::KeyVerificationCancel(content), } + .into(), + CancelContent::ToDevice(c) => self + .content_to_request(AnyToDeviceEventContent::KeyVerificationCancel(c)) + .into(), }) } else { None @@ -602,6 +627,15 @@ impl Sas { self.inner.lock().unwrap().decimals() } + pub(crate) fn receive_room_event(&self, event: &AnyMessageEvent) -> Option { + let mut guard = self.inner.lock().unwrap(); + let sas: InnerSas = (*guard).clone(); + let (sas, content) = sas.receive_room_event(event); + *guard = sas; + + content + } + pub(crate) fn receive_event(&self, event: &AnyToDeviceEvent) -> Option { let mut guard = self.inner.lock().unwrap(); let sas: InnerSas = (*guard).clone(); diff --git a/matrix_sdk_crypto/src/verification/sas/sas_state.rs b/matrix_sdk_crypto/src/verification/sas/sas_state.rs index 2ae05e32..d7533c75 100644 --- a/matrix_sdk_crypto/src/verification/sas/sas_state.rs +++ b/matrix_sdk_crypto/src/verification/sas/sas_state.rs @@ -28,8 +28,8 @@ use matrix_sdk_common::{ AcceptEventContent, AcceptMethod, AcceptToDeviceEventContent, MSasV1Content as AcceptV1Content, MSasV1ContentInit as AcceptV1ContentInit, }, - cancel::{CancelCode, CancelToDeviceEventContent}, - key::KeyToDeviceEventContent, + cancel::{CancelCode, CancelEventContent, CancelToDeviceEventContent}, + key::{KeyEventContent, KeyToDeviceEventContent}, mac::MacToDeviceEventContent, start::{ MSasV1Content, MSasV1ContentInit, StartEventContent, StartMethod, @@ -46,7 +46,9 @@ use matrix_sdk_common::{ use tracing::error; use super::{ - event_enums::{AcceptContent, OutgoingContent, StartContent}, + event_enums::{ + AcceptContent, CancelContent, KeyContent, MacContent, OutgoingContent, StartContent, + }, helpers::{ calculate_commitment, get_decimal, get_emoji, get_mac_content, receive_mac_event, SasIds, }, @@ -618,14 +620,17 @@ impl SasState { /// anymore. pub fn into_key_received( self, - event: &ToDeviceEvent, + sender: &UserId, + content: impl Into, ) -> Result, SasState> { - self.check_event(&event.sender, &event.content.transaction_id) + let content = content.into(); + + self.check_event(&sender, &content.flow_id().as_str()) .map_err(|c| self.clone().cancel(c))?; let accepted_protocols = AcceptedProtocols::default(); - let their_pubkey = event.content.key.clone(); + let their_pubkey = content.public_key().to_owned(); self.inner .lock() @@ -659,20 +664,23 @@ impl SasState { /// anymore. pub fn into_key_received( self, - event: &ToDeviceEvent, + sender: &UserId, + content: impl Into, ) -> Result, SasState> { - self.check_event(&event.sender, &event.content.transaction_id) + let content = content.into(); + + self.check_event(&sender, content.flow_id().as_str()) .map_err(|c| self.clone().cancel(c))?; let commitment = calculate_commitment( - &event.content.key, + content.public_key(), self.state.start_content.as_ref().clone(), ); if self.state.commitment != commitment { Err(self.cancel(CancelCode::InvalidMessage)) } else { - let their_pubkey = event.content.key.clone(); + let their_pubkey = content.public_key().to_owned(); self.inner .lock() @@ -698,15 +706,23 @@ impl SasState { /// Get the content for the key event. /// /// The content needs to be automatically sent to the other side. - pub fn as_content(&self) -> KeyToDeviceEventContent { + pub fn as_content(&self) -> OutgoingContent { match &*self.verification_flow_id { - FlowId::ToDevice(s) => KeyToDeviceEventContent { + FlowId::ToDevice(s) => KeyContent::ToDevice(KeyToDeviceEventContent { transaction_id: s.to_string(), key: self.inner.lock().unwrap().public_key(), - }, - FlowId::InRoom(_, r) => { - todo!("In-room verifications aren't implemented") - } + }) + .into(), + FlowId::InRoom(r, e) => KeyContent::Room( + r.clone(), + KeyEventContent { + key: self.inner.lock().unwrap().public_key(), + relation: Relation { + event_id: e.clone(), + }, + }, + ) + .into(), } } } @@ -716,13 +732,23 @@ impl SasState { /// /// The content needs to be automatically sent to the other side if and only /// if we_started is false. - pub fn as_content(&self) -> KeyToDeviceEventContent { - match self.verification_flow_id.as_ref() { - FlowId::ToDevice(s) => KeyToDeviceEventContent { + pub fn as_content(&self) -> KeyContent { + match &*self.verification_flow_id { + FlowId::ToDevice(s) => KeyContent::ToDevice(KeyToDeviceEventContent { transaction_id: s.to_string(), key: self.inner.lock().unwrap().public_key(), - }, - _ => todo!(), + }) + .into(), + FlowId::InRoom(r, e) => KeyContent::Room( + r.clone(), + KeyEventContent { + key: self.inner.lock().unwrap().public_key(), + relation: Relation { + event_id: e.clone(), + }, + }, + ) + .into(), } } @@ -763,16 +789,20 @@ impl SasState { /// the other side. pub fn into_mac_received( self, - event: &ToDeviceEvent, + sender: &UserId, + content: impl Into, ) -> Result, SasState> { - self.check_event(&event.sender, &event.content.transaction_id) + let content = content.into(); + + self.check_event(&sender, content.flow_id().as_str()) .map_err(|c| self.clone().cancel(c))?; let (devices, master_keys) = receive_mac_event( &self.inner.lock().unwrap(), &self.ids, self.verification_flow_id.as_str(), - event, + sender, + &content, ) .map_err(|c| self.clone().cancel(c))?; @@ -819,16 +849,20 @@ impl SasState { /// the other side. pub fn into_done( self, - event: &ToDeviceEvent, + sender: &UserId, + content: impl Into, ) -> Result, SasState> { - self.check_event(&event.sender, &event.content.transaction_id) + let content = content.into(); + + self.check_event(&sender, &content.flow_id().as_str()) .map_err(|c| self.clone().cancel(c))?; let (devices, master_keys) = receive_mac_event( &self.inner.lock().unwrap(), &self.ids, &self.verification_flow_id.as_str(), - event, + sender, + &content, ) .map_err(|c| self.clone().cancel(c))?; @@ -849,7 +883,7 @@ impl SasState { /// Get the content for the mac event. /// /// The content needs to be automatically sent to the other side. - pub fn as_content(&self) -> MacToDeviceEventContent { + pub fn as_content(&self) -> MacContent { get_mac_content( &self.inner.lock().unwrap(), &self.ids, @@ -911,7 +945,7 @@ impl SasState { /// /// The content needs to be automatically sent to the other side if it /// wasn't already sent. - pub fn as_content(&self) -> MacToDeviceEventContent { + pub fn as_content(&self) -> MacContent { get_mac_content( &self.inner.lock().unwrap(), &self.ids, @@ -959,17 +993,26 @@ impl Canceled { } impl SasState { - pub fn as_content(&self) -> OutgoingContent { + pub fn as_content(&self) -> CancelContent { match self.verification_flow_id.as_ref() { - FlowId::ToDevice(s) => { - AnyToDeviceEventContent::KeyVerificationCancel(CancelToDeviceEventContent { - transaction_id: self.verification_flow_id.to_string(), + FlowId::ToDevice(s) => CancelToDeviceEventContent { + transaction_id: s.clone(), + reason: self.state.reason.to_string(), + code: self.state.cancel_code.clone(), + } + .into(), + + FlowId::InRoom(r, e) => ( + r.clone(), + CancelEventContent { reason: self.state.reason.to_string(), code: self.state.cancel_code.clone(), - }) - .into() - } - _ => todo!(), + relation: Relation { + event_id: e.clone(), + }, + }, + ) + .into(), } } } From 4ad4ad1e947f2081120a10c9504eeae02f373ce0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Thu, 17 Dec 2020 15:50:13 +0100 Subject: [PATCH 11/20] crypto: Send out done events for in-room verifications. --- matrix_sdk_crypto/src/verification/machine.rs | 27 ++- .../src/verification/sas/event_enums.rs | 27 +++ .../src/verification/sas/inner_sas.rs | 54 +++++- .../src/verification/sas/sas_state.rs | 159 +++++++++++++++++- 4 files changed, 260 insertions(+), 7 deletions(-) diff --git a/matrix_sdk_crypto/src/verification/machine.rs b/matrix_sdk_crypto/src/verification/machine.rs index fc168782..b0ebffe2 100644 --- a/matrix_sdk_crypto/src/verification/machine.rs +++ b/matrix_sdk_crypto/src/verification/machine.rs @@ -222,7 +222,7 @@ impl VerificationMachine { event: &AnySyncRoomEvent, ) -> Result<(), CryptoStoreError> { if let AnySyncRoomEvent::Message(m) = event { - // Since this are room events we will get events that we send out on + // Since these are room events we will get events that we send out on // our own as well. if m.sender() == self.account.user_id() { return Ok(()); @@ -308,10 +308,25 @@ impl VerificationMachine { &s, &m.clone().into_full_event(room_id.clone()), ); + } + } + + AnySyncMessageEvent::KeyVerificationDone(e) => { + if let Some(s) = self.room_verifications.get(&e.content.relation.event_id) { + let content = + s.receive_room_event(&m.clone().into_full_event(room_id.clone())); if s.is_done() { match s.mark_as_done().await? { - VerificationResult::Ok => (), + VerificationResult::Ok => { + if let Some(c) = content { + self.queue_up_content( + s.other_user_id(), + s.other_device_id(), + c, + ); + } + } VerificationResult::Cancel(r) => { self.outgoing_to_device_messages .insert(r.request_id(), r.into()); @@ -321,6 +336,14 @@ impl VerificationMachine { self.outgoing_to_device_messages .insert(request.request_id, request); + + if let Some(c) = content { + self.queue_up_content( + s.other_user_id(), + s.other_device_id(), + c, + ); + } } } } diff --git a/matrix_sdk_crypto/src/verification/sas/event_enums.rs b/matrix_sdk_crypto/src/verification/sas/event_enums.rs index 35bf3034..7b6b2629 100644 --- a/matrix_sdk_crypto/src/verification/sas/event_enums.rs +++ b/matrix_sdk_crypto/src/verification/sas/event_enums.rs @@ -21,6 +21,7 @@ use matrix_sdk_common::{ key::verification::{ accept::{AcceptEventContent, AcceptToDeviceEventContent}, cancel::{CancelEventContent, CancelToDeviceEventContent}, + done::DoneEventContent, key::{KeyEventContent, KeyToDeviceEventContent}, mac::{MacEventContent, MacToDeviceEventContent}, start::{StartEventContent, StartMethod, StartToDeviceEventContent}, @@ -187,6 +188,24 @@ impl From for CancelContent { } } +pub enum DoneContent { + Room(RoomId, DoneEventContent), +} + +impl DoneContent { + pub fn flow_id(&self) -> FlowId { + match self { + DoneContent::Room(r, c) => FlowId::InRoom(r.clone(), c.relation.event_id.clone()), + } + } +} + +impl From<(RoomId, DoneEventContent)> for DoneContent { + fn from(content: (RoomId, DoneEventContent)) -> Self { + DoneContent::Room(content.0, content.1) + } +} + #[derive(Clone, Debug)] pub enum OutgoingContent { Room(RoomId, AnyMessageEventContent), @@ -222,6 +241,14 @@ impl From for OutgoingContent { } } +impl From for OutgoingContent { + fn from(content: DoneContent) -> Self { + match content { + DoneContent::Room(r, c) => (r, AnyMessageEventContent::KeyVerificationDone(c)).into(), + } + } +} + impl From for OutgoingContent { fn from(content: AnyToDeviceEventContent) -> Self { OutgoingContent::ToDevice(content) diff --git a/matrix_sdk_crypto/src/verification/sas/inner_sas.rs b/matrix_sdk_crypto/src/verification/sas/inner_sas.rs index ffebb8bc..f10388b0 100644 --- a/matrix_sdk_crypto/src/verification/sas/inner_sas.rs +++ b/matrix_sdk_crypto/src/verification/sas/inner_sas.rs @@ -30,6 +30,7 @@ use matrix_sdk_common::{ }, identifiers::{EventId, RoomId, UserId}, }; +use tracing::trace; use crate::{ identities::{ReadOnlyDevice, UserIdentities}, @@ -40,7 +41,7 @@ use super::{ event_enums::{AcceptContent, CancelContent, MacContent, OutgoingContent}, sas_state::{ Accepted, Canceled, Confirmed, Created, Done, FlowId, KeyReceived, MacReceived, SasState, - Started, + Started, WaitingForDone, }, StartContent, }; @@ -53,6 +54,8 @@ pub enum InnerSas { KeyRecieved(SasState), Confirmed(SasState), MacReceived(SasState), + WaitingForDone(SasState), + WaitingForDoneUnconfirmed(SasState), Done(SasState), Canceled(SasState), } @@ -125,6 +128,8 @@ impl InnerSas { InnerSas::Confirmed(s) => s.set_creation_time(time), InnerSas::MacReceived(s) => s.set_creation_time(time), InnerSas::Done(s) => s.set_creation_time(time), + InnerSas::WaitingForDone(s) => s.set_creation_time(time), + InnerSas::WaitingForDoneUnconfirmed(s) => s.set_creation_time(time), } } @@ -151,9 +156,17 @@ impl InnerSas { (InnerSas::Confirmed(sas), Some(content)) } InnerSas::MacReceived(s) => { - let sas = s.confirm(); - let content = sas.as_content(); - (InnerSas::Done(sas), Some(content)) + if s.is_dm_verification() { + let sas = s.confirm_and_wait_for_done(); + let content = sas.as_content(); + + (InnerSas::WaitingForDoneUnconfirmed(sas), Some(content)) + } else { + let sas = s.confirm(); + let content = sas.as_content(); + + (InnerSas::Done(sas), Some(content)) + } } _ => (self, None), } @@ -201,6 +214,22 @@ impl InnerSas { } } InnerSas::Confirmed(s) => { + match s.into_waiting_for_done(&e.sender, (e.room_id.clone(), e.content.clone())) + { + Ok(s) => { + let content = s.done_content(); + (InnerSas::WaitingForDone(s), Some(content.into())) + } + Err(s) => { + let content = s.as_content(); + (InnerSas::Canceled(s), Some(content.into())) + } + } + } + _ => (self, None), + }, + AnyMessageEvent::KeyVerificationDone(e) => match self { + InnerSas::WaitingForDone(s) => { match s.into_done(&e.sender, (e.room_id.clone(), e.content.clone())) { Ok(s) => (InnerSas::Done(s), None), Err(s) => { @@ -209,6 +238,19 @@ impl InnerSas { } } } + InnerSas::WaitingForDoneUnconfirmed(s) => { + match s.into_done(&e.sender, (e.room_id.clone(), e.content.clone())) { + Ok(s) => { + let content = s.done_content(); + (InnerSas::Done(s), Some(content.into())) + } + Err(s) => { + let content = s.as_content(); + (InnerSas::Canceled(s), Some(content.into())) + } + } + } + _ => (self, None), }, _ => (self, None), @@ -300,6 +342,8 @@ impl InnerSas { InnerSas::KeyRecieved(s) => s.timed_out(), InnerSas::Confirmed(s) => s.timed_out(), InnerSas::MacReceived(s) => s.timed_out(), + InnerSas::WaitingForDone(s) => s.timed_out(), + InnerSas::WaitingForDoneUnconfirmed(s) => s.timed_out(), InnerSas::Done(s) => s.timed_out(), } } @@ -313,6 +357,8 @@ impl InnerSas { InnerSas::KeyRecieved(s) => s.verification_flow_id.clone(), InnerSas::Confirmed(s) => s.verification_flow_id.clone(), InnerSas::MacReceived(s) => s.verification_flow_id.clone(), + InnerSas::WaitingForDone(s) => s.verification_flow_id.clone(), + InnerSas::WaitingForDoneUnconfirmed(s) => s.verification_flow_id.clone(), InnerSas::Done(s) => s.verification_flow_id.clone(), } } diff --git a/matrix_sdk_crypto/src/verification/sas/sas_state.rs b/matrix_sdk_crypto/src/verification/sas/sas_state.rs index d7533c75..ed4876cb 100644 --- a/matrix_sdk_crypto/src/verification/sas/sas_state.rs +++ b/matrix_sdk_crypto/src/verification/sas/sas_state.rs @@ -29,6 +29,7 @@ use matrix_sdk_common::{ MSasV1Content as AcceptV1Content, MSasV1ContentInit as AcceptV1ContentInit, }, cancel::{CancelCode, CancelEventContent, CancelToDeviceEventContent}, + done::DoneEventContent, key::{KeyEventContent, KeyToDeviceEventContent}, mac::MacToDeviceEventContent, start::{ @@ -47,7 +48,8 @@ use tracing::error; use super::{ event_enums::{ - AcceptContent, CancelContent, KeyContent, MacContent, OutgoingContent, StartContent, + AcceptContent, CancelContent, DoneContent, KeyContent, MacContent, OutgoingContent, + StartContent, }, helpers::{ calculate_commitment, get_decimal, get_emoji, get_mac_content, receive_mac_event, SasIds, @@ -251,6 +253,15 @@ pub struct MacReceived { verified_master_keys: Arc<[UserIdentities]>, } +/// The SAS state we're going to be in after we receive a MAC event in a DM. DMs +/// require a final message `m.key.verification.done` message to conclude the +/// verificaton. This state waits for such a message. +#[derive(Clone, Debug)] +pub struct WaitingForDone { + verified_devices: Arc<[ReadOnlyDevice]>, + verified_master_keys: Arc<[UserIdentities]>, +} + /// The SAS state indicating that the verification finished successfully. /// /// We can now mark the device in our verified devices lits as verified and sign @@ -300,6 +311,11 @@ impl SasState { self.creation_time.elapsed() > MAX_AGE || self.last_event_time.elapsed() > MAX_EVENT_TIMEOUT } + /// Is this verification happening inside a DM. + pub fn is_dm_verification(&self) -> bool { + matches!(&*self.verification_flow_id, FlowId::InRoom(_, _)) + } + #[cfg(test)] #[allow(dead_code)] pub fn set_creation_time(&mut self, time: Instant) { @@ -880,6 +896,48 @@ impl SasState { }) } + /// Receive a m.key.verification.mac event, changing the state into + /// a `WaitingForDone` one. This method should be used instead of + /// `into_done()` if the verification started with a + /// `m.key.verification.request`. + /// + /// # Arguments + /// + /// * `event` - The m.key.verification.mac event that was sent to us by + /// the other side. + pub fn into_waiting_for_done( + self, + sender: &UserId, + content: impl Into, + ) -> Result, SasState> { + let content = content.into(); + + self.check_event(&sender, &content.flow_id().as_str()) + .map_err(|c| self.clone().cancel(c))?; + + let (devices, master_keys) = receive_mac_event( + &self.inner.lock().unwrap(), + &self.ids, + &self.verification_flow_id.as_str(), + sender, + &content, + ) + .map_err(|c| self.clone().cancel(c))?; + + Ok(SasState { + inner: self.inner, + creation_time: self.creation_time, + last_event_time: self.last_event_time, + verification_flow_id: self.verification_flow_id, + ids: self.ids, + + state: Arc::new(WaitingForDone { + verified_devices: devices.into(), + verified_master_keys: master_keys.into(), + }), + }) + } + /// Get the content for the mac event. /// /// The content needs to be automatically sent to the other side. @@ -911,6 +969,26 @@ impl SasState { } } + /// Confirm that the short auth string matches but wait for the other side + /// to confirm that it's done. + /// + /// This needs to be done by the user, this will put us in the `WaitForDone` + /// state where we wait for the other side to confirm that the MAC event was + /// successfully received. + pub fn confirm_and_wait_for_done(self) -> SasState { + SasState { + inner: self.inner, + verification_flow_id: self.verification_flow_id, + creation_time: self.creation_time, + last_event_time: self.last_event_time, + ids: self.ids, + state: Arc::new(WaitingForDone { + verified_devices: self.state.verified_devices.clone(), + verified_master_keys: self.state.verified_master_keys.clone(), + }), + } + } + /// Get the emoji version of the short authentication string. /// /// Returns a vector of tuples where the first element is the emoji and the @@ -940,6 +1018,68 @@ impl SasState { } } +impl SasState { + /// Get the content for the mac event. + /// + /// The content needs to be automatically sent to the other side if it + /// wasn't already sent. + pub fn as_content(&self) -> MacContent { + get_mac_content( + &self.inner.lock().unwrap(), + &self.ids, + &self.verification_flow_id, + ) + } + + pub fn done_content(&self) -> DoneContent { + match self.verification_flow_id.as_ref() { + FlowId::ToDevice(_) => { + unreachable!("The done content isn't supported yet for to-device verifications") + } + FlowId::InRoom(r, e) => ( + r.clone(), + DoneEventContent { + relation: Relation { + event_id: e.clone(), + }, + }, + ) + .into(), + } + } + + /// Receive a m.key.verification.mac event, changing the state into + /// a `Done` one + /// + /// # Arguments + /// + /// * `event` - The m.key.verification.mac event that was sent to us by + /// the other side. + pub fn into_done( + self, + sender: &UserId, + content: impl Into, + ) -> Result, SasState> { + let content = content.into(); + + self.check_event(&sender, &content.flow_id().as_str()) + .map_err(|c| self.clone().cancel(c))?; + + Ok(SasState { + inner: self.inner, + creation_time: self.creation_time, + last_event_time: self.last_event_time, + verification_flow_id: self.verification_flow_id, + ids: self.ids, + + state: Arc::new(Done { + verified_devices: self.state.verified_devices.clone(), + verified_master_keys: self.state.verified_master_keys.clone(), + }), + }) + } +} + impl SasState { /// Get the content for the mac event. /// @@ -953,6 +1093,23 @@ impl SasState { ) } + pub fn done_content(&self) -> DoneContent { + match self.verification_flow_id.as_ref() { + FlowId::ToDevice(_) => { + unreachable!("The done content isn't supported yet for to-device verifications") + } + FlowId::InRoom(r, e) => ( + r.clone(), + DoneEventContent { + relation: Relation { + event_id: e.clone(), + }, + }, + ) + .into(), + } + } + /// Get the list of verified devices. pub fn verified_devices(&self) -> Arc<[ReadOnlyDevice]> { self.state.verified_devices.clone() From 48f43a4af103d3a4651d818bb59c018239039de8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Thu, 17 Dec 2020 16:28:12 +0100 Subject: [PATCH 12/20] crypto: Remove some unused imports. --- matrix_sdk_crypto/src/identities/device.rs | 2 +- matrix_sdk_crypto/src/verification/machine.rs | 7 +++---- .../src/verification/requests.rs | 14 ++++++------- .../src/verification/sas/helpers.rs | 4 +--- .../src/verification/sas/inner_sas.rs | 21 +++---------------- matrix_sdk_crypto/src/verification/sas/mod.rs | 8 ++----- .../src/verification/sas/sas_state.rs | 13 ++---------- 7 files changed, 19 insertions(+), 50 deletions(-) diff --git a/matrix_sdk_crypto/src/identities/device.rs b/matrix_sdk_crypto/src/identities/device.rs index 377a7d45..f732cc19 100644 --- a/matrix_sdk_crypto/src/identities/device.rs +++ b/matrix_sdk_crypto/src/identities/device.rs @@ -42,7 +42,7 @@ use tracing::warn; use crate::{ olm::{InboundGroupSession, PrivateCrossSigningIdentity, Session}, store::{Changes, DeviceChanges}, - OutgoingRequest, OutgoingRequests, OutgoingVerificationRequest, + OutgoingVerificationRequest, }; #[cfg(test)] use crate::{OlmMachine, ReadOnlyAccount}; diff --git a/matrix_sdk_crypto/src/verification/machine.rs b/matrix_sdk_crypto/src/verification/machine.rs index b0ebffe2..2a845bfd 100644 --- a/matrix_sdk_crypto/src/verification/machine.rs +++ b/matrix_sdk_crypto/src/verification/machine.rs @@ -21,7 +21,7 @@ use tracing::{info, trace, warn}; use matrix_sdk_common::{ events::{ room::message::MessageEventContent, AnyMessageEvent, AnySyncMessageEvent, AnySyncRoomEvent, - AnyToDeviceEvent, AnyToDeviceEventContent, + AnyToDeviceEvent, }, identifiers::{DeviceId, EventId, RoomId, UserId}, locks::Mutex, @@ -35,10 +35,9 @@ use super::{ use crate::{ olm::PrivateCrossSigningIdentity, - requests::{OutgoingRequest, ToDeviceRequest}, + requests::OutgoingRequest, store::{CryptoStore, CryptoStoreError}, - OutgoingRequests, OutgoingVerificationRequest, ReadOnlyAccount, ReadOnlyDevice, - RoomMessageRequest, + OutgoingVerificationRequest, ReadOnlyAccount, ReadOnlyDevice, RoomMessageRequest, }; #[derive(Clone, Debug)] diff --git a/matrix_sdk_crypto/src/verification/requests.rs b/matrix_sdk_crypto/src/verification/requests.rs index c7f9d950..393c9fbc 100644 --- a/matrix_sdk_crypto/src/verification/requests.rs +++ b/matrix_sdk_crypto/src/verification/requests.rs @@ -31,10 +31,10 @@ use matrix_sdk_common::{ use crate::{ olm::{PrivateCrossSigningIdentity, ReadOnlyAccount}, store::CryptoStore, - ReadOnlyDevice, Sas, UserIdentities, UserIdentity, + ReadOnlyDevice, Sas, UserIdentities, }; -use super::sas::{OutgoingContent, StartContent}; +use super::sas::OutgoingContent; const SUPPORTED_METHODS: &[VerificationMethod] = &[VerificationMethod::MSasV1]; @@ -321,11 +321,11 @@ impl RequestState { fn start_sas( self, - store: Arc>, - account: ReadOnlyAccount, - private_identity: PrivateCrossSigningIdentity, - other_device: ReadOnlyDevice, - other_identity: Option, + _store: Arc>, + _account: ReadOnlyAccount, + _private_identity: PrivateCrossSigningIdentity, + _other_device: ReadOnlyDevice, + _other_identity: Option, ) -> (Sas, OutgoingContent) { todo!() // Sas::start_in_room( diff --git a/matrix_sdk_crypto/src/verification/sas/helpers.rs b/matrix_sdk_crypto/src/verification/sas/helpers.rs index fd6f4bca..db6eec84 100644 --- a/matrix_sdk_crypto/src/verification/sas/helpers.rs +++ b/matrix_sdk_crypto/src/verification/sas/helpers.rs @@ -25,14 +25,12 @@ use matrix_sdk_common::{ key::verification::{ cancel::CancelCode, mac::{MacEventContent, MacToDeviceEventContent}, - start::StartToDeviceEventContent, Relation, }, - AnyToDeviceEventContent, EventType, ToDeviceEvent, + AnyToDeviceEventContent, EventType, }, identifiers::{DeviceId, DeviceKeyAlgorithm, DeviceKeyId, UserId}, uuid::Uuid, - CanonicalJsonValue, }; use crate::{ diff --git a/matrix_sdk_crypto/src/verification/sas/inner_sas.rs b/matrix_sdk_crypto/src/verification/sas/inner_sas.rs index f10388b0..3b4dbfdf 100644 --- a/matrix_sdk_crypto/src/verification/sas/inner_sas.rs +++ b/matrix_sdk_crypto/src/verification/sas/inner_sas.rs @@ -18,19 +18,9 @@ use std::time::Instant; use std::sync::Arc; use matrix_sdk_common::{ - events::{ - key::verification::{ - accept::AcceptToDeviceEventContent, - cancel::CancelCode, - mac::MacToDeviceEventContent, - start::{StartEventContent, StartToDeviceEventContent}, - }, - AnyMessageEvent, AnyMessageEventContent, AnySyncMessageEvent, AnyToDeviceEvent, - AnyToDeviceEventContent, MessageEvent, ToDeviceEvent, - }, + events::{key::verification::cancel::CancelCode, AnyMessageEvent, AnyToDeviceEvent}, identifiers::{EventId, RoomId, UserId}, }; -use tracing::trace; use crate::{ identities::{ReadOnlyDevice, UserIdentities}, @@ -96,13 +86,8 @@ impl InnerSas { content: impl Into, other_identity: Option, ) -> Result { - match SasState::::from_start_event( - account, - other_device, - other_identity, - &sender, - content, - ) { + match SasState::::from_start_event(account, other_device, other_identity, content) + { Ok(s) => Ok(InnerSas::Started(s)), Err(s) => Err(s.as_content()), } diff --git a/matrix_sdk_crypto/src/verification/sas/mod.rs b/matrix_sdk_crypto/src/verification/sas/mod.rs index 8ee330d4..68bf810b 100644 --- a/matrix_sdk_crypto/src/verification/sas/mod.rs +++ b/matrix_sdk_crypto/src/verification/sas/mod.rs @@ -27,12 +27,8 @@ use tracing::{error, info, trace, warn}; use matrix_sdk_common::{ api::r0::keys::upload_signatures::Request as SignatureUploadRequest, events::{ - key::verification::{ - cancel::CancelCode, - start::{StartEventContent, StartToDeviceEventContent}, - }, - AnyMessageEvent, AnyMessageEventContent, AnySyncMessageEvent, AnyToDeviceEvent, - AnyToDeviceEventContent, MessageEvent, ToDeviceEvent, + key::verification::cancel::CancelCode, AnyMessageEvent, AnyMessageEventContent, + AnyToDeviceEvent, AnyToDeviceEventContent, }, identifiers::{DeviceId, EventId, RoomId, UserId}, uuid::Uuid, diff --git a/matrix_sdk_crypto/src/verification/sas/sas_state.rs b/matrix_sdk_crypto/src/verification/sas/sas_state.rs index ed4876cb..d4cb9c8a 100644 --- a/matrix_sdk_crypto/src/verification/sas/sas_state.rs +++ b/matrix_sdk_crypto/src/verification/sas/sas_state.rs @@ -31,7 +31,6 @@ use matrix_sdk_common::{ cancel::{CancelCode, CancelEventContent, CancelToDeviceEventContent}, done::DoneEventContent, key::{KeyEventContent, KeyToDeviceEventContent}, - mac::MacToDeviceEventContent, start::{ MSasV1Content, MSasV1ContentInit, StartEventContent, StartMethod, StartToDeviceEventContent, @@ -39,7 +38,7 @@ use matrix_sdk_common::{ HashAlgorithm, KeyAgreementProtocol, MessageAuthenticationCode, Relation, ShortAuthenticationString, VerificationMethod, }, - AnyMessageEventContent, AnyToDeviceEventContent, MessageEvent, ToDeviceEvent, + ToDeviceEvent, }, identifiers::{DeviceId, EventId, RoomId, UserId}, uuid::Uuid, @@ -495,23 +494,15 @@ impl SasState { account: ReadOnlyAccount, other_device: ReadOnlyDevice, other_identity: Option, - sender: &UserId, content: impl Into, ) -> Result, SasState> { - Self::from_start_helper( - account, - other_device, - other_identity, - sender, - &content.into(), - ) + Self::from_start_helper(account, other_device, other_identity, &content.into()) } fn from_start_helper( account: ReadOnlyAccount, other_device: ReadOnlyDevice, other_identity: Option, - sender: &UserId, content: &StartContent, ) -> Result, SasState> { if let StartMethod::MSasV1(method_content) = content.method() { From f735107caf1237fd81dbce88cf08b049b4d6b0ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Thu, 17 Dec 2020 17:03:42 +0100 Subject: [PATCH 13/20] crypto: Remove an unused argument. --- matrix_sdk_crypto/src/verification/machine.rs | 1 - matrix_sdk_crypto/src/verification/requests.rs | 1 - matrix_sdk_crypto/src/verification/sas/inner_sas.rs | 3 +-- matrix_sdk_crypto/src/verification/sas/mod.rs | 2 -- 4 files changed, 1 insertion(+), 6 deletions(-) diff --git a/matrix_sdk_crypto/src/verification/machine.rs b/matrix_sdk_crypto/src/verification/machine.rs index 2a845bfd..eaf55850 100644 --- a/matrix_sdk_crypto/src/verification/machine.rs +++ b/matrix_sdk_crypto/src/verification/machine.rs @@ -377,7 +377,6 @@ impl VerificationMachine { private_identity, d, self.store.clone(), - &e.sender, e.content.clone(), self.store.get_user_identity(&e.sender).await?, ) { diff --git a/matrix_sdk_crypto/src/verification/requests.rs b/matrix_sdk_crypto/src/verification/requests.rs index 393c9fbc..d7f55446 100644 --- a/matrix_sdk_crypto/src/verification/requests.rs +++ b/matrix_sdk_crypto/src/verification/requests.rs @@ -313,7 +313,6 @@ impl RequestState { private_identity, other_device, store, - &event.sender, (event.room_id.clone(), event.content.clone()), other_identity, ) diff --git a/matrix_sdk_crypto/src/verification/sas/inner_sas.rs b/matrix_sdk_crypto/src/verification/sas/inner_sas.rs index 3b4dbfdf..6a8872b1 100644 --- a/matrix_sdk_crypto/src/verification/sas/inner_sas.rs +++ b/matrix_sdk_crypto/src/verification/sas/inner_sas.rs @@ -19,7 +19,7 @@ use std::sync::Arc; use matrix_sdk_common::{ events::{key::verification::cancel::CancelCode, AnyMessageEvent, AnyToDeviceEvent}, - identifiers::{EventId, RoomId, UserId}, + identifiers::{EventId, RoomId}, }; use crate::{ @@ -82,7 +82,6 @@ impl InnerSas { pub fn from_start_event( account: ReadOnlyAccount, other_device: ReadOnlyDevice, - sender: &UserId, content: impl Into, other_identity: Option, ) -> Result { diff --git a/matrix_sdk_crypto/src/verification/sas/mod.rs b/matrix_sdk_crypto/src/verification/sas/mod.rs index 68bf810b..9e5a7de2 100644 --- a/matrix_sdk_crypto/src/verification/sas/mod.rs +++ b/matrix_sdk_crypto/src/verification/sas/mod.rs @@ -223,14 +223,12 @@ impl Sas { private_identity: PrivateCrossSigningIdentity, other_device: ReadOnlyDevice, store: Arc>, - sender: &UserId, content: impl Into, other_identity: Option, ) -> Result { let inner = InnerSas::from_start_event( account.clone(), other_device.clone(), - &sender, content, other_identity.clone(), )?; From 897c6abe92fab60d68f36e236b60238d7fb62a00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Fri, 18 Dec 2020 12:55:06 +0100 Subject: [PATCH 14/20] crypto: Fix our tests now that we support in-room verifications. --- matrix_sdk_crypto/src/machine.rs | 2 +- matrix_sdk_crypto/src/verification/machine.rs | 11 +- matrix_sdk_crypto/src/verification/mod.rs | 33 ++- .../src/verification/sas/event_enums.rs | 18 +- .../src/verification/sas/helpers.rs | 2 +- .../src/verification/sas/inner_sas.rs | 6 +- matrix_sdk_crypto/src/verification/sas/mod.rs | 149 +++---------- .../src/verification/sas/sas_state.rs | 195 +++++++++--------- 8 files changed, 185 insertions(+), 231 deletions(-) diff --git a/matrix_sdk_crypto/src/machine.rs b/matrix_sdk_crypto/src/machine.rs index 585962a2..fcb00c86 100644 --- a/matrix_sdk_crypto/src/machine.rs +++ b/matrix_sdk_crypto/src/machine.rs @@ -1826,7 +1826,7 @@ pub(crate) mod test { let (alice_sas, request) = bob_device.start_verification().await.unwrap(); - let mut event = request_to_event(alice.user_id(), &request); + let mut event = request_to_event(alice.user_id(), &request.into()); bob.handle_verification_event(&mut event).await; let bob_sas = bob.get_verification(alice_sas.flow_id().as_str()).unwrap(); diff --git a/matrix_sdk_crypto/src/verification/machine.rs b/matrix_sdk_crypto/src/verification/machine.rs index eaf55850..64b52ab7 100644 --- a/matrix_sdk_crypto/src/verification/machine.rs +++ b/matrix_sdk_crypto/src/verification/machine.rs @@ -85,7 +85,7 @@ impl VerificationMachine { identity, ); - let request = match content { + let request = match content.into() { OutgoingContent::Room(r, c) => RoomMessageRequest { room_id: r, txn_id: Uuid::new_v4(), @@ -459,7 +459,6 @@ mod test { }; use matrix_sdk_common::{ - events::AnyToDeviceEventContent, identifiers::{DeviceId, UserId}, locks::Mutex, }; @@ -511,10 +510,11 @@ mod test { bob_store, None, ); + machine .receive_event(&mut wrap_any_to_device_content( bob_sas.user_id(), - AnyToDeviceEventContent::KeyVerificationStart(start_content), + start_content.into(), )) .await .unwrap(); @@ -559,12 +559,13 @@ mod test { let txn_id = *request.request_id(); let r = if let OutgoingRequests::ToDeviceRequest(r) = request.request() { - r + r.clone() } else { panic!("Invalid request type"); }; - let mut event = wrap_any_to_device_content(alice.user_id(), get_content_from_request(r)); + let mut event = + wrap_any_to_device_content(alice.user_id(), get_content_from_request(&r.into())); drop(request); alice_machine.mark_request_as_sent(&txn_id); diff --git a/matrix_sdk_crypto/src/verification/mod.rs b/matrix_sdk_crypto/src/verification/mod.rs index 0e170a35..eeed8de5 100644 --- a/matrix_sdk_crypto/src/verification/mod.rs +++ b/matrix_sdk_crypto/src/verification/mod.rs @@ -22,7 +22,10 @@ pub use sas::{Sas, VerificationResult}; #[cfg(test)] pub(crate) mod test { - use crate::requests::{OutgoingRequest, OutgoingRequests, ToDeviceRequest}; + use crate::{ + requests::{OutgoingRequest, OutgoingRequests}, + OutgoingVerificationRequest, + }; use serde_json::Value; use matrix_sdk_common::{ @@ -30,7 +33,12 @@ pub(crate) mod test { identifiers::UserId, }; - pub(crate) fn request_to_event(sender: &UserId, request: &ToDeviceRequest) -> AnyToDeviceEvent { + use super::sas::OutgoingContent; + + pub(crate) fn request_to_event( + sender: &UserId, + request: &OutgoingVerificationRequest, + ) -> AnyToDeviceEvent { let content = get_content_from_request(request); wrap_any_to_device_content(sender, content) } @@ -40,15 +48,21 @@ pub(crate) mod test { request: &OutgoingRequest, ) -> AnyToDeviceEvent { match request.request() { - OutgoingRequests::ToDeviceRequest(r) => request_to_event(sender, r), + OutgoingRequests::ToDeviceRequest(r) => request_to_event(sender, &r.clone().into()), _ => panic!("Unsupported outgoing request"), } } pub(crate) fn wrap_any_to_device_content( sender: &UserId, - content: AnyToDeviceEventContent, + content: OutgoingContent, ) -> AnyToDeviceEvent { + let content = if let OutgoingContent::ToDevice(c) = content { + c + } else { + unreachable!() + }; + match content { AnyToDeviceEventContent::KeyVerificationKey(c) => { AnyToDeviceEvent::KeyVerificationKey(ToDeviceEvent { @@ -79,7 +93,15 @@ pub(crate) mod test { } } - pub(crate) fn get_content_from_request(request: &ToDeviceRequest) -> AnyToDeviceEventContent { + pub(crate) fn get_content_from_request( + request: &OutgoingVerificationRequest, + ) -> OutgoingContent { + let request = if let OutgoingVerificationRequest::ToDevice(r) = request { + r + } else { + unreachable!() + }; + let json: Value = serde_json::from_str( request .messages @@ -111,5 +133,6 @@ pub(crate) mod test { ), _ => unreachable!(), } + .into() } } diff --git a/matrix_sdk_crypto/src/verification/sas/event_enums.rs b/matrix_sdk_crypto/src/verification/sas/event_enums.rs index 7b6b2629..c21b0e8b 100644 --- a/matrix_sdk_crypto/src/verification/sas/event_enums.rs +++ b/matrix_sdk_crypto/src/verification/sas/event_enums.rs @@ -19,7 +19,7 @@ use std::{collections::BTreeMap, convert::TryInto}; use matrix_sdk_common::{ events::{ key::verification::{ - accept::{AcceptEventContent, AcceptToDeviceEventContent}, + accept::{AcceptEventContent, AcceptMethod, AcceptToDeviceEventContent}, cancel::{CancelEventContent, CancelToDeviceEventContent}, done::DoneEventContent, key::{KeyEventContent, KeyToDeviceEventContent}, @@ -86,6 +86,22 @@ pub enum AcceptContent { Room(RoomId, AcceptEventContent), } +impl AcceptContent { + pub fn flow_id(&self) -> FlowId { + match self { + AcceptContent::ToDevice(c) => FlowId::ToDevice(c.transaction_id.clone()), + AcceptContent::Room(r, c) => FlowId::InRoom(r.clone(), c.relation.event_id.clone()), + } + } + + pub fn method(&self) -> &AcceptMethod { + match self { + AcceptContent::ToDevice(c) => &c.method, + AcceptContent::Room(_, c) => &c.method, + } + } +} + impl From for AcceptContent { fn from(content: AcceptToDeviceEventContent) -> Self { AcceptContent::ToDevice(content) diff --git a/matrix_sdk_crypto/src/verification/sas/helpers.rs b/matrix_sdk_crypto/src/verification/sas/helpers.rs index db6eec84..587982f6 100644 --- a/matrix_sdk_crypto/src/verification/sas/helpers.rs +++ b/matrix_sdk_crypto/src/verification/sas/helpers.rs @@ -570,7 +570,7 @@ mod test { }); let content: StartToDeviceEventContent = serde_json::from_value(content).unwrap(); - let calculated_commitment = calculate_commitment(public_key, &content); + let calculated_commitment = calculate_commitment(public_key, content); assert_eq!(commitment, &calculated_commitment); } diff --git a/matrix_sdk_crypto/src/verification/sas/inner_sas.rs b/matrix_sdk_crypto/src/verification/sas/inner_sas.rs index 6a8872b1..3f8e08ad 100644 --- a/matrix_sdk_crypto/src/verification/sas/inner_sas.rs +++ b/matrix_sdk_crypto/src/verification/sas/inner_sas.rs @@ -55,7 +55,7 @@ impl InnerSas { account: ReadOnlyAccount, other_device: ReadOnlyDevice, other_identity: Option, - ) -> (InnerSas, OutgoingContent) { + ) -> (InnerSas, StartContent) { let sas = SasState::::new(account, other_device, other_identity); let content = sas.as_content(); (InnerSas::Created(sas), content) @@ -67,7 +67,7 @@ impl InnerSas { account: ReadOnlyAccount, other_device: ReadOnlyDevice, other_identity: Option, - ) -> (InnerSas, OutgoingContent) { + ) -> (InnerSas, StartContent) { let sas = SasState::::new_in_room( room_id, event_id, @@ -245,7 +245,7 @@ impl InnerSas { match event { AnyToDeviceEvent::KeyVerificationAccept(e) => { if let InnerSas::Created(s) = self { - match s.into_accepted(e) { + match s.into_accepted(&e.sender, e.content.clone()) { Ok(s) => { let content = s.as_content(); (InnerSas::Accepted(s), Some(content.into())) diff --git a/matrix_sdk_crypto/src/verification/sas/mod.rs b/matrix_sdk_crypto/src/verification/sas/mod.rs index 9e5a7de2..de23dc64 100644 --- a/matrix_sdk_crypto/src/verification/sas/mod.rs +++ b/matrix_sdk_crypto/src/verification/sas/mod.rs @@ -113,16 +113,15 @@ impl Sas { fn start_helper( inner_sas: InnerSas, - content: OutgoingContent, account: ReadOnlyAccount, private_identity: PrivateCrossSigningIdentity, other_device: ReadOnlyDevice, store: Arc>, other_identity: Option, - ) -> (Sas, OutgoingContent) { + ) -> Sas { let flow_id = inner_sas.verification_flow_id(); - let sas = Sas { + Sas { inner: Arc::new(Mutex::new(inner_sas)), account, private_identity, @@ -130,9 +129,7 @@ impl Sas { other_device, flow_id, other_identity, - }; - - (sas, content) + } } /// Start a new SAS auth flow with the given device. @@ -151,21 +148,23 @@ impl Sas { other_device: ReadOnlyDevice, store: Arc>, other_identity: Option, - ) -> (Sas, OutgoingContent) { + ) -> (Sas, StartContent) { let (inner, content) = InnerSas::start( account.clone(), other_device.clone(), other_identity.clone(), ); - Self::start_helper( - inner, + ( + Self::start_helper( + inner, + account, + private_identity, + other_device, + store, + other_identity, + ), content, - account, - private_identity, - other_device, - store, - other_identity, ) } @@ -188,7 +187,7 @@ impl Sas { other_device: ReadOnlyDevice, store: Arc>, other_identity: Option, - ) -> (Sas, OutgoingContent) { + ) -> (Sas, StartContent) { let (inner, content) = InnerSas::start_in_room( flow_id, room_id, @@ -197,14 +196,16 @@ impl Sas { other_identity.clone(), ); - Self::start_helper( - inner, + ( + Self::start_helper( + inner, + account, + private_identity, + other_device, + store, + other_identity, + ), content, - account, - private_identity, - other_device, - store, - other_identity, ) } @@ -656,10 +657,7 @@ impl Sas { mod test { use std::{convert::TryFrom, sync::Arc}; - use matrix_sdk_common::{ - events::{EventContent, ToDeviceEvent}, - identifiers::{DeviceId, UserId}, - }; + use matrix_sdk_common::identifiers::{DeviceId, UserId}; use crate::{ olm::PrivateCrossSigningIdentity, @@ -668,10 +666,7 @@ mod test { ReadOnlyAccount, ReadOnlyDevice, }; - use super::{ - sas_state::{Accepted, Created, SasState, Started}, - Sas, - }; + use super::Sas; fn alice_id() -> UserId { UserId::try_from("@alice:example.org").unwrap() @@ -689,97 +684,6 @@ mod test { "BOBDEVCIE".into() } - fn wrap_to_device_event(sender: &UserId, content: C) -> ToDeviceEvent { - ToDeviceEvent { - sender: sender.clone(), - content, - } - } - - async fn get_sas_pair() -> (SasState, SasState) { - let alice = ReadOnlyAccount::new(&alice_id(), &alice_device_id()); - let alice_device = ReadOnlyDevice::from_account(&alice).await; - - let bob = ReadOnlyAccount::new(&bob_id(), &bob_device_id()); - let bob_device = ReadOnlyDevice::from_account(&bob).await; - - let alice_sas = SasState::::new(alice.clone(), bob_device, None); - - let start_content = alice_sas.as_content(); - let event = wrap_to_device_event(alice_sas.user_id(), start_content); - - let bob_sas = - SasState::::from_start_event(bob.clone(), alice_device, &event, None); - - (alice_sas, bob_sas.unwrap()) - } - - #[tokio::test] - async fn create_sas() { - let (_, _) = get_sas_pair().await; - } - - #[tokio::test] - async fn sas_accept() { - let (alice, bob) = get_sas_pair().await; - - let event = wrap_to_device_event(bob.user_id(), bob.as_content()); - - alice.into_accepted(&event).unwrap(); - } - - #[tokio::test] - async fn sas_key_share() { - let (alice, bob) = get_sas_pair().await; - - let event = wrap_to_device_event(bob.user_id(), bob.as_content()); - - let alice: SasState = alice.into_accepted(&event).unwrap(); - let mut event = wrap_to_device_event(alice.user_id(), alice.as_content()); - - let bob = bob.into_key_received(&mut event).unwrap(); - - let mut event = wrap_to_device_event(bob.user_id(), bob.as_content()); - - let alice = alice.into_key_received(&mut event).unwrap(); - - assert_eq!(alice.get_decimal(), bob.get_decimal()); - assert_eq!(alice.get_emoji(), bob.get_emoji()); - } - - #[tokio::test] - async fn sas_full() { - let (alice, bob) = get_sas_pair().await; - - let event = wrap_to_device_event(bob.user_id(), bob.as_content()); - - let alice: SasState = alice.into_accepted(&event).unwrap(); - let mut event = wrap_to_device_event(alice.user_id(), alice.as_content()); - - let bob = bob.into_key_received(&mut event).unwrap(); - - let mut event = wrap_to_device_event(bob.user_id(), bob.as_content()); - - let alice = alice.into_key_received(&mut event).unwrap(); - - assert_eq!(alice.get_decimal(), bob.get_decimal()); - assert_eq!(alice.get_emoji(), bob.get_emoji()); - - let bob = bob.confirm(); - - let event = wrap_to_device_event(bob.user_id(), bob.as_content()); - - let alice = alice.into_mac_received(&event).unwrap(); - assert!(!alice.get_emoji().is_empty()); - let alice = alice.confirm(); - - let event = wrap_to_device_event(alice.user_id(), alice.as_content()); - let bob = bob.into_done(&event).unwrap(); - - assert!(bob.verified_devices().contains(&bob.other_device())); - assert!(alice.verified_devices().contains(&alice.other_device())); - } - #[tokio::test] async fn sas_wrapper_full() { let alice = ReadOnlyAccount::new(&alice_id(), &alice_device_id()); @@ -802,14 +706,13 @@ mod test { alice_store, None, ); - let event = wrap_to_device_event(alice.user_id(), content); let bob = Sas::from_start_event( bob, PrivateCrossSigningIdentity::empty(bob_id()), alice_device, bob_store, - &event, + content, None, ) .unwrap(); diff --git a/matrix_sdk_crypto/src/verification/sas/sas_state.rs b/matrix_sdk_crypto/src/verification/sas/sas_state.rs index d4cb9c8a..d3eec749 100644 --- a/matrix_sdk_crypto/src/verification/sas/sas_state.rs +++ b/matrix_sdk_crypto/src/verification/sas/sas_state.rs @@ -22,23 +22,20 @@ use std::{ use olm_rs::sas::OlmSas; use matrix_sdk_common::{ - events::{ - key::verification::{ - accept::{ - AcceptEventContent, AcceptMethod, AcceptToDeviceEventContent, - MSasV1Content as AcceptV1Content, MSasV1ContentInit as AcceptV1ContentInit, - }, - cancel::{CancelCode, CancelEventContent, CancelToDeviceEventContent}, - done::DoneEventContent, - key::{KeyEventContent, KeyToDeviceEventContent}, - start::{ - MSasV1Content, MSasV1ContentInit, StartEventContent, StartMethod, - StartToDeviceEventContent, - }, - HashAlgorithm, KeyAgreementProtocol, MessageAuthenticationCode, Relation, - ShortAuthenticationString, VerificationMethod, + events::key::verification::{ + accept::{ + AcceptEventContent, AcceptMethod, AcceptToDeviceEventContent, + MSasV1Content as AcceptV1Content, MSasV1ContentInit as AcceptV1ContentInit, }, - ToDeviceEvent, + cancel::{CancelCode, CancelEventContent, CancelToDeviceEventContent}, + done::DoneEventContent, + key::{KeyEventContent, KeyToDeviceEventContent}, + start::{ + MSasV1Content, MSasV1ContentInit, StartEventContent, StartMethod, + StartToDeviceEventContent, + }, + HashAlgorithm, KeyAgreementProtocol, MessageAuthenticationCode, Relation, + ShortAuthenticationString, VerificationMethod, }, identifiers::{DeviceId, EventId, RoomId, UserId}, uuid::Uuid, @@ -47,8 +44,7 @@ use tracing::error; use super::{ event_enums::{ - AcceptContent, CancelContent, DoneContent, KeyContent, MacContent, OutgoingContent, - StartContent, + AcceptContent, CancelContent, DoneContent, KeyContent, MacContent, StartContent, }, helpers::{ calculate_commitment, get_decimal, get_emoji, get_mac_content, receive_mac_event, SasIds, @@ -405,7 +401,7 @@ impl SasState { } } - pub fn as_start_content(&self) -> StartContent { + pub fn as_content(&self) -> StartContent { match self.verification_flow_id.as_ref() { FlowId::ToDevice(_) => StartContent::ToDevice(StartToDeviceEventContent { transaction_id: self.verification_flow_id.to_string(), @@ -431,13 +427,6 @@ impl SasState { } } - /// Get the content for the start event. - /// - /// The content needs to be sent to the other device. - pub fn as_content(&self) -> OutgoingContent { - self.as_start_content().into() - } - /// Receive a m.key.verification.accept event, changing the state into /// an Accepted one. /// @@ -447,16 +436,18 @@ impl SasState { /// the other side. pub fn into_accepted( self, - event: &ToDeviceEvent, + sender: &UserId, + content: impl Into, ) -> Result, SasState> { - self.check_event(&event.sender, &event.content.transaction_id) + let content = content.into(); + self.check_event(&sender, content.flow_id().as_str()) .map_err(|c| self.clone().cancel(c))?; - if let AcceptMethod::MSasV1(content) = &event.content.method { + if let AcceptMethod::MSasV1(content) = content.method() { let accepted_protocols = AcceptedProtocols::try_from(content.clone()).map_err(|c| self.clone().cancel(c))?; - let start_content = self.as_start_content().into(); + let start_content = self.as_content().into(); Ok(SasState { inner: self.inner, @@ -713,14 +704,14 @@ impl SasState { /// Get the content for the key event. /// /// The content needs to be automatically sent to the other side. - pub fn as_content(&self) -> OutgoingContent { + pub fn as_content(&self) -> KeyContent { match &*self.verification_flow_id { - FlowId::ToDevice(s) => KeyContent::ToDevice(KeyToDeviceEventContent { + FlowId::ToDevice(s) => KeyToDeviceEventContent { transaction_id: s.to_string(), key: self.inner.lock().unwrap().public_key(), - }) + } .into(), - FlowId::InRoom(r, e) => KeyContent::Room( + FlowId::InRoom(r, e) => ( r.clone(), KeyEventContent { key: self.inner.lock().unwrap().public_key(), @@ -729,7 +720,7 @@ impl SasState { }, }, ) - .into(), + .into(), } } } @@ -1169,14 +1160,14 @@ impl SasState { mod test { use std::convert::TryFrom; - use crate::{ReadOnlyAccount, ReadOnlyDevice}; + use crate::{ + verification::sas::{event_enums::AcceptContent, StartContent}, + ReadOnlyAccount, ReadOnlyDevice, + }; use matrix_sdk_common::{ - events::{ - key::verification::{ - accept::{AcceptMethod, CustomContent}, - start::{CustomContent as CustomStartContent, StartMethod}, - }, - EventContent, ToDeviceEvent, + events::key::verification::{ + accept::{AcceptMethod, CustomContent}, + start::{CustomContent as CustomStartContent, StartMethod}, }, identifiers::{DeviceId, UserId}, }; @@ -1199,13 +1190,6 @@ mod test { "BOBDEVCIE".into() } - fn wrap_to_device_event(sender: &UserId, content: C) -> ToDeviceEvent { - ToDeviceEvent { - sender: sender.clone(), - content, - } - } - async fn get_sas_pair() -> (SasState, SasState) { let alice = ReadOnlyAccount::new(&alice_id(), &alice_device_id()); let alice_device = ReadOnlyDevice::from_account(&alice).await; @@ -1216,10 +1200,9 @@ mod test { let alice_sas = SasState::::new(alice.clone(), bob_device, None); let start_content = alice_sas.as_content(); - let event = wrap_to_device_event(alice_sas.user_id(), start_content); let bob_sas = - SasState::::from_start_event(bob.clone(), alice_device, &event, None); + SasState::::from_start_event(bob.clone(), alice_device, None, start_content); (alice_sas, bob_sas.unwrap()) } @@ -1233,25 +1216,25 @@ mod test { async fn sas_accept() { let (alice, bob) = get_sas_pair().await; - let event = wrap_to_device_event(bob.user_id(), bob.as_content()); + let event = bob.as_content(); - alice.into_accepted(&event).unwrap(); + alice.into_accepted(bob.user_id(), event).unwrap(); } #[tokio::test] async fn sas_key_share() { let (alice, bob) = get_sas_pair().await; - let event = wrap_to_device_event(bob.user_id(), bob.as_content()); + let content = bob.as_content(); - let alice: SasState = alice.into_accepted(&event).unwrap(); - let mut event = wrap_to_device_event(alice.user_id(), alice.as_content()); + let alice: SasState = alice.into_accepted(bob.user_id(), content).unwrap(); + let content = alice.as_content(); - let bob = bob.into_key_received(&mut event).unwrap(); + let bob = bob.into_key_received(alice.user_id(), content).unwrap(); - let mut event = wrap_to_device_event(bob.user_id(), bob.as_content()); + let content = bob.as_content(); - let alice = alice.into_key_received(&mut event).unwrap(); + let alice = alice.into_key_received(bob.user_id(), content).unwrap(); assert_eq!(alice.get_decimal(), bob.get_decimal()); assert_eq!(alice.get_emoji(), bob.get_emoji()); @@ -1261,16 +1244,16 @@ mod test { async fn sas_full() { let (alice, bob) = get_sas_pair().await; - let event = wrap_to_device_event(bob.user_id(), bob.as_content()); + let content = bob.as_content(); - let alice: SasState = alice.into_accepted(&event).unwrap(); - let mut event = wrap_to_device_event(alice.user_id(), alice.as_content()); + let alice: SasState = alice.into_accepted(bob.user_id(), content).unwrap(); + let content = alice.as_content(); - let bob = bob.into_key_received(&mut event).unwrap(); + let bob = bob.into_key_received(alice.user_id(), content).unwrap(); - let mut event = wrap_to_device_event(bob.user_id(), bob.as_content()); + let content = bob.as_content(); - let alice = alice.into_key_received(&mut event).unwrap(); + let alice = alice.into_key_received(bob.user_id(), content).unwrap(); assert_eq!(alice.get_decimal(), bob.get_decimal()); assert_eq!(alice.get_emoji(), bob.get_emoji()); @@ -1279,15 +1262,15 @@ mod test { let bob = bob.confirm(); - let event = wrap_to_device_event(bob.user_id(), bob.as_content()); + let content = bob.as_content(); - let alice = alice.into_mac_received(&event).unwrap(); + let alice = alice.into_mac_received(bob.user_id(), content).unwrap(); assert!(!alice.get_emoji().is_empty()); assert_eq!(alice.get_decimal(), bob_decimals); let alice = alice.confirm(); - let event = wrap_to_device_event(alice.user_id(), alice.as_content()); - let bob = bob.into_done(&event).unwrap(); + let content = alice.as_content(); + let bob = bob.into_done(alice.user_id(), content).unwrap(); assert!(bob.verified_devices().contains(&bob.other_device())); assert!(alice.verified_devices().contains(&alice.other_device())); @@ -1297,23 +1280,28 @@ mod test { async fn sas_invalid_commitment() { let (alice, bob) = get_sas_pair().await; - let mut event = wrap_to_device_event(bob.user_id(), bob.as_content()); + let mut content = bob.as_content(); - match &mut event.content.method { + let mut method = match &mut content { + AcceptContent::ToDevice(c) => &mut c.method, + AcceptContent::Room(_, c) => &mut c.method, + }; + + match &mut method { AcceptMethod::MSasV1(ref mut c) => { c.commitment = "".to_string(); } _ => panic!("Unknown accept event content"), } - let alice: SasState = alice.into_accepted(&event).unwrap(); + let alice: SasState = alice.into_accepted(bob.user_id(), content).unwrap(); - let mut event = wrap_to_device_event(alice.user_id(), alice.as_content()); - let bob = bob.into_key_received(&mut event).unwrap(); - let mut event = wrap_to_device_event(bob.user_id(), bob.as_content()); + let content = alice.as_content(); + let bob = bob.into_key_received(alice.user_id(), content).unwrap(); + let content = bob.as_content(); alice - .into_key_received(&mut event) + .into_key_received(bob.user_id(), content) .expect_err("Didn't cancel on invalid commitment"); } @@ -1321,10 +1309,10 @@ mod test { async fn sas_invalid_sender() { let (alice, bob) = get_sas_pair().await; - let mut event = wrap_to_device_event(bob.user_id(), bob.as_content()); - event.sender = UserId::try_from("@malory:example.org").unwrap(); + let content = bob.as_content(); + let sender = UserId::try_from("@malory:example.org").unwrap(); alice - .into_accepted(&event) + .into_accepted(&sender, content) .expect_err("Didn't cancel on a invalid sender"); } @@ -1332,9 +1320,14 @@ mod test { async fn sas_unknown_sas_method() { let (alice, bob) = get_sas_pair().await; - let mut event = wrap_to_device_event(bob.user_id(), bob.as_content()); + let mut content = bob.as_content(); - match &mut event.content.method { + let mut method = match &mut content { + AcceptContent::ToDevice(c) => &mut c.method, + AcceptContent::Room(_, c) => &mut c.method, + }; + + match &mut method { AcceptMethod::MSasV1(ref mut c) => { c.short_authentication_string = vec![]; } @@ -1342,7 +1335,7 @@ mod test { } alice - .into_accepted(&event) + .into_accepted(bob.user_id(), content) .expect_err("Didn't cancel on an invalid SAS method"); } @@ -1350,15 +1343,20 @@ mod test { async fn sas_unknown_method() { let (alice, bob) = get_sas_pair().await; - let mut event = wrap_to_device_event(bob.user_id(), bob.as_content()); + let mut content = bob.as_content(); - event.content.method = AcceptMethod::Custom(CustomContent { + let method = match &mut content { + AcceptContent::ToDevice(c) => &mut c.method, + AcceptContent::Room(_, c) => &mut c.method, + }; + + *method = AcceptMethod::Custom(CustomContent { method: "m.sas.custom".to_string(), fields: vec![].into_iter().collect(), }); alice - .into_accepted(&event) + .into_accepted(bob.user_id(), content) .expect_err("Didn't cancel on an unknown SAS method"); } @@ -1374,26 +1372,39 @@ mod test { let mut start_content = alice_sas.as_content(); - match start_content.method { + let method = match &mut start_content { + StartContent::ToDevice(c) => &mut c.method, + StartContent::Room(_, c) => &mut c.method, + }; + + match method { StartMethod::MSasV1(ref mut c) => { c.message_authentication_codes = vec![]; } _ => panic!("Unknown SAS start method"), } - let event = wrap_to_device_event(alice_sas.user_id(), start_content); - SasState::::from_start_event(bob.clone(), alice_device.clone(), &event, None) - .expect_err("Didn't cancel on invalid MAC method"); + SasState::::from_start_event( + bob.clone(), + alice_device.clone(), + None, + start_content, + ) + .expect_err("Didn't cancel on invalid MAC method"); let mut start_content = alice_sas.as_content(); - start_content.method = StartMethod::Custom(CustomStartContent { + let method = match &mut start_content { + StartContent::ToDevice(c) => &mut c.method, + StartContent::Room(_, c) => &mut c.method, + }; + + *method = StartMethod::Custom(CustomStartContent { method: "m.sas.custom".to_string(), fields: vec![].into_iter().collect(), }); - let event = wrap_to_device_event(alice_sas.user_id(), start_content); - SasState::::from_start_event(bob.clone(), alice_device, &event, None) + SasState::::from_start_event(bob.clone(), alice_device, None, start_content) .expect_err("Didn't cancel on unknown sas method"); } } From 1fd8c2052e29950678e7357e15754e42e288b075 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Fri, 18 Dec 2020 13:50:02 +0100 Subject: [PATCH 15/20] crypto: Fix a bunch of clippy warnings. --- matrix_sdk/src/client.rs | 2 +- matrix_sdk_crypto/src/machine.rs | 30 +++++++++---------- matrix_sdk_crypto/src/verification/machine.rs | 26 ++++++++-------- .../src/verification/requests.rs | 19 +++++------- .../src/verification/sas/event_enums.rs | 2 +- .../src/verification/sas/helpers.rs | 2 +- matrix_sdk_crypto/src/verification/sas/mod.rs | 1 + .../src/verification/sas/sas_state.rs | 23 +++++--------- 8 files changed, 48 insertions(+), 57 deletions(-) diff --git a/matrix_sdk/src/client.rs b/matrix_sdk/src/client.rs index 114275d9..a0c3e9d2 100644 --- a/matrix_sdk/src/client.rs +++ b/matrix_sdk/src/client.rs @@ -1165,7 +1165,7 @@ impl Client { request: &RoomMessageRequest, ) -> Result { let content = request.content.clone(); - let txn_id = request.txn_id.into(); + let txn_id = request.txn_id; let room_id = &request.room_id; self.room_send(&room_id, content, Some(txn_id)).await diff --git a/matrix_sdk_crypto/src/machine.rs b/matrix_sdk_crypto/src/machine.rs index fcb00c86..2a731a5b 100644 --- a/matrix_sdk_crypto/src/machine.rs +++ b/matrix_sdk_crypto/src/machine.rs @@ -743,8 +743,8 @@ impl OlmMachine { } } - async fn handle_verification_event(&self, mut event: &mut AnyToDeviceEvent) { - if let Err(e) = self.verification_machine.receive_event(&mut event).await { + async fn handle_verification_event(&self, event: &AnyToDeviceEvent) { + if let Err(e) = self.verification_machine.receive_event(&event).await { error!("Error handling a verification event: {:?}", e); } } @@ -879,7 +879,7 @@ impl OlmMachine { | AnyToDeviceEvent::KeyVerificationMac(..) | AnyToDeviceEvent::KeyVerificationRequest(..) | AnyToDeviceEvent::KeyVerificationStart(..) => { - self.handle_verification_event(&mut event).await; + self.handle_verification_event(&event).await; } _ => continue, } @@ -1826,34 +1826,34 @@ pub(crate) mod test { let (alice_sas, request) = bob_device.start_verification().await.unwrap(); - let mut event = request_to_event(alice.user_id(), &request.into()); - bob.handle_verification_event(&mut event).await; + let event = request_to_event(alice.user_id(), &request.into()); + bob.handle_verification_event(&event).await; let bob_sas = bob.get_verification(alice_sas.flow_id().as_str()).unwrap(); assert!(alice_sas.emoji().is_none()); assert!(bob_sas.emoji().is_none()); - let mut event = bob_sas + let event = bob_sas .accept() .map(|r| request_to_event(bob.user_id(), &r)) .unwrap(); - alice.handle_verification_event(&mut event).await; + alice.handle_verification_event(&event).await; - let mut event = alice + let event = alice .outgoing_to_device_requests() .first() .map(|r| outgoing_request_to_event(alice.user_id(), r)) .unwrap(); - bob.handle_verification_event(&mut event).await; + bob.handle_verification_event(&event).await; - let mut event = bob + let event = bob .outgoing_to_device_requests() .first() .map(|r| outgoing_request_to_event(bob.user_id(), r)) .unwrap(); - alice.handle_verification_event(&mut event).await; + alice.handle_verification_event(&event).await; assert!(alice_sas.emoji().is_some()); assert!(bob_sas.emoji().is_some()); @@ -1861,19 +1861,19 @@ pub(crate) mod test { assert_eq!(alice_sas.emoji(), bob_sas.emoji()); assert_eq!(alice_sas.decimals(), bob_sas.decimals()); - let mut event = bob_sas + let event = bob_sas .confirm() .await .unwrap() .0 .map(|r| request_to_event(bob.user_id(), &r)) .unwrap(); - alice.handle_verification_event(&mut event).await; + alice.handle_verification_event(&event).await; assert!(!alice_sas.is_done()); assert!(!bob_sas.is_done()); - let mut event = alice_sas + let event = alice_sas .confirm() .await .unwrap() @@ -1891,7 +1891,7 @@ pub(crate) mod test { .unwrap(); assert!(!alice_device.is_trusted()); - bob.handle_verification_event(&mut event).await; + bob.handle_verification_event(&event).await; assert!(bob_sas.is_done()); assert!(alice_device.is_trusted()); } diff --git a/matrix_sdk_crypto/src/verification/machine.rs b/matrix_sdk_crypto/src/verification/machine.rs index 64b52ab7..b59fd64a 100644 --- a/matrix_sdk_crypto/src/verification/machine.rs +++ b/matrix_sdk_crypto/src/verification/machine.rs @@ -96,7 +96,7 @@ impl VerificationMachine { let request = content_to_request(device.user_id(), device.device_id(), c); self.verifications - .insert(sas.flow_id().to_string(), sas.clone()); + .insert(sas.flow_id().as_str().to_owned(), sas.clone()); request.into() } @@ -152,7 +152,7 @@ impl VerificationMachine { request: Arc::new( RoomMessageRequest { room_id: r, - txn_id: request_id.clone(), + txn_id: request_id, content: c, } .into(), @@ -512,7 +512,7 @@ mod test { ); machine - .receive_event(&mut wrap_any_to_device_content( + .receive_event(&wrap_any_to_device_content( bob_sas.user_id(), start_content.into(), )) @@ -536,18 +536,18 @@ mod test { let alice = alice_machine.get_sas(bob.flow_id().as_str()).unwrap(); - let mut event = alice + let event = alice .accept() .map(|c| wrap_any_to_device_content(alice.user_id(), get_content_from_request(&c))) .unwrap(); - let mut event = bob - .receive_event(&mut event) + let event = bob + .receive_event(&event) .map(|c| wrap_any_to_device_content(bob.user_id(), c)) .unwrap(); assert!(alice_machine.outgoing_to_device_messages.is_empty()); - alice_machine.receive_event(&mut event).await.unwrap(); + alice_machine.receive_event(&event).await.unwrap(); assert!(!alice_machine.outgoing_to_device_messages.is_empty()); let request = alice_machine @@ -564,29 +564,29 @@ mod test { panic!("Invalid request type"); }; - let mut event = + let event = wrap_any_to_device_content(alice.user_id(), get_content_from_request(&r.into())); drop(request); alice_machine.mark_request_as_sent(&txn_id); - assert!(bob.receive_event(&mut event).is_none()); + assert!(bob.receive_event(&event).is_none()); assert!(alice.emoji().is_some()); assert!(bob.emoji().is_some()); assert_eq!(alice.emoji(), bob.emoji()); - let mut event = wrap_any_to_device_content( + let event = wrap_any_to_device_content( alice.user_id(), get_content_from_request(&alice.confirm().await.unwrap().0.unwrap()), ); - bob.receive_event(&mut event); + bob.receive_event(&event); - let mut event = wrap_any_to_device_content( + let event = wrap_any_to_device_content( bob.user_id(), get_content_from_request(&bob.confirm().await.unwrap().0.unwrap()), ); - alice.receive_event(&mut event); + alice.receive_event(&event); assert!(alice.is_done()); assert!(bob.is_done()); diff --git a/matrix_sdk_crypto/src/verification/requests.rs b/matrix_sdk_crypto/src/verification/requests.rs index d7f55446..ae96cf90 100644 --- a/matrix_sdk_crypto/src/verification/requests.rs +++ b/matrix_sdk_crypto/src/verification/requests.rs @@ -86,13 +86,13 @@ impl VerificationRequest { } pub(crate) fn into_started_sas( - &self, + self, event: &SyncMessageEvent, device: ReadOnlyDevice, user_identity: Option, ) -> Result { match &*self.inner.lock().unwrap() { - InnerRequest::Ready(s) => s.into_started_sas( + InnerRequest::Ready(s) => s.clone().into_started_sas( &event.clone().into_full_event(self.room_id().clone()), self.store.clone(), self.account.clone(), @@ -129,7 +129,7 @@ impl InnerRequest { } fn into_started_sas( - &mut self, + self, event: &MessageEvent, store: Arc>, account: ReadOnlyAccount, @@ -193,8 +193,7 @@ impl RequestState { state: Sent { methods: SUPPORTED_METHODS.to_vec(), flow_id: response.event_id.clone(), - } - .into(), + }, } } } @@ -219,9 +218,8 @@ impl RequestState { state: Ready { methods: content.methods.to_owned(), other_device_id: content.from_device.clone(), - flow_id: self.state.flow_id.clone(), - } - .into(), + flow_id: self.state.flow_id, + }, } } } @@ -256,8 +254,7 @@ impl RequestState { methods: content.methods.clone(), flow_id: event_id.clone(), other_device_id: content.from_device.clone(), - } - .into(), + }, } } @@ -300,7 +297,7 @@ struct Ready { impl RequestState { fn into_started_sas( - &self, + self, event: &MessageEvent, store: Arc>, account: ReadOnlyAccount, diff --git a/matrix_sdk_crypto/src/verification/sas/event_enums.rs b/matrix_sdk_crypto/src/verification/sas/event_enums.rs index c21b0e8b..ce122eac 100644 --- a/matrix_sdk_crypto/src/verification/sas/event_enums.rs +++ b/matrix_sdk_crypto/src/verification/sas/event_enums.rs @@ -55,7 +55,7 @@ impl StartContent { } } - pub fn to_canonical_json(self) -> CanonicalJsonValue { + pub fn canonical_json(self) -> CanonicalJsonValue { let content = match self { StartContent::ToDevice(c) => serde_json::to_value(c), StartContent::Room(_, c) => serde_json::to_value(c), diff --git a/matrix_sdk_crypto/src/verification/sas/helpers.rs b/matrix_sdk_crypto/src/verification/sas/helpers.rs index 587982f6..76d29729 100644 --- a/matrix_sdk_crypto/src/verification/sas/helpers.rs +++ b/matrix_sdk_crypto/src/verification/sas/helpers.rs @@ -62,7 +62,7 @@ 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: impl Into) -> String { - let content = content.into().to_canonical_json(); + let content = content.into().canonical_json(); encode( Sha256::new() diff --git a/matrix_sdk_crypto/src/verification/sas/mod.rs b/matrix_sdk_crypto/src/verification/sas/mod.rs index de23dc64..ede3a46a 100644 --- a/matrix_sdk_crypto/src/verification/sas/mod.rs +++ b/matrix_sdk_crypto/src/verification/sas/mod.rs @@ -53,6 +53,7 @@ use self::event_enums::CancelContent; #[derive(Debug)] /// A result of a verification flow. +#[allow(clippy::large_enum_variant)] pub enum VerificationResult { /// The verification succeeded, nothing needs to be done. Ok, diff --git a/matrix_sdk_crypto/src/verification/sas/sas_state.rs b/matrix_sdk_crypto/src/verification/sas/sas_state.rs index d3eec749..ae65d7e5 100644 --- a/matrix_sdk_crypto/src/verification/sas/sas_state.rs +++ b/matrix_sdk_crypto/src/verification/sas/sas_state.rs @@ -86,13 +86,6 @@ impl FlowId { } } - pub fn to_string(&self) -> String { - match self { - FlowId::InRoom(_, r) => r.to_string(), - FlowId::ToDevice(t) => t.to_string(), - } - } - pub fn as_str(&self) -> &str { match self { FlowId::InRoom(_, r) => r.as_str(), @@ -403,8 +396,8 @@ impl SasState { pub fn as_content(&self) -> StartContent { match self.verification_flow_id.as_ref() { - FlowId::ToDevice(_) => StartContent::ToDevice(StartToDeviceEventContent { - transaction_id: self.verification_flow_id.to_string(), + FlowId::ToDevice(s) => StartContent::ToDevice(StartToDeviceEventContent { + transaction_id: s.to_string(), from_device: self.device_id().into(), method: StartMethod::MSasV1( MSasV1Content::new(self.state.protocol_definitions.clone()) @@ -590,8 +583,8 @@ impl SasState { ); match self.verification_flow_id.as_ref() { - FlowId::ToDevice(_) => AcceptToDeviceEventContent { - transaction_id: self.verification_flow_id.to_string(), + FlowId::ToDevice(s) => AcceptToDeviceEventContent { + transaction_id: s.to_string(), method, } .into(), @@ -732,12 +725,12 @@ impl SasState { /// if we_started is false. pub fn as_content(&self) -> KeyContent { match &*self.verification_flow_id { - FlowId::ToDevice(s) => KeyContent::ToDevice(KeyToDeviceEventContent { + FlowId::ToDevice(s) => KeyToDeviceEventContent { transaction_id: s.to_string(), key: self.inner.lock().unwrap().public_key(), - }) + } .into(), - FlowId::InRoom(r, e) => KeyContent::Room( + FlowId::InRoom(r, e) => ( r.clone(), KeyEventContent { key: self.inner.lock().unwrap().public_key(), @@ -746,7 +739,7 @@ impl SasState { }, }, ) - .into(), + .into(), } } From ec863a928d370bf9d25cce78fedad8427efcf173 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Fri, 18 Dec 2020 13:57:57 +0100 Subject: [PATCH 16/20] crypto: More clippy warnings. --- matrix_sdk_crypto/src/verification/sas/mod.rs | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/matrix_sdk_crypto/src/verification/sas/mod.rs b/matrix_sdk_crypto/src/verification/sas/mod.rs index ede3a46a..22445fa0 100644 --- a/matrix_sdk_crypto/src/verification/sas/mod.rs +++ b/matrix_sdk_crypto/src/verification/sas/mod.rs @@ -717,39 +717,38 @@ mod test { None, ) .unwrap(); - let mut event = wrap_any_to_device_content( + let event = wrap_any_to_device_content( bob.user_id(), get_content_from_request(&bob.accept().unwrap()), ); - let content = alice.receive_event(&mut event); + let content = alice.receive_event(&event); assert!(!alice.can_be_presented()); assert!(!bob.can_be_presented()); - let mut event = wrap_any_to_device_content(alice.user_id(), content.unwrap()); - let mut event = - wrap_any_to_device_content(bob.user_id(), bob.receive_event(&mut event).unwrap()); + let event = wrap_any_to_device_content(alice.user_id(), content.unwrap()); + let event = wrap_any_to_device_content(bob.user_id(), bob.receive_event(&event).unwrap()); assert!(bob.can_be_presented()); - alice.receive_event(&mut event); + alice.receive_event(&event); assert!(alice.can_be_presented()); assert_eq!(alice.emoji().unwrap(), bob.emoji().unwrap()); assert_eq!(alice.decimals().unwrap(), bob.decimals().unwrap()); - let mut event = wrap_any_to_device_content( + let event = wrap_any_to_device_content( alice.user_id(), get_content_from_request(&alice.confirm().await.unwrap().0.unwrap()), ); - bob.receive_event(&mut event); + bob.receive_event(&event); - let mut event = wrap_any_to_device_content( + let event = wrap_any_to_device_content( bob.user_id(), get_content_from_request(&bob.confirm().await.unwrap().0.unwrap()), ); - alice.receive_event(&mut event); + alice.receive_event(&event); assert!(alice .verified_devices() From 55436c65145f364081d979f43003f6dea5899ff6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Fri, 18 Dec 2020 18:23:42 +0100 Subject: [PATCH 17/20] crypto: Add a test for verification request flows. --- matrix_sdk/src/client.rs | 5 +- matrix_sdk_crypto/src/verification/machine.rs | 12 ++ .../src/verification/requests.rs | 161 ++++++++++++++++++ 3 files changed, 176 insertions(+), 2 deletions(-) diff --git a/matrix_sdk/src/client.rs b/matrix_sdk/src/client.rs index a0c3e9d2..1b63d4ac 100644 --- a/matrix_sdk/src/client.rs +++ b/matrix_sdk/src/client.rs @@ -2172,8 +2172,8 @@ impl Client { let encrypt = move || -> Result<()> { let export: String = encrypt_key_export(&keys, &passphrase, 500_000)?; - let mut file = std::fs::File::create(path).unwrap(); - file.write_all(&export.into_bytes()).unwrap(); + let mut file = std::fs::File::create(path)?; + file.write_all(&export.into_bytes())?; Ok(()) }; @@ -2237,6 +2237,7 @@ impl Client { }; let task = tokio::task::spawn_blocking(decrypt); + // TODO remove this unwrap. let import = task.await.expect("Task join error").unwrap(); Ok(olm.import_keys(import).await?) diff --git a/matrix_sdk_crypto/src/verification/machine.rs b/matrix_sdk_crypto/src/verification/machine.rs index b59fd64a..2b7e03cb 100644 --- a/matrix_sdk_crypto/src/verification/machine.rs +++ b/matrix_sdk_crypto/src/verification/machine.rs @@ -224,6 +224,10 @@ impl VerificationMachine { // Since these are room events we will get events that we send out on // our own as well. if m.sender() == self.account.user_id() { + if let AnySyncMessageEvent::KeyVerificationReady(_e) = m { + // TODO if there is a verification request, go into passive + // mode since another device is handling this request. + } return Ok(()); } @@ -250,6 +254,14 @@ impl VerificationMachine { } } } + AnySyncMessageEvent::KeyVerificationReady(e) => { + if let Some(request) = self.requests.get(&e.content.relation.event_id) { + if &e.sender == request.other_user() { + // TODO remove this unwrap. + request.receive_ready(&e.sender, &e.content).unwrap(); + } + } + } AnySyncMessageEvent::KeyVerificationStart(e) => { info!( "Received a new verification start event from {} {}", diff --git a/matrix_sdk_crypto/src/verification/requests.rs b/matrix_sdk_crypto/src/verification/requests.rs index ae96cf90..e11e244c 100644 --- a/matrix_sdk_crypto/src/verification/requests.rs +++ b/matrix_sdk_crypto/src/verification/requests.rs @@ -43,12 +43,63 @@ const SUPPORTED_METHODS: &[VerificationMethod] = &[VerificationMethod::MSasV1]; pub struct VerificationRequest { inner: Arc>, account: ReadOnlyAccount, + other_user_id: Arc, private_cross_signing_identity: PrivateCrossSigningIdentity, store: Arc>, room_id: Arc, } impl VerificationRequest { + /// TODO + pub fn new( + account: ReadOnlyAccount, + private_cross_signing_identity: PrivateCrossSigningIdentity, + store: Arc>, + room_id: Arc, + other_user: &UserId, + ) -> Self { + let inner = Mutex::new(InnerRequest::Created(RequestState::new( + account.user_id(), + account.device_id(), + other_user, + ))) + .into(); + Self { + inner, + account, + private_cross_signing_identity, + store, + other_user_id: other_user.clone().into(), + room_id, + } + } + + /// TODO + pub fn request(&self) -> Option { + match &*self.inner.lock().unwrap() { + InnerRequest::Created(c) => Some(c.as_content()), + _ => None, + } + } + + /// The id of the other user that is participating in this verification + /// request. + pub fn other_user(&self) -> &UserId { + &self.other_user_id + } + + /// Mark the request as sent. + pub fn mark_as_sent(&self, response: &RoomMessageResponse) { + let mut inner = self.inner.lock().unwrap(); + + match &*inner { + InnerRequest::Created(c) => { + *inner = InnerRequest::Sent(c.clone().into_sent(response)); + } + _ => (), + } + } + pub(crate) fn from_request_event( account: ReadOnlyAccount, private_cross_signing_identity: PrivateCrossSigningIdentity, @@ -69,6 +120,7 @@ impl VerificationRequest { ), ))), account, + other_user_id: sender.clone().into(), private_cross_signing_identity, store, room_id: room_id.clone().into(), @@ -85,6 +137,28 @@ impl VerificationRequest { self.inner.lock().unwrap().accept() } + pub(crate) fn receive_ready( + &self, + sender: &UserId, + content: &ReadyEventContent, + ) -> Result<(), ()> { + let mut inner = self.inner.lock().unwrap(); + + match &*inner { + InnerRequest::Sent(s) => { + *inner = InnerRequest::Ready(s.clone().into_ready(sender, content)); + } + _ => (), + } + + Ok(()) + } + + /// Is the verification request ready to start a verification flow. + pub fn is_ready(&self) -> bool { + matches!(&*self.inner.lock().unwrap(), InnerRequest::Ready(_)) + } + pub(crate) fn into_started_sas( self, event: &SyncMessageEvent, @@ -171,6 +245,15 @@ struct RequestState { struct Created {} impl RequestState { + fn new(own_user_id: &UserId, own_device_id: &DeviceId, other_user: &UserId) -> Self { + Self { + own_user_id: own_user_id.clone(), + own_device_id: own_device_id.into(), + other_user_id: other_user.clone(), + state: Created {}, + } + } + fn as_content(&self) -> KeyVerificationRequestEventContent { KeyVerificationRequestEventContent { body: format!( @@ -343,3 +426,81 @@ struct Passive { /// unique id identifying this verification flow. pub flow_id: EventId, } + +#[cfg(test)] +mod test { + use std::convert::TryFrom; + + use matrix_sdk_common::{ + api::r0::message::send_message_event::Response as RoomMessageResponse, + identifiers::{event_id, room_id, DeviceIdBox, UserId}, + }; + use matrix_sdk_test::async_test; + + use crate::{ + olm::{PrivateCrossSigningIdentity, ReadOnlyAccount}, + store::{CryptoStore, MemoryStore}, + }; + + use super::VerificationRequest; + + fn alice_id() -> UserId { + UserId::try_from("@alice:example.org").unwrap() + } + + fn alice_device_id() -> DeviceIdBox { + "JLAFKJWSCS".into() + } + + fn bob_id() -> UserId { + UserId::try_from("@bob:example.org").unwrap() + } + + fn bob_device_id() -> DeviceIdBox { + "BOBDEVCIE".into() + } + + #[async_test] + async fn test_request_accepting() { + let event_id = event_id!("$1234localhost"); + let room_id = room_id!("!test:localhost"); + + let alice = ReadOnlyAccount::new(&alice_id(), &alice_device_id()); + let alice_store: Box = Box::new(MemoryStore::new()); + let alice_identity = PrivateCrossSigningIdentity::empty(alice_id()); + + let bob = ReadOnlyAccount::new(&bob_id(), &bob_device_id()); + let bob_store: Box = Box::new(MemoryStore::new()); + let bob_identity = PrivateCrossSigningIdentity::empty(alice_id()); + + let bob_request = VerificationRequest::new( + bob, + bob_identity, + bob_store.into(), + room_id.clone().into(), + &alice_id(), + ); + + let content = bob_request.request().unwrap(); + + let alice_request = VerificationRequest::from_request_event( + alice, + alice_identity, + alice_store.into(), + &room_id, + &bob_id(), + &event_id, + &content, + ); + + let content = alice_request.accept().unwrap(); + + let response = RoomMessageResponse::new(event_id); + bob_request.mark_as_sent(&response); + + bob_request.receive_ready(&alice_id(), &content).unwrap(); + + assert!(bob_request.is_ready()); + assert!(alice_request.is_ready()); + } +} From b119b30939295fcc14b75ebe63d301ff6058ecd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Fri, 18 Dec 2020 19:26:51 +0100 Subject: [PATCH 18/20] crypto: Clippy warnings. --- matrix_sdk_crypto/src/verification/requests.rs | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/matrix_sdk_crypto/src/verification/requests.rs b/matrix_sdk_crypto/src/verification/requests.rs index e11e244c..6a96d9e6 100644 --- a/matrix_sdk_crypto/src/verification/requests.rs +++ b/matrix_sdk_crypto/src/verification/requests.rs @@ -92,11 +92,8 @@ impl VerificationRequest { pub fn mark_as_sent(&self, response: &RoomMessageResponse) { let mut inner = self.inner.lock().unwrap(); - match &*inner { - InnerRequest::Created(c) => { - *inner = InnerRequest::Sent(c.clone().into_sent(response)); - } - _ => (), + if let InnerRequest::Created(c) = &*inner { + *inner = InnerRequest::Sent(c.clone().into_sent(response)); } } @@ -144,11 +141,8 @@ impl VerificationRequest { ) -> Result<(), ()> { let mut inner = self.inner.lock().unwrap(); - match &*inner { - InnerRequest::Sent(s) => { - *inner = InnerRequest::Ready(s.clone().into_ready(sender, content)); - } - _ => (), + if let InnerRequest::Sent(s) = &*inner { + *inner = InnerRequest::Ready(s.clone().into_ready(sender, content)); } Ok(()) From d39e3141fcc5f3d8c46d7dcf3b83a842dc49c53d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Tue, 22 Dec 2020 14:12:57 +0100 Subject: [PATCH 19/20] crypto: Use CanonicalJsonValue for all the signature calculations. --- matrix_sdk_crypto/src/olm/account.rs | 12 +++++++----- matrix_sdk_crypto/src/olm/signing/mod.rs | 2 +- matrix_sdk_crypto/src/olm/signing/pk_signing.rs | 8 +++++--- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/matrix_sdk_crypto/src/olm/account.rs b/matrix_sdk_crypto/src/olm/account.rs index 9b6551bd..c619ffeb 100644 --- a/matrix_sdk_crypto/src/olm/account.rs +++ b/matrix_sdk_crypto/src/olm/account.rs @@ -43,7 +43,7 @@ use matrix_sdk_common::{ instant::Instant, js_int::UInt, locks::Mutex, - Raw, + CanonicalJsonValue, Raw, }; use olm_rs::{ account::{IdentityKeys, OlmAccount, OneTimeKeys}, @@ -743,7 +743,7 @@ impl ReadOnlyAccount { .or_insert_with(BTreeMap::new) .insert( DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, &self.device_id), - self.sign_json(&json_device_keys).await, + self.sign_json(json_device_keys).await, ); device_keys @@ -770,8 +770,10 @@ impl ReadOnlyAccount { /// # Panic /// /// Panics if the json value can't be serialized. - pub async fn sign_json(&self, json: &Value) -> String { - self.sign(&json.to_string()).await + pub async fn sign_json(&self, json: Value) -> String { + let canonical_json: CanonicalJsonValue = + json.try_into().expect("Can't canonicalize the json value"); + self.sign(&canonical_json.to_string()).await } pub(crate) async fn signed_one_time_keys_helper( @@ -785,7 +787,7 @@ impl ReadOnlyAccount { "key": key, }); - let signature = self.sign_json(&key_json).await; + let signature = self.sign_json(key_json).await; let mut signature_map = BTreeMap::new(); diff --git a/matrix_sdk_crypto/src/olm/signing/mod.rs b/matrix_sdk_crypto/src/olm/signing/mod.rs index 0e73f73e..1208edf1 100644 --- a/matrix_sdk_crypto/src/olm/signing/mod.rs +++ b/matrix_sdk_crypto/src/olm/signing/mod.rs @@ -214,7 +214,7 @@ impl PrivateCrossSigningIdentity { master.cross_signing_key(account.user_id().to_owned(), KeyUsage::Master); let signature = account .sign_json( - &serde_json::to_value(&public_key) + serde_json::to_value(&public_key) .expect("Can't convert own public master key to json"), ) .await; diff --git a/matrix_sdk_crypto/src/olm/signing/pk_signing.rs b/matrix_sdk_crypto/src/olm/signing/pk_signing.rs index 661c7a61..b78d11e1 100644 --- a/matrix_sdk_crypto/src/olm/signing/pk_signing.rs +++ b/matrix_sdk_crypto/src/olm/signing/pk_signing.rs @@ -23,7 +23,7 @@ use matrix_sdk_common::{ }; use serde::{Deserialize, Serialize}; use serde_json::{json, Error as JsonError, Value}; -use std::{collections::BTreeMap, sync::Arc}; +use std::{collections::BTreeMap, convert::TryInto, sync::Arc}; use thiserror::Error; use zeroize::Zeroizing; @@ -36,6 +36,7 @@ use matrix_sdk_common::{ api::r0::keys::{CrossSigningKey, KeyUsage}, identifiers::UserId, locks::Mutex, + CanonicalJsonValue, }; use crate::{ @@ -404,8 +405,9 @@ impl Signing { pub async fn sign_json(&self, mut json: Value) -> Result { let json_object = json.as_object_mut().ok_or(SignatureError::NotAnObject)?; let _ = json_object.remove("signatures"); - let canonical_json = serde_json::to_string(json_object)?; - Ok(self.sign(&canonical_json).await) + let canonical_json: CanonicalJsonValue = + json.try_into().expect("Can't canonicalize the json value"); + Ok(self.sign(&canonical_json.to_string()).await) } pub async fn sign(&self, message: &str) -> Signature { From 9245b2a89a9cd61038379a51423abe8f7fc71a82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Tue, 22 Dec 2020 15:45:42 +0100 Subject: [PATCH 20/20] crypto: Properly canonicalize the json when verifying signatures as well. --- matrix_sdk_crypto/src/olm/utility.rs | 73 ++++++++++++++++++++++++---- 1 file changed, 63 insertions(+), 10 deletions(-) diff --git a/matrix_sdk_crypto/src/olm/utility.rs b/matrix_sdk_crypto/src/olm/utility.rs index 5994a453..e398a139 100644 --- a/matrix_sdk_crypto/src/olm/utility.rs +++ b/matrix_sdk_crypto/src/olm/utility.rs @@ -14,8 +14,12 @@ use olm_rs::utility::OlmUtility; use serde_json::Value; +use std::convert::TryInto; -use matrix_sdk_common::identifiers::{DeviceKeyAlgorithm, DeviceKeyId, UserId}; +use matrix_sdk_common::{ + identifiers::{DeviceKeyAlgorithm, DeviceKeyId, UserId}, + CanonicalJsonValue, +}; use crate::error::SignatureError; @@ -63,11 +67,12 @@ impl Utility { let unsigned = json_object.remove("unsigned"); let signatures = json_object.remove("signatures"); - let canonical_json = serde_json::to_string(json_object)?; + let canonical_json: CanonicalJsonValue = json + .clone() + .try_into() + .map_err(|_| SignatureError::NotAnObject)?; - if let Some(u) = unsigned { - json_object.insert("unsigned".to_string(), u); - } + let canonical_json: String = canonical_json.to_string(); let signatures = signatures.ok_or(SignatureError::NoSignatureFound)?; let signature_object = signatures @@ -81,18 +86,66 @@ impl Utility { .ok_or(SignatureError::NoSignatureFound)?; let signature = signature.as_str().ok_or(SignatureError::NoSignatureFound)?; - let ret = if self + let ret = match self .inner .ed25519_verify(signing_key, &canonical_json, signature) - .is_ok() { - Ok(()) - } else { - Err(SignatureError::VerificationError) + Ok(_) => Ok(()), + Err(_) => Err(SignatureError::VerificationError), }; + let json_object = json.as_object_mut().ok_or(SignatureError::NotAnObject)?; + + if let Some(u) = unsigned { + json_object.insert("unsigned".to_string(), u); + } + json_object.insert("signatures".to_string(), signatures); ret } } + +#[cfg(test)] +mod test { + use super::Utility; + use matrix_sdk_common::identifiers::{user_id, DeviceKeyAlgorithm, DeviceKeyId}; + use serde_json::json; + + #[test] + fn signature_test() { + let mut device_keys = json!({ + "device_id": "GBEWHQOYGS", + "algorithms": [ + "m.olm.v1.curve25519-aes-sha2", + "m.megolm.v1.aes-sha2" + ], + "keys": { + "curve25519:GBEWHQOYGS": "F8QhZ0Z1rjtWrQOblMDgZtEX5x1UrG7sZ2Kk3xliNAU", + "ed25519:GBEWHQOYGS": "n469gw7zm+KW+JsFIJKnFVvCKU14HwQyocggcCIQgZY" + }, + "signatures": { + "@example:localhost": { + "ed25519:GBEWHQOYGS": "OlF2REsqjYdAfr04ONx8VS/5cB7KjrWYRlLF4eUm2foAiQL/RAfsjsa2JXZeoOHh6vEualZHbWlod49OewVqBg" + } + }, + "unsigned": { + "device_display_name": "Weechat-Matrix-rs" + }, + "user_id": "@example:localhost" + }); + + let signing_key = "n469gw7zm+KW+JsFIJKnFVvCKU14HwQyocggcCIQgZY"; + + let utility = Utility::new(); + + utility + .verify_json( + &user_id!("@example:localhost"), + &DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, "GBEWHQOYGS".into()), + &signing_key, + &mut device_keys, + ) + .expect("Can't verify device keys"); + } +}