crypto: Initial support for the longer to-device verification flow

master
Damir Jelić 2021-05-13 11:15:56 +02:00
parent ec55258be9
commit 09a7858702
12 changed files with 558 additions and 270 deletions

View File

@ -103,14 +103,11 @@ use matrix_sdk_common::{
}; };
#[cfg(feature = "encryption")] #[cfg(feature = "encryption")]
use matrix_sdk_common::{ use matrix_sdk_common::api::r0::{
api::r0::{ keys::{get_keys, upload_keys, upload_signing_keys::Request as UploadSigningKeysRequest},
keys::{get_keys, upload_keys, upload_signing_keys::Request as UploadSigningKeysRequest}, to_device::send_event_to_device::{
to_device::send_event_to_device::{ Request as RumaToDeviceRequest, Response as ToDeviceResponse,
Request as RumaToDeviceRequest, Response as ToDeviceResponse,
},
}, },
identifiers::EventId,
}; };
use matrix_sdk_common::locks::Mutex; use matrix_sdk_common::locks::Mutex;
@ -2131,13 +2128,17 @@ impl Client {
/// Get a `VerificationRequest` object with the given flow id. /// Get a `VerificationRequest` object with the given flow id.
#[cfg(feature = "encryption")] #[cfg(feature = "encryption")]
#[cfg_attr(feature = "docs", doc(cfg(encryption)))] #[cfg_attr(feature = "docs", doc(cfg(encryption)))]
pub async fn get_verification_request(&self, flow_id: &EventId) -> Option<VerificationRequest> { pub async fn get_verification_request(
&self,
flow_id: impl AsRef<str>,
) -> Option<VerificationRequest> {
let olm = self.base_client.olm_machine().await?; let olm = self.base_client.olm_machine().await?;
olm.get_verification_request(flow_id).and_then(|r| { olm.get_verification_request(flow_id)
self.get_joined_room(r.room_id()) .map(|r| VerificationRequest {
.map(|room| VerificationRequest { inner: r, room }) inner: r,
}) client: self.clone(),
})
} }
/// Get a specific device of a user. /// Get a specific device of a user.

View File

@ -110,6 +110,9 @@ pub use room_member::RoomMember;
#[cfg(feature = "encryption")] #[cfg(feature = "encryption")]
#[cfg_attr(feature = "docs", doc(cfg(encryption)))] #[cfg_attr(feature = "docs", doc(cfg(encryption)))]
pub use sas::Sas; pub use sas::Sas;
#[cfg(feature = "encryption")]
#[cfg_attr(feature = "docs", doc(cfg(encryption)))]
pub use verification_request::VerificationRequest;
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
pub(crate) const VERSION: &str = env!("CARGO_PKG_VERSION"); pub(crate) const VERSION: &str = env!("CARGO_PKG_VERSION");

View File

@ -12,27 +12,38 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
use matrix_sdk_base::{ use matrix_sdk_base::crypto::{
crypto::VerificationRequest as BaseVerificationRequest, events::AnyMessageEventContent, OutgoingVerificationRequest, VerificationRequest as BaseVerificationRequest,
}; };
use crate::{room::Joined, Result}; use crate::{Client, Result};
/// An object controling the interactive verification flow. /// An object controling the interactive verification flow.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct VerificationRequest { pub struct VerificationRequest {
pub(crate) inner: BaseVerificationRequest, pub(crate) inner: BaseVerificationRequest,
pub(crate) room: Joined, pub(crate) client: Client,
} }
impl VerificationRequest { impl VerificationRequest {
/// Accept the interactive verification flow. /// Accept the verification request
pub async fn accept(&self) -> Result<()> { pub async fn accept(&self) -> Result<()> {
if let Some(content) = self.inner.accept() { if let Some(request) = self.inner.accept() {
let content = AnyMessageEventContent::KeyVerificationReady(content); match request {
self.room.send(content, None).await?; OutgoingVerificationRequest::ToDevice(r) => {
self.client.send_to_device(&r).await?;
}
OutgoingVerificationRequest::InRoom(r) => {
self.client.room_send_helper(&r).await?;
}
};
} }
Ok(()) Ok(())
} }
/// Cancel the verification request
pub async fn cancel(&self) -> Result<()> {
todo!()
}
} }

View File

@ -37,8 +37,7 @@ use matrix_sdk_common::{
AnyMessageEventContent, AnyToDeviceEvent, SyncMessageEvent, ToDeviceEvent, AnyMessageEventContent, AnyToDeviceEvent, SyncMessageEvent, ToDeviceEvent,
}, },
identifiers::{ identifiers::{
DeviceId, DeviceIdBox, DeviceKeyAlgorithm, EventEncryptionAlgorithm, EventId, RoomId, DeviceId, DeviceIdBox, DeviceKeyAlgorithm, EventEncryptionAlgorithm, RoomId, UserId,
UserId,
}, },
locks::Mutex, locks::Mutex,
uuid::Uuid, uuid::Uuid,
@ -317,8 +316,7 @@ impl OlmMachine {
requests.push(request); requests.push(request);
} }
requests.append(&mut self.outgoing_to_device_requests()); requests.append(&mut self.verification_machine.outgoing_messages());
requests.append(&mut self.verification_machine.outgoing_room_message_requests());
requests.append( requests.append(
&mut self &mut self
.key_request_machine .key_request_machine
@ -747,11 +745,6 @@ impl OlmMachine {
} }
} }
/// Get the to-device requests that need to be sent out.
fn outgoing_to_device_requests(&self) -> Vec<OutgoingRequest> {
self.verification_machine.outgoing_to_device_requests()
}
/// Mark an outgoing to-device requests as sent. /// Mark an outgoing to-device requests as sent.
async fn mark_to_device_request_as_sent(&self, request_id: &Uuid) -> StoreResult<()> { async fn mark_to_device_request_as_sent(&self, request_id: &Uuid) -> StoreResult<()> {
self.verification_machine.mark_request_as_sent(request_id); self.verification_machine.mark_request_as_sent(request_id);
@ -773,7 +766,10 @@ impl OlmMachine {
} }
/// Get a verification request object with the given flow id. /// Get a verification request object with the given flow id.
pub fn get_verification_request(&self, flow_id: &EventId) -> Option<VerificationRequest> { pub fn get_verification_request(
&self,
flow_id: impl AsRef<str>,
) -> Option<VerificationRequest> {
self.verification_machine.get_request(flow_id) self.verification_machine.get_request(flow_id)
} }
@ -1967,14 +1963,16 @@ pub(crate) mod test {
alice.handle_verification_event(&event).await; alice.handle_verification_event(&event).await;
let event = alice let event = alice
.outgoing_to_device_requests() .verification_machine
.outgoing_messages()
.first() .first()
.map(|r| outgoing_request_to_event(alice.user_id(), r)) .map(|r| outgoing_request_to_event(alice.user_id(), r))
.unwrap(); .unwrap();
bob.handle_verification_event(&event).await; bob.handle_verification_event(&event).await;
let event = bob let event = bob
.outgoing_to_device_requests() .verification_machine
.outgoing_messages()
.first() .first()
.map(|r| outgoing_request_to_event(bob.user_id(), r)) .map(|r| outgoing_request_to_event(bob.user_id(), r))
.unwrap(); .unwrap();

View File

@ -47,9 +47,8 @@ pub struct VerificationMachine {
pub(crate) store: Arc<Box<dyn CryptoStore>>, pub(crate) store: Arc<Box<dyn CryptoStore>>,
verifications: Arc<DashMap<String, Sas>>, verifications: Arc<DashMap<String, Sas>>,
room_verifications: Arc<DashMap<EventId, Sas>>, room_verifications: Arc<DashMap<EventId, Sas>>,
requests: Arc<DashMap<EventId, VerificationRequest>>, requests: Arc<DashMap<String, VerificationRequest>>,
outgoing_to_device_messages: Arc<DashMap<Uuid, OutgoingRequest>>, outgoing_messages: Arc<DashMap<Uuid, OutgoingRequest>>,
outgoing_room_messages: Arc<DashMap<Uuid, OutgoingRequest>>,
} }
impl VerificationMachine { impl VerificationMachine {
@ -64,9 +63,8 @@ impl VerificationMachine {
store, store,
verifications: DashMap::new().into(), verifications: DashMap::new().into(),
requests: DashMap::new().into(), requests: DashMap::new().into(),
outgoing_to_device_messages: DashMap::new().into(),
room_verifications: DashMap::new().into(), room_verifications: DashMap::new().into(),
outgoing_room_messages: DashMap::new().into(), outgoing_messages: DashMap::new().into(),
} }
} }
@ -83,6 +81,7 @@ impl VerificationMachine {
device.clone(), device.clone(),
self.store.clone(), self.store.clone(),
identity, identity,
None,
); );
let request = match content.into() { let request = match content.into() {
@ -93,7 +92,8 @@ impl VerificationMachine {
} }
.into(), .into(),
OutgoingContent::ToDevice(c) => { OutgoingContent::ToDevice(c) => {
let request = content_to_request(device.user_id(), device.device_id(), c); let request =
content_to_request(device.user_id(), device.device_id().to_owned(), c);
self.verifications self.verifications
.insert(sas.flow_id().as_str().to_owned(), sas.clone()); .insert(sas.flow_id().as_str().to_owned(), sas.clone());
@ -105,9 +105,8 @@ impl VerificationMachine {
Ok((sas, request)) Ok((sas, request))
} }
pub fn get_request(&self, flow_id: &EventId) -> Option<VerificationRequest> { pub fn get_request(&self, flow_id: impl AsRef<str>) -> Option<VerificationRequest> {
#[allow(clippy::map_clone)] self.requests.get(flow_id.as_ref()).map(|s| s.clone())
self.requests.get(flow_id).map(|s| s.clone())
} }
pub fn get_sas(&self, transaction_id: &str) -> Option<Sas> { pub fn get_sas(&self, transaction_id: &str) -> Option<Sas> {
@ -134,7 +133,7 @@ impl VerificationMachine {
) { ) {
match content { match content {
OutgoingContent::ToDevice(c) => { OutgoingContent::ToDevice(c) => {
let request = content_to_request(recipient, recipient_device, c); let request = content_to_request(recipient, recipient_device.to_owned(), c);
let request_id = request.txn_id; let request_id = request.txn_id;
let request = OutgoingRequest { let request = OutgoingRequest {
@ -142,7 +141,7 @@ impl VerificationMachine {
request: Arc::new(request.into()), request: Arc::new(request.into()),
}; };
self.outgoing_to_device_messages.insert(request_id, request); self.outgoing_messages.insert(request_id, request);
} }
OutgoingContent::Room(r, c) => { OutgoingContent::Room(r, c) => {
@ -160,12 +159,11 @@ impl VerificationMachine {
request_id, request_id,
}; };
self.outgoing_room_messages.insert(request_id, request); self.outgoing_messages.insert(request_id, request);
} }
} }
} }
#[allow(dead_code)]
fn receive_room_event_helper(&self, sas: &Sas, event: &AnyMessageEvent) { fn receive_room_event_helper(&self, sas: &Sas, event: &AnyMessageEvent) {
if let Some(c) = sas.receive_room_event(event) { if let Some(c) = sas.receive_room_event(event) {
self.queue_up_content(sas.other_user_id(), sas.other_device_id(), c); self.queue_up_content(sas.other_user_id(), sas.other_device_id(), c);
@ -179,20 +177,11 @@ impl VerificationMachine {
} }
pub fn mark_request_as_sent(&self, uuid: &Uuid) { pub fn mark_request_as_sent(&self, uuid: &Uuid) {
self.outgoing_room_messages.remove(uuid); self.outgoing_messages.remove(uuid);
self.outgoing_to_device_messages.remove(uuid);
} }
pub fn outgoing_room_message_requests(&self) -> Vec<OutgoingRequest> { pub fn outgoing_messages(&self) -> Vec<OutgoingRequest> {
self.outgoing_room_messages self.outgoing_messages
.iter()
.map(|r| (*r).clone())
.collect()
}
pub fn outgoing_to_device_requests(&self) -> Vec<OutgoingRequest> {
#[allow(clippy::map_clone)]
self.outgoing_to_device_messages
.iter() .iter()
.map(|r| (*r).clone()) .map(|r| (*r).clone())
.collect() .collect()
@ -204,7 +193,7 @@ impl VerificationMachine {
for sas in self.verifications.iter() { for sas in self.verifications.iter() {
if let Some(r) = sas.cancel_if_timed_out() { if let Some(r) = sas.cancel_if_timed_out() {
self.outgoing_to_device_messages.insert( self.outgoing_messages.insert(
r.request_id(), r.request_id(),
OutgoingRequest { OutgoingRequest {
request_id: r.request_id(), request_id: r.request_id(),
@ -240,22 +229,23 @@ impl VerificationMachine {
m.sender, r.from_device m.sender, r.from_device
); );
let request = VerificationRequest::from_request_event( let request = VerificationRequest::from_room_request(
self.account.clone(), self.account.clone(),
self.private_identity.lock().await.clone(), self.private_identity.lock().await.clone(),
self.store.clone(), self.store.clone(),
room_id,
&m.sender, &m.sender,
&m.event_id, &m.event_id,
room_id,
r, r,
); );
self.requests.insert(m.event_id.clone(), request); self.requests
.insert(request.flow_id().as_str().to_owned(), request);
} }
} }
} }
AnySyncMessageEvent::KeyVerificationReady(e) => { AnySyncMessageEvent::KeyVerificationReady(e) => {
if let Some(request) = self.requests.get(&e.content.relation.event_id) { if let Some(request) = self.requests.get(e.content.relation.event_id.as_str()) {
if &e.sender == request.other_user() { if &e.sender == request.other_user() {
// TODO remove this unwrap. // TODO remove this unwrap.
request.receive_ready(&e.sender, &e.content).unwrap(); request.receive_ready(&e.sender, &e.content).unwrap();
@ -268,7 +258,9 @@ impl VerificationMachine {
e.sender, e.content.from_device e.sender, e.content.from_device
); );
if let Some((_, request)) = self.requests.remove(&e.content.relation.event_id) { if let Some((_, request)) =
self.requests.remove(e.content.relation.event_id.as_str())
{
if let Some(d) = self if let Some(d) = self
.store .store
.get_device(&e.sender, &e.content.from_device) .get_device(&e.sender, &e.content.from_device)
@ -282,7 +274,7 @@ impl VerificationMachine {
Ok(s) => { Ok(s) => {
info!( info!(
"Started a new SAS verification, \ "Started a new SAS verification, \
automatically accepting because of in-room" automatically accepting because we accepted from a request"
); );
// TODO remove this unwrap // TODO remove this unwrap
@ -291,7 +283,7 @@ impl VerificationMachine {
self.room_verifications self.room_verifications
.insert(e.content.relation.event_id.clone(), s); .insert(e.content.relation.event_id.clone(), s);
self.outgoing_room_messages self.outgoing_messages
.insert(accept_request.request_id(), accept_request.into()); .insert(accept_request.request_id(), accept_request.into());
} }
Err(c) => { Err(c) => {
@ -299,7 +291,7 @@ impl VerificationMachine {
"Can't start key verification with {} {}, canceling: {:?}", "Can't start key verification with {} {}, canceling: {:?}",
e.sender, e.content.from_device, c e.sender, e.content.from_device, c
); );
// self.queue_up_content(&e.sender, &e.content.from_device, c) self.queue_up_content(&e.sender, &e.content.from_device, c)
} }
} }
} }
@ -339,14 +331,12 @@ impl VerificationMachine {
} }
} }
VerificationResult::Cancel(r) => { VerificationResult::Cancel(r) => {
self.outgoing_to_device_messages self.outgoing_messages.insert(r.request_id(), r.into());
.insert(r.request_id(), r.into());
} }
VerificationResult::SignatureUpload(r) => { VerificationResult::SignatureUpload(r) => {
let request: OutgoingRequest = r.into(); let request: OutgoingRequest = r.into();
self.outgoing_to_device_messages self.outgoing_messages.insert(request.request_id, request);
.insert(request.request_id, request);
if let Some(c) = content { if let Some(c) = content {
self.queue_up_content( self.queue_up_content(
@ -371,6 +361,26 @@ impl VerificationMachine {
trace!("Received a key verification event {:?}", event); trace!("Received a key verification event {:?}", event);
match event { match event {
AnyToDeviceEvent::KeyVerificationRequest(e) => {
let request = VerificationRequest::from_request(
self.account.clone(),
self.private_identity.lock().await.clone(),
self.store.clone(),
&e.sender,
&e.content,
);
self.requests
.insert(request.flow_id().as_str().to_string(), request);
}
AnyToDeviceEvent::KeyVerificationReady(e) => {
if let Some(request) = self.requests.get(&e.content.transaction_id) {
if &e.sender == request.other_user() {
// TODO remove this unwrap.
request.receive_ready(&e.sender, &e.content).unwrap();
}
}
}
AnyToDeviceEvent::KeyVerificationStart(e) => { AnyToDeviceEvent::KeyVerificationStart(e) => {
trace!( trace!(
"Received a m.key.verification start event from {} {}", "Received a m.key.verification start event from {} {}",
@ -432,7 +442,7 @@ impl VerificationMachine {
match s.mark_as_done().await? { match s.mark_as_done().await? {
VerificationResult::Ok => (), VerificationResult::Ok => (),
VerificationResult::Cancel(r) => { VerificationResult::Cancel(r) => {
self.outgoing_to_device_messages.insert( self.outgoing_messages.insert(
r.request_id(), r.request_id(),
OutgoingRequest { OutgoingRequest {
request_id: r.request_id(), request_id: r.request_id(),
@ -443,7 +453,7 @@ impl VerificationMachine {
VerificationResult::SignatureUpload(r) => { VerificationResult::SignatureUpload(r) => {
let request_id = Uuid::new_v4(); let request_id = Uuid::new_v4();
self.outgoing_to_device_messages.insert( self.outgoing_messages.insert(
request_id, request_id,
OutgoingRequest { OutgoingRequest {
request_id, request_id,
@ -521,6 +531,7 @@ mod test {
alice_device, alice_device,
bob_store, bob_store,
None, None,
None,
); );
machine machine
@ -558,15 +569,11 @@ mod test {
.map(|c| wrap_any_to_device_content(bob.user_id(), c)) .map(|c| wrap_any_to_device_content(bob.user_id(), c))
.unwrap(); .unwrap();
assert!(alice_machine.outgoing_to_device_messages.is_empty()); assert!(alice_machine.outgoing_messages.is_empty());
alice_machine.receive_event(&event).await.unwrap(); alice_machine.receive_event(&event).await.unwrap();
assert!(!alice_machine.outgoing_to_device_messages.is_empty()); assert!(!alice_machine.outgoing_messages.is_empty());
let request = alice_machine let request = alice_machine.outgoing_messages.iter().next().unwrap();
.outgoing_to_device_messages
.iter()
.next()
.unwrap();
let txn_id = *request.request_id(); let txn_id = *request.request_id();
@ -611,14 +618,14 @@ mod test {
let alice = alice_machine.get_sas(bob.flow_id().as_str()).unwrap(); let alice = alice_machine.get_sas(bob.flow_id().as_str()).unwrap();
assert!(!alice.timed_out()); assert!(!alice.timed_out());
assert!(alice_machine.outgoing_to_device_messages.is_empty()); assert!(alice_machine.outgoing_messages.is_empty());
// This line panics on macOS, so we're disabled for now. // This line panics on macOS, so we're disabled for now.
alice.set_creation_time(Instant::now() - Duration::from_secs(60 * 15)); alice.set_creation_time(Instant::now() - Duration::from_secs(60 * 15));
assert!(alice.timed_out()); assert!(alice.timed_out());
assert!(alice_machine.outgoing_to_device_messages.is_empty()); assert!(alice_machine.outgoing_messages.is_empty());
alice_machine.garbage_collect(); alice_machine.garbage_collect();
assert!(!alice_machine.outgoing_to_device_messages.is_empty()); assert!(!alice_machine.outgoing_messages.is_empty());
alice_machine.garbage_collect(); alice_machine.garbage_collect();
assert!(alice_machine.verifications.is_empty()); assert!(alice_machine.verifications.is_empty());
} }

View File

@ -20,6 +20,43 @@ pub use machine::VerificationMachine;
pub use requests::VerificationRequest; pub use requests::VerificationRequest;
pub use sas::{AcceptSettings, Sas, VerificationResult}; pub use sas::{AcceptSettings, Sas, VerificationResult};
use matrix_sdk_common::identifiers::{EventId, RoomId};
#[derive(Clone, Debug, Hash, PartialEq, PartialOrd)]
pub enum FlowId {
ToDevice(String),
InRoom(RoomId, EventId),
}
impl FlowId {
pub fn room_id(&self) -> Option<&RoomId> {
if let FlowId::InRoom(r, _) = &self {
Some(r)
} else {
None
}
}
pub fn as_str(&self) -> &str {
match self {
FlowId::InRoom(_, r) => r.as_str(),
FlowId::ToDevice(t) => t.as_str(),
}
}
}
impl From<String> for FlowId {
fn from(transaciton_id: String) -> Self {
FlowId::ToDevice(transaciton_id)
}
}
impl From<(RoomId, EventId)> for FlowId {
fn from(ids: (RoomId, EventId)) -> Self {
FlowId::InRoom(ids.0, ids.1)
}
}
#[cfg(test)] #[cfg(test)]
pub(crate) mod test { pub(crate) mod test {
use crate::{ use crate::{

View File

@ -14,30 +14,130 @@
#![allow(dead_code)] #![allow(dead_code)]
use std::sync::{Arc, Mutex}; use std::{
convert::TryFrom,
sync::{Arc, Mutex},
};
use matrix_sdk_common::{ use matrix_sdk_common::{
api::r0::message::send_message_event::Response as RoomMessageResponse, api::r0::to_device::DeviceIdOrAllDevices,
events::{ events::{
key::verification::{ key::verification::{
ready::ReadyEventContent, start::StartEventContent, Relation, VerificationMethod, ready::{ReadyEventContent, ReadyToDeviceEventContent},
request::RequestToDeviceEventContent,
start::StartEventContent,
Relation, VerificationMethod,
}, },
room::message::KeyVerificationRequestEventContent, room::message::KeyVerificationRequestEventContent,
MessageEvent, SyncMessageEvent, AnyMessageEventContent, AnyToDeviceEventContent, MessageEvent, SyncMessageEvent,
}, },
identifiers::{DeviceId, DeviceIdBox, EventId, RoomId, UserId}, identifiers::{DeviceId, DeviceIdBox, EventId, RoomId, UserId},
uuid::Uuid,
}; };
use crate::{ use crate::{
olm::{PrivateCrossSigningIdentity, ReadOnlyAccount}, olm::{PrivateCrossSigningIdentity, ReadOnlyAccount},
store::CryptoStore, store::CryptoStore,
ReadOnlyDevice, Sas, UserIdentities, OutgoingVerificationRequest, ReadOnlyDevice, RoomMessageRequest, Sas, ToDeviceRequest,
UserIdentities,
}; };
use super::sas::{OutgoingContent, StartContent}; use super::{
sas::{content_to_request, OutgoingContent, StartContent},
FlowId,
};
const SUPPORTED_METHODS: &[VerificationMethod] = &[VerificationMethod::MSasV1]; const SUPPORTED_METHODS: &[VerificationMethod] = &[VerificationMethod::MSasV1];
pub enum RequestContent<'a> {
ToDevice(&'a RequestToDeviceEventContent),
Room(&'a KeyVerificationRequestEventContent),
}
impl RequestContent<'_> {
fn from_device(&self) -> &DeviceId {
match self {
RequestContent::ToDevice(t) => &t.from_device,
RequestContent::Room(r) => &r.from_device,
}
}
fn methods(&self) -> &[VerificationMethod] {
match self {
RequestContent::ToDevice(t) => &t.methods,
RequestContent::Room(r) => &r.methods,
}
}
}
impl<'a> From<&'a KeyVerificationRequestEventContent> for RequestContent<'a> {
fn from(c: &'a KeyVerificationRequestEventContent) -> Self {
Self::Room(c)
}
}
impl<'a> From<&'a RequestToDeviceEventContent> for RequestContent<'a> {
fn from(c: &'a RequestToDeviceEventContent) -> Self {
Self::ToDevice(c)
}
}
pub enum ReadyContent<'a> {
ToDevice(&'a ReadyToDeviceEventContent),
Room(&'a ReadyEventContent),
}
impl ReadyContent<'_> {
fn from_device(&self) -> &DeviceId {
match self {
ReadyContent::ToDevice(t) => &t.from_device,
ReadyContent::Room(r) => &r.from_device,
}
}
fn methods(&self) -> &[VerificationMethod] {
match self {
ReadyContent::ToDevice(t) => &t.methods,
ReadyContent::Room(r) => &r.methods,
}
}
}
impl<'a> From<&'a ReadyEventContent> for ReadyContent<'a> {
fn from(c: &'a ReadyEventContent) -> Self {
Self::Room(c)
}
}
impl<'a> From<&'a ReadyToDeviceEventContent> for ReadyContent<'a> {
fn from(c: &'a ReadyToDeviceEventContent) -> Self {
Self::ToDevice(c)
}
}
impl<'a> TryFrom<&'a OutgoingContent> for ReadyContent<'a> {
type Error = ();
fn try_from(value: &'a OutgoingContent) -> Result<Self, Self::Error> {
match value {
OutgoingContent::Room(_, c) => {
if let AnyMessageEventContent::KeyVerificationReady(c) = c {
Ok(ReadyContent::Room(c))
} else {
Err(())
}
}
OutgoingContent::ToDevice(c) => {
if let AnyToDeviceEventContent::KeyVerificationReady(c) = c {
Ok(ReadyContent::ToDevice(c))
} else {
Err(())
}
}
}
}
}
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
/// TODO /// TODO
pub struct VerificationRequest { pub struct VerificationRequest {
@ -46,7 +146,7 @@ pub struct VerificationRequest {
other_user_id: Arc<UserId>, other_user_id: Arc<UserId>,
private_cross_signing_identity: PrivateCrossSigningIdentity, private_cross_signing_identity: PrivateCrossSigningIdentity,
store: Arc<Box<dyn CryptoStore>>, store: Arc<Box<dyn CryptoStore>>,
room_id: Arc<RoomId>, flow_id: Arc<FlowId>,
} }
impl VerificationRequest { impl VerificationRequest {
@ -55,30 +155,46 @@ impl VerificationRequest {
account: ReadOnlyAccount, account: ReadOnlyAccount,
private_cross_signing_identity: PrivateCrossSigningIdentity, private_cross_signing_identity: PrivateCrossSigningIdentity,
store: Arc<Box<dyn CryptoStore>>, store: Arc<Box<dyn CryptoStore>>,
room_id: Arc<RoomId>, room_id: &RoomId,
event_id: &EventId,
other_user: &UserId, other_user: &UserId,
) -> Self { ) -> Self {
let flow_id = (room_id.to_owned(), event_id.to_owned()).into();
let inner = Mutex::new(InnerRequest::Created(RequestState::new( let inner = Mutex::new(InnerRequest::Created(RequestState::new(
account.user_id(), account.user_id(),
account.device_id(), account.device_id(),
other_user, other_user,
&flow_id,
))) )))
.into(); .into();
Self { Self {
inner, inner,
account, account,
private_cross_signing_identity, private_cross_signing_identity,
store, store,
other_user_id: other_user.clone().into(), other_user_id: other_user.clone().into(),
room_id, flow_id: flow_id.into(),
} }
} }
/// TODO /// TODO
pub fn request(&self) -> Option<KeyVerificationRequestEventContent> { pub fn request(
match &*self.inner.lock().unwrap() { own_user_id: &UserId,
InnerRequest::Created(c) => Some(c.as_content()), own_device_id: &DeviceId,
_ => None, other_user_id: &UserId,
) -> 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.",
own_user_id
),
methods: SUPPORTED_METHODS.to_vec(),
from_device: own_device_id.into(),
to: other_user_id.to_owned(),
} }
} }
@ -88,23 +204,56 @@ impl VerificationRequest {
&self.other_user_id &self.other_user_id
} }
/// Mark the request as sent. /// Get the unique ID of this verification request
pub fn mark_as_sent(&self, response: &RoomMessageResponse) { pub fn flow_id(&self) -> &FlowId {
let mut inner = self.inner.lock().unwrap(); &self.flow_id
if let InnerRequest::Created(c) = &*inner {
*inner = InnerRequest::Sent(c.clone().into_sent(response));
}
} }
pub(crate) fn from_request_event( pub(crate) fn from_room_request(
account: ReadOnlyAccount, account: ReadOnlyAccount,
private_cross_signing_identity: PrivateCrossSigningIdentity, private_cross_signing_identity: PrivateCrossSigningIdentity,
store: Arc<Box<dyn CryptoStore>>, store: Arc<Box<dyn CryptoStore>>,
room_id: &RoomId,
sender: &UserId, sender: &UserId,
event_id: &EventId, event_id: &EventId,
room_id: &RoomId,
content: &KeyVerificationRequestEventContent, content: &KeyVerificationRequestEventContent,
) -> Self {
let flow_id = FlowId::from((room_id.to_owned(), event_id.to_owned()));
Self::from_helper(
account,
private_cross_signing_identity,
store,
sender,
flow_id,
content.into(),
)
}
pub(crate) fn from_request(
account: ReadOnlyAccount,
private_cross_signing_identity: PrivateCrossSigningIdentity,
store: Arc<Box<dyn CryptoStore>>,
sender: &UserId,
content: &RequestToDeviceEventContent,
) -> Self {
let flow_id = FlowId::from(content.transaction_id.to_owned());
Self::from_helper(
account,
private_cross_signing_identity,
store,
sender,
flow_id,
content.into(),
)
}
fn from_helper(
account: ReadOnlyAccount,
private_cross_signing_identity: PrivateCrossSigningIdentity,
store: Arc<Box<dyn CryptoStore>>,
sender: &UserId,
flow_id: FlowId,
content: RequestContent,
) -> Self { ) -> Self {
Self { Self {
inner: Arc::new(Mutex::new(InnerRequest::Requested( inner: Arc::new(Mutex::new(InnerRequest::Requested(
@ -112,7 +261,7 @@ impl VerificationRequest {
account.user_id(), account.user_id(),
account.device_id(), account.device_id(),
sender, sender,
event_id, &flow_id,
content, content,
), ),
))), ))),
@ -120,29 +269,37 @@ impl VerificationRequest {
other_user_id: sender.clone().into(), other_user_id: sender.clone().into(),
private_cross_signing_identity, private_cross_signing_identity,
store, store,
room_id: room_id.clone().into(), flow_id: flow_id.into(),
} }
} }
/// The room id where the verification is happening.
pub fn room_id(&self) -> &RoomId {
&self.room_id
}
/// Accept the verification request. /// Accept the verification request.
pub fn accept(&self) -> Option<ReadyEventContent> { pub fn accept(&self) -> Option<OutgoingVerificationRequest> {
self.inner.lock().unwrap().accept() let mut inner = self.inner.lock().unwrap();
inner.accept().map(|c| match c {
OutgoingContent::ToDevice(content) => self
.content_to_request(inner.other_device_id(), content)
.into(),
OutgoingContent::Room(room_id, content) => RoomMessageRequest {
room_id,
txn_id: Uuid::new_v4(),
content,
}
.into(),
})
} }
#[allow(clippy::unnecessary_wraps)] #[allow(clippy::unnecessary_wraps)]
pub(crate) fn receive_ready( pub(crate) fn receive_ready<'a>(
&self, &self,
sender: &UserId, sender: &UserId,
content: &ReadyEventContent, content: impl Into<ReadyContent<'a>>,
) -> Result<(), ()> { ) -> Result<(), ()> {
let mut inner = self.inner.lock().unwrap(); let mut inner = self.inner.lock().unwrap();
let content = content.into();
if let InnerRequest::Sent(s) = &*inner { if let InnerRequest::Created(s) = &*inner {
*inner = InnerRequest::Ready(s.clone().into_ready(sender, content)); *inner = InnerRequest::Ready(s.clone().into_ready(sender, content));
} }
@ -161,14 +318,17 @@ impl VerificationRequest {
user_identity: Option<UserIdentities>, user_identity: Option<UserIdentities>,
) -> Result<Sas, OutgoingContent> { ) -> Result<Sas, OutgoingContent> {
match &*self.inner.lock().unwrap() { match &*self.inner.lock().unwrap() {
InnerRequest::Ready(s) => s.clone().into_started_sas( InnerRequest::Ready(s) => match &s.state.flow_id {
&event.clone().into_full_event(self.room_id().clone()), FlowId::ToDevice(_) => todo!(),
self.store.clone(), FlowId::InRoom(r, _) => s.clone().into_started_sas(
self.account.clone(), &event.clone().into_full_event(r.to_owned()),
self.private_cross_signing_identity.clone(), self.store.clone(),
device, self.account.clone(),
user_identity, self.private_cross_signing_identity.clone(),
), device,
user_identity,
),
},
// TODO cancel here since we got a missmatched message or do // TODO cancel here since we got a missmatched message or do
// nothing? // nothing?
_ => todo!(), _ => todo!(),
@ -181,30 +341,50 @@ impl VerificationRequest {
user_identity: Option<UserIdentities>, user_identity: Option<UserIdentities>,
) -> Option<(Sas, StartContent)> { ) -> Option<(Sas, StartContent)> {
match &*self.inner.lock().unwrap() { match &*self.inner.lock().unwrap() {
InnerRequest::Ready(s) => Some(s.clone().start_sas( InnerRequest::Ready(s) => match &s.state.flow_id {
self.room_id(), FlowId::ToDevice(_) => todo!(),
self.store.clone(), FlowId::InRoom(_, _) => Some(s.clone().start_sas(
self.account.clone(), self.store.clone(),
self.private_cross_signing_identity.clone(), self.account.clone(),
device, self.private_cross_signing_identity.clone(),
user_identity, device,
)), user_identity,
)),
},
_ => None, _ => None,
} }
} }
fn content_to_request(
&self,
other_device_id: DeviceIdOrAllDevices,
content: AnyToDeviceEventContent,
) -> ToDeviceRequest {
content_to_request(&self.other_user_id, other_device_id, content)
}
} }
#[derive(Debug)] #[derive(Debug)]
enum InnerRequest { enum InnerRequest {
Created(RequestState<Created>), Created(RequestState<Created>),
Sent(RequestState<Sent>),
Requested(RequestState<Requested>), Requested(RequestState<Requested>),
Ready(RequestState<Ready>), Ready(RequestState<Ready>),
Passive(RequestState<Passive>), Passive(RequestState<Passive>),
} }
impl InnerRequest { impl InnerRequest {
fn accept(&mut self) -> Option<ReadyEventContent> { fn other_device_id(&self) -> DeviceIdOrAllDevices {
match self {
InnerRequest::Created(_) => DeviceIdOrAllDevices::AllDevices,
InnerRequest::Requested(_) => DeviceIdOrAllDevices::AllDevices,
InnerRequest::Ready(r) => {
DeviceIdOrAllDevices::DeviceId(r.state.other_device_id.to_owned())
}
InnerRequest::Passive(_) => DeviceIdOrAllDevices::AllDevices,
}
}
fn accept(&mut self) -> Option<OutgoingContent> {
if let InnerRequest::Requested(s) = self { if let InnerRequest::Requested(s) = self {
let (state, content) = s.clone().accept(); let (state, content) = s.clone().accept();
*self = InnerRequest::Ready(state); *self = InnerRequest::Ready(state);
@ -254,72 +434,49 @@ struct RequestState<S: Clone> {
state: S, state: S,
} }
#[derive(Clone, Debug)]
struct Created {}
impl RequestState<Created> { impl RequestState<Created> {
fn new(own_user_id: &UserId, own_device_id: &DeviceId, other_user: &UserId) -> Self { fn new(
own_user_id: &UserId,
own_device_id: &DeviceId,
other_user_id: &UserId,
flow_id: &FlowId,
) -> Self {
Self { Self {
own_user_id: own_user_id.clone(), own_user_id: own_user_id.to_owned(),
own_device_id: own_device_id.into(), own_device_id: own_device_id.to_owned(),
other_user_id: other_user.clone(), other_user_id: other_user_id.to_owned(),
state: Created {}, state: Created {
}
}
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<Sent> {
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(), methods: SUPPORTED_METHODS.to_vec(),
flow_id: response.event_id.clone(), flow_id: flow_id.to_owned(),
}, },
} }
} }
}
#[derive(Clone, Debug)] fn into_ready(self, _sender: &UserId, content: ReadyContent) -> RequestState<Ready> {
struct Sent {
/// The verification methods supported by the sender.
pub methods: Vec<VerificationMethod>,
/// 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<Sent> {
fn into_ready(self, _sender: &UserId, content: &ReadyEventContent) -> RequestState<Ready> {
// TODO check the flow id, and that the methods match what we suggested. // TODO check the flow id, and that the methods match what we suggested.
RequestState { RequestState {
own_user_id: self.own_user_id, own_user_id: self.own_user_id,
own_device_id: self.own_device_id, own_device_id: self.own_device_id,
other_user_id: self.other_user_id, other_user_id: self.other_user_id,
state: Ready { state: Ready {
methods: content.methods.to_owned(), methods: content.methods().to_owned(),
other_device_id: content.from_device.clone(), other_device_id: content.from_device().into(),
flow_id: self.state.flow_id, flow_id: self.state.flow_id,
}, },
} }
} }
} }
#[derive(Clone, Debug)]
struct Created {
/// The verification methods supported by the sender.
pub methods: Vec<VerificationMethod>,
/// The event id of our `m.key.verification.request` event which acts as an
/// unique id identifying this verification flow.
pub flow_id: FlowId,
}
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
struct Requested { struct Requested {
/// The verification methods supported by the sender. /// The verification methods supported by the sender.
@ -327,7 +484,7 @@ struct Requested {
/// The event id of the `m.key.verification.request` event which acts as an /// The event id of the `m.key.verification.request` event which acts as an
/// unique id identifying this verification flow. /// unique id identifying this verification flow.
pub flow_id: EventId, pub flow_id: FlowId,
/// The device id of the device that responded to the verification request. /// The device id of the device that responded to the verification request.
pub other_device_id: DeviceIdBox, pub other_device_id: DeviceIdBox,
@ -338,8 +495,8 @@ impl RequestState<Requested> {
own_user_id: &UserId, own_user_id: &UserId,
own_device_id: &DeviceId, own_device_id: &DeviceId,
sender: &UserId, sender: &UserId,
event_id: &EventId, flow_id: &FlowId,
content: &KeyVerificationRequestEventContent, content: RequestContent,
) -> RequestState<Requested> { ) -> RequestState<Requested> {
// TODO only create this if we suport the methods // TODO only create this if we suport the methods
RequestState { RequestState {
@ -347,14 +504,14 @@ impl RequestState<Requested> {
own_device_id: own_device_id.into(), own_device_id: own_device_id.into(),
other_user_id: sender.clone(), other_user_id: sender.clone(),
state: Requested { state: Requested {
methods: content.methods.clone(), methods: content.methods().to_owned(),
flow_id: event_id.clone(), flow_id: flow_id.clone(),
other_device_id: content.from_device.clone(), other_device_id: content.from_device().into(),
}, },
} }
} }
fn accept(self) -> (RequestState<Ready>, ReadyEventContent) { fn accept(self) -> (RequestState<Ready>, OutgoingContent) {
let state = RequestState { let state = RequestState {
own_user_id: self.own_user_id, own_user_id: self.own_user_id,
own_device_id: self.own_device_id.clone(), own_device_id: self.own_device_id.clone(),
@ -366,12 +523,24 @@ impl RequestState<Requested> {
}, },
}; };
let content = ReadyEventContent { let content = match self.state.flow_id {
from_device: self.own_device_id, FlowId::ToDevice(i) => {
methods: self.state.methods, AnyToDeviceEventContent::KeyVerificationReady(ReadyToDeviceEventContent {
relation: Relation { from_device: self.own_device_id,
event_id: self.state.flow_id, methods: self.state.methods,
}, transaction_id: i,
})
.into()
}
FlowId::InRoom(r, e) => (
r,
AnyMessageEventContent::KeyVerificationReady(ReadyEventContent {
from_device: self.own_device_id,
methods: self.state.methods,
relation: Relation { event_id: e },
}),
)
.into(),
}; };
(state, content) (state, content)
@ -388,7 +557,7 @@ struct Ready {
/// The event id of the `m.key.verification.request` event which acts as an /// The event id of the `m.key.verification.request` event which acts as an
/// unique id identifying this verification flow. /// unique id identifying this verification flow.
pub flow_id: EventId, pub flow_id: FlowId,
} }
impl RequestState<Ready> { impl RequestState<Ready> {
@ -413,22 +582,31 @@ impl RequestState<Ready> {
fn start_sas( fn start_sas(
self, self,
room_id: &RoomId,
store: Arc<Box<dyn CryptoStore>>, store: Arc<Box<dyn CryptoStore>>,
account: ReadOnlyAccount, account: ReadOnlyAccount,
private_identity: PrivateCrossSigningIdentity, private_identity: PrivateCrossSigningIdentity,
other_device: ReadOnlyDevice, other_device: ReadOnlyDevice,
other_identity: Option<UserIdentities>, other_identity: Option<UserIdentities>,
) -> (Sas, StartContent) { ) -> (Sas, StartContent) {
Sas::start_in_room( match self.state.flow_id {
self.state.flow_id, FlowId::ToDevice(t) => Sas::start(
room_id.clone(), account,
account, private_identity,
private_identity, other_device,
other_device, store,
store, other_identity,
other_identity, Some(t),
) ),
FlowId::InRoom(r, e) => Sas::start_in_room(
e,
r,
account,
private_identity,
other_device,
store,
other_identity,
),
}
} }
} }
@ -447,7 +625,6 @@ mod test {
use std::{convert::TryFrom, time::SystemTime}; use std::{convert::TryFrom, time::SystemTime};
use matrix_sdk_common::{ use matrix_sdk_common::{
api::r0::message::send_message_event::Response as RoomMessageResponse,
events::{SyncMessageEvent, Unsigned}, events::{SyncMessageEvent, Unsigned},
identifiers::{event_id, room_id, DeviceIdBox, UserId}, identifiers::{event_id, room_id, DeviceIdBox, UserId},
}; };
@ -456,7 +633,10 @@ mod test {
use crate::{ use crate::{
olm::{PrivateCrossSigningIdentity, ReadOnlyAccount}, olm::{PrivateCrossSigningIdentity, ReadOnlyAccount},
store::{CryptoStore, MemoryStore}, store::{CryptoStore, MemoryStore},
verification::sas::StartContent, verification::{
requests::ReadyContent,
sas::{OutgoingContent, StartContent},
},
ReadOnlyDevice, ReadOnlyDevice,
}; };
@ -491,32 +671,31 @@ mod test {
let bob_store: Box<dyn CryptoStore> = Box::new(MemoryStore::new()); let bob_store: Box<dyn CryptoStore> = Box::new(MemoryStore::new());
let bob_identity = PrivateCrossSigningIdentity::empty(alice_id()); let bob_identity = PrivateCrossSigningIdentity::empty(alice_id());
let content = VerificationRequest::request(bob.user_id(), bob.device_id(), &alice_id());
let bob_request = VerificationRequest::new( let bob_request = VerificationRequest::new(
bob, bob,
bob_identity, bob_identity,
bob_store.into(), bob_store.into(),
room_id.clone().into(), &room_id,
&event_id,
&alice_id(), &alice_id(),
); );
let content = bob_request.request().unwrap(); let alice_request = VerificationRequest::from_room_request(
let alice_request = VerificationRequest::from_request_event(
alice, alice,
alice_identity, alice_identity,
alice_store.into(), alice_store.into(),
&room_id,
&bob_id(), &bob_id(),
&event_id, &event_id,
&room_id,
&content, &content,
); );
let content = alice_request.accept().unwrap(); let content: OutgoingContent = alice_request.accept().unwrap().into();
let content = ReadyContent::try_from(&content).unwrap();
let response = RoomMessageResponse::new(event_id); bob_request.receive_ready(&alice_id(), content).unwrap();
bob_request.mark_as_sent(&response);
bob_request.receive_ready(&alice_id(), &content).unwrap();
assert!(bob_request.is_ready()); assert!(bob_request.is_ready());
assert!(alice_request.is_ready()); assert!(alice_request.is_ready());
@ -538,32 +717,31 @@ mod test {
let bob_store: Box<dyn CryptoStore> = Box::new(MemoryStore::new()); let bob_store: Box<dyn CryptoStore> = Box::new(MemoryStore::new());
let bob_identity = PrivateCrossSigningIdentity::empty(alice_id()); let bob_identity = PrivateCrossSigningIdentity::empty(alice_id());
let content = VerificationRequest::request(bob.user_id(), bob.device_id(), &alice_id());
let bob_request = VerificationRequest::new( let bob_request = VerificationRequest::new(
bob, bob,
bob_identity, bob_identity,
bob_store.into(), bob_store.into(),
room_id.clone().into(), &room_id,
&event_id,
&alice_id(), &alice_id(),
); );
let content = bob_request.request().unwrap(); let alice_request = VerificationRequest::from_room_request(
let alice_request = VerificationRequest::from_request_event(
alice, alice,
alice_identity, alice_identity,
alice_store.into(), alice_store.into(),
&room_id,
&bob_id(), &bob_id(),
&event_id, &event_id,
&room_id,
&content, &content,
); );
let content = alice_request.accept().unwrap(); let content: OutgoingContent = alice_request.accept().unwrap().into();
let content = ReadyContent::try_from(&content).unwrap();
let response = RoomMessageResponse::new(event_id.clone()); bob_request.receive_ready(&alice_id(), content).unwrap();
bob_request.mark_as_sent(&response);
bob_request.receive_ready(&alice_id(), &content).unwrap();
assert!(bob_request.is_ready()); assert!(bob_request.is_ready());
assert!(alice_request.is_ready()); assert!(alice_request.is_ready());

View File

@ -12,8 +12,6 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
#![allow(dead_code)]
use std::{collections::BTreeMap, convert::TryInto}; use std::{collections::BTreeMap, convert::TryInto};
use matrix_sdk_common::{ use matrix_sdk_common::{
@ -276,3 +274,71 @@ impl From<(RoomId, AnyMessageEventContent)> for OutgoingContent {
OutgoingContent::Room(content.0, content.1) OutgoingContent::Room(content.0, content.1)
} }
} }
#[cfg(test)]
use crate::OutgoingVerificationRequest;
#[cfg(test)]
impl From<OutgoingVerificationRequest> for OutgoingContent {
fn from(request: OutgoingVerificationRequest) -> Self {
use matrix_sdk_common::events::EventType;
use serde_json::Value;
match request {
OutgoingVerificationRequest::ToDevice(r) => {
let json: Value = serde_json::from_str(
r.messages
.values()
.next()
.unwrap()
.values()
.next()
.unwrap()
.get(),
)
.unwrap();
match r.event_type {
EventType::KeyVerificationRequest => {
AnyToDeviceEventContent::KeyVerificationRequest(
serde_json::from_value(json).unwrap(),
)
}
EventType::KeyVerificationReady => {
AnyToDeviceEventContent::KeyVerificationReady(
serde_json::from_value(json).unwrap(),
)
}
EventType::KeyVerificationDone => AnyToDeviceEventContent::KeyVerificationDone(
serde_json::from_value(json).unwrap(),
),
EventType::KeyVerificationStart => {
AnyToDeviceEventContent::KeyVerificationStart(
serde_json::from_value(json).unwrap(),
)
}
EventType::KeyVerificationKey => AnyToDeviceEventContent::KeyVerificationKey(
serde_json::from_value(json).unwrap(),
),
EventType::KeyVerificationAccept => {
AnyToDeviceEventContent::KeyVerificationAccept(
serde_json::from_value(json).unwrap(),
)
}
EventType::KeyVerificationMac => AnyToDeviceEventContent::KeyVerificationMac(
serde_json::from_value(json).unwrap(),
),
EventType::KeyVerificationCancel => {
AnyToDeviceEventContent::KeyVerificationCancel(
serde_json::from_value(json).unwrap(),
)
}
_ => unreachable!(),
}
.into()
}
OutgoingVerificationRequest::InRoom(r) => (r.room_id, r.content).into(),
}
}
}

View File

@ -29,7 +29,7 @@ use matrix_sdk_common::{
}, },
AnyToDeviceEventContent, EventType, AnyToDeviceEventContent, EventType,
}, },
identifiers::{DeviceId, DeviceKeyAlgorithm, DeviceKeyId, UserId}, identifiers::{DeviceKeyAlgorithm, DeviceKeyId, UserId},
uuid::Uuid, uuid::Uuid,
}; };
@ -41,7 +41,7 @@ use crate::{
use super::{ use super::{
event_enums::{MacContent, StartContent}, event_enums::{MacContent, StartContent},
sas_state::FlowId, FlowId,
}; };
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -562,14 +562,14 @@ fn bytes_to_decimal(bytes: Vec<u8>) -> (u16, u16, u16) {
pub fn content_to_request( pub fn content_to_request(
recipient: &UserId, recipient: &UserId,
recipient_device: &DeviceId, recipient_device: impl Into<DeviceIdOrAllDevices>,
content: AnyToDeviceEventContent, content: AnyToDeviceEventContent,
) -> ToDeviceRequest { ) -> ToDeviceRequest {
let mut messages = BTreeMap::new(); let mut messages = BTreeMap::new();
let mut user_messages = BTreeMap::new(); let mut user_messages = BTreeMap::new();
user_messages.insert( user_messages.insert(
DeviceIdOrAllDevices::DeviceId(recipient_device.into()), recipient_device.into(),
serde_json::value::to_raw_value(&content).expect("Can't serialize to-device content"), serde_json::value::to_raw_value(&content).expect("Can't serialize to-device content"),
); );
messages.insert(recipient.clone(), user_messages); messages.insert(recipient.clone(), user_messages);
@ -580,6 +580,8 @@ pub fn content_to_request(
AnyToDeviceEventContent::KeyVerificationKey(_) => EventType::KeyVerificationKey, AnyToDeviceEventContent::KeyVerificationKey(_) => EventType::KeyVerificationKey,
AnyToDeviceEventContent::KeyVerificationMac(_) => EventType::KeyVerificationMac, AnyToDeviceEventContent::KeyVerificationMac(_) => EventType::KeyVerificationMac,
AnyToDeviceEventContent::KeyVerificationCancel(_) => EventType::KeyVerificationCancel, AnyToDeviceEventContent::KeyVerificationCancel(_) => EventType::KeyVerificationCancel,
AnyToDeviceEventContent::KeyVerificationReady(_) => EventType::KeyVerificationReady,
AnyToDeviceEventContent::KeyVerificationDone(_) => EventType::KeyVerificationDone,
_ => unreachable!(), _ => unreachable!(),
}; };

View File

@ -33,10 +33,10 @@ use crate::{
use super::{ use super::{
event_enums::{AcceptContent, CancelContent, MacContent, OutgoingContent}, event_enums::{AcceptContent, CancelContent, MacContent, OutgoingContent},
sas_state::{ sas_state::{
Accepted, Canceled, Confirmed, Created, Done, FlowId, KeyReceived, MacReceived, SasState, Accepted, Canceled, Confirmed, Created, Done, KeyReceived, MacReceived, SasState, Started,
Started, WaitingForDone, WaitingForDone,
}, },
StartContent, FlowId, StartContent,
}; };
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -58,8 +58,9 @@ impl InnerSas {
account: ReadOnlyAccount, account: ReadOnlyAccount,
other_device: ReadOnlyDevice, other_device: ReadOnlyDevice,
other_identity: Option<UserIdentities>, other_identity: Option<UserIdentities>,
transaction_id: Option<String>,
) -> (InnerSas, StartContent) { ) -> (InnerSas, StartContent) {
let sas = SasState::<Created>::new(account, other_device, other_identity); let sas = SasState::<Created>::new(account, other_device, other_identity, transaction_id);
let content = sas.as_content(); let content = sas.as_content();
(InnerSas::Created(sas), content) (InnerSas::Created(sas), content)
} }

View File

@ -47,13 +47,11 @@ use crate::{
ReadOnlyAccount, ToDeviceRequest, ReadOnlyAccount, ToDeviceRequest,
}; };
use super::FlowId;
pub use event_enums::{CancelContent, OutgoingContent, StartContent};
pub use helpers::content_to_request; pub use helpers::content_to_request;
use inner_sas::InnerSas; use inner_sas::InnerSas;
pub use sas_state::FlowId;
pub use event_enums::{OutgoingContent, StartContent};
use self::event_enums::CancelContent;
#[derive(Debug)] #[derive(Debug)]
/// A result of a verification flow. /// A result of a verification flow.
@ -159,11 +157,13 @@ impl Sas {
other_device: ReadOnlyDevice, other_device: ReadOnlyDevice,
store: Arc<Box<dyn CryptoStore>>, store: Arc<Box<dyn CryptoStore>>,
other_identity: Option<UserIdentities>, other_identity: Option<UserIdentities>,
transaction_id: Option<String>,
) -> (Sas, StartContent) { ) -> (Sas, StartContent) {
let (inner, content) = InnerSas::start( let (inner, content) = InnerSas::start(
account.clone(), account.clone(),
other_device.clone(), other_device.clone(),
other_identity.clone(), other_identity.clone(),
transaction_id,
); );
( (
@ -189,7 +189,6 @@ impl Sas {
/// ///
/// Returns the new `Sas` object and a `StartEventContent` that needs to be /// Returns the new `Sas` object and a `StartEventContent` that needs to be
/// sent out through the server to the other device. /// sent out through the server to the other device.
#[allow(dead_code)]
pub(crate) fn start_in_room( pub(crate) fn start_in_room(
flow_id: EventId, flow_id: EventId,
room_id: RoomId, room_id: RoomId,
@ -685,7 +684,11 @@ impl Sas {
} }
pub(crate) fn content_to_request(&self, content: AnyToDeviceEventContent) -> ToDeviceRequest { pub(crate) fn content_to_request(&self, content: AnyToDeviceEventContent) -> ToDeviceRequest {
content_to_request(self.other_user_id(), self.other_device_id(), content) content_to_request(
self.other_user_id(),
self.other_device_id().to_owned(),
content,
)
} }
} }
@ -793,6 +796,7 @@ mod test {
bob_device, bob_device,
alice_store, alice_store,
None, None,
None,
); );
let bob = Sas::from_start_event( let bob = Sas::from_start_event(

View File

@ -54,6 +54,7 @@ use super::{
use crate::{ use crate::{
identities::{ReadOnlyDevice, UserIdentities}, identities::{ReadOnlyDevice, UserIdentities},
verification::FlowId,
ReadOnlyAccount, ReadOnlyAccount,
}; };
@ -72,29 +73,6 @@ const MAX_AGE: Duration = Duration::from_secs(60 * 5);
// The max time a SAS object will wait for a new event to arrive. // The max time a SAS object will wait for a new event to arrive.
const MAX_EVENT_TIMEOUT: Duration = Duration::from_secs(60); const MAX_EVENT_TIMEOUT: Duration = Duration::from_secs(60);
#[derive(Clone, Debug)]
pub enum FlowId {
ToDevice(String),
InRoom(RoomId, EventId),
}
impl FlowId {
pub fn room_id(&self) -> Option<&RoomId> {
if let FlowId::InRoom(r, _) = &self {
Some(r)
} else {
None
}
}
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 /// Struct containing the protocols that were agreed to be used for the SAS
/// flow. /// flow.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -386,8 +364,10 @@ impl SasState<Created> {
account: ReadOnlyAccount, account: ReadOnlyAccount,
other_device: ReadOnlyDevice, other_device: ReadOnlyDevice,
other_identity: Option<UserIdentities>, other_identity: Option<UserIdentities>,
transaction_id: Option<String>,
) -> SasState<Created> { ) -> SasState<Created> {
let flow_id = FlowId::ToDevice(Uuid::new_v4().to_string()); let flow_id =
FlowId::ToDevice(transaction_id.unwrap_or_else(|| Uuid::new_v4().to_string()));
Self::new_helper(flow_id, account, other_device, other_identity) Self::new_helper(flow_id, account, other_device, other_identity)
} }
@ -1259,7 +1239,7 @@ mod test {
let bob = ReadOnlyAccount::new(&bob_id(), &bob_device_id()); let bob = ReadOnlyAccount::new(&bob_id(), &bob_device_id());
let bob_device = ReadOnlyDevice::from_account(&bob).await; let bob_device = ReadOnlyDevice::from_account(&bob).await;
let alice_sas = SasState::<Created>::new(alice.clone(), bob_device, None); let alice_sas = SasState::<Created>::new(alice.clone(), bob_device, None, None);
let start_content = alice_sas.as_content(); let start_content = alice_sas.as_content();
@ -1430,7 +1410,7 @@ mod test {
let bob = ReadOnlyAccount::new(&bob_id(), &bob_device_id()); let bob = ReadOnlyAccount::new(&bob_id(), &bob_device_id());
let bob_device = ReadOnlyDevice::from_account(&bob).await; let bob_device = ReadOnlyDevice::from_account(&bob).await;
let alice_sas = SasState::<Created>::new(alice.clone(), bob_device, None); let alice_sas = SasState::<Created>::new(alice.clone(), bob_device, None, None);
let mut start_content = alice_sas.as_content(); let mut start_content = alice_sas.as_content();