crypto: Add enums so we can handle in-room and to-device verifications the same

master
Damir Jelić 2021-06-03 16:00:03 +02:00
parent b52f3fb11f
commit 1e48b15040
10 changed files with 1407 additions and 1192 deletions

View File

@ -32,7 +32,7 @@ use matrix_sdk_common::{
events::{ events::{
room::encrypted::{EncryptedEventContent, EncryptedEventScheme}, room::encrypted::{EncryptedEventContent, EncryptedEventScheme},
room_key::RoomKeyToDeviceEventContent, room_key::RoomKeyToDeviceEventContent,
AnyMessageEventContent, AnyToDeviceEvent, SyncMessageEvent, ToDeviceEvent, AnyMessageEventContent, AnyRoomEvent, AnyToDeviceEvent, SyncMessageEvent, ToDeviceEvent,
}, },
identifiers::{ identifiers::{
DeviceId, DeviceIdBox, DeviceKeyAlgorithm, EventEncryptionAlgorithm, RoomId, UserId, DeviceId, DeviceIdBox, DeviceKeyAlgorithm, EventEncryptionAlgorithm, RoomId, UserId,
@ -703,7 +703,7 @@ impl OlmMachine {
} }
async fn handle_verification_event(&self, event: &AnyToDeviceEvent) { async fn handle_verification_event(&self, event: &AnyToDeviceEvent) {
if let Err(e) = self.verification_machine.receive_event(&event).await { if let Err(e) = self.verification_machine.receive_any_event(event).await {
error!("Error handling a verification event: {:?}", e); error!("Error handling a verification event: {:?}", e);
} }
} }
@ -986,7 +986,11 @@ impl OlmMachine {
trace!("Successfully decrypted a Megolm event {:?}", decrypted_event); trace!("Successfully decrypted a Megolm event {:?}", decrypted_event);
if let Ok(e) = decrypted_event.deserialize() { if let Ok(e) = decrypted_event.deserialize() {
self.verification_machine.receive_room_event(room_id, &e).await?; let event = e.into_full_event(room_id.to_owned());
if let AnyRoomEvent::Message(e) = event {
self.verification_machine.receive_any_event(&e).await?;
}
} }
let encryption_info = let encryption_info =

View File

@ -0,0 +1,711 @@
// 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::{
collections::BTreeMap,
convert::{TryFrom, TryInto},
};
use matrix_sdk_common::{
events::{
key::verification::{
accept::{AcceptEventContent, AcceptMethod, AcceptToDeviceEventContent},
cancel::{CancelCode, CancelEventContent, CancelToDeviceEventContent},
done::{DoneEventContent, DoneToDeviceEventContent},
key::{KeyEventContent, KeyToDeviceEventContent},
mac::{MacEventContent, MacToDeviceEventContent},
ready::{ReadyEventContent, ReadyToDeviceEventContent},
request::RequestToDeviceEventContent,
start::{StartEventContent, StartMethod, StartToDeviceEventContent},
VerificationMethod,
},
room::message::{KeyVerificationRequestEventContent, MessageType},
AnyMessageEvent, AnyMessageEventContent, AnyToDeviceEvent, AnyToDeviceEventContent,
},
identifiers::{DeviceId, RoomId, UserId},
CanonicalJsonValue,
};
use super::{sas::OutgoingContent, FlowId};
#[derive(Debug)]
pub enum AnyEvent<'a> {
Room(&'a AnyMessageEvent),
ToDevice(&'a AnyToDeviceEvent),
}
impl AnyEvent<'_> {
pub fn sender(&self) -> &UserId {
match self {
Self::Room(e) => &e.sender(),
Self::ToDevice(e) => &e.sender(),
}
}
pub fn verification_content(&self) -> Option<AnyVerificationContent> {
match self {
AnyEvent::Room(e) => match e {
AnyMessageEvent::CallAnswer(_)
| AnyMessageEvent::CallInvite(_)
| AnyMessageEvent::CallHangup(_)
| AnyMessageEvent::CallCandidates(_)
| AnyMessageEvent::Reaction(_)
| AnyMessageEvent::RoomEncrypted(_)
| AnyMessageEvent::RoomMessageFeedback(_)
| AnyMessageEvent::RoomRedaction(_)
| AnyMessageEvent::Sticker(_)
| AnyMessageEvent::Custom(_) => None,
AnyMessageEvent::RoomMessage(m) => {
if let MessageType::VerificationRequest(v) = &m.content.msgtype {
Some(RequestContent::from(v).into())
} else {
None
}
}
AnyMessageEvent::KeyVerificationReady(e) => {
Some(ReadyContent::from(&e.content).into())
}
AnyMessageEvent::KeyVerificationStart(e) => {
Some(StartContent::from(&e.content).into())
}
AnyMessageEvent::KeyVerificationCancel(e) => {
Some(CancelContent::from(&e.content).into())
}
AnyMessageEvent::KeyVerificationAccept(e) => {
Some(AcceptContent::from(&e.content).into())
}
AnyMessageEvent::KeyVerificationKey(e) => Some(KeyContent::from(&e.content).into()),
AnyMessageEvent::KeyVerificationMac(e) => Some(MacContent::from(&e.content).into()),
AnyMessageEvent::KeyVerificationDone(e) => {
Some(DoneContent::from(&e.content).into())
}
},
AnyEvent::ToDevice(e) => match e {
AnyToDeviceEvent::Dummy(_)
| AnyToDeviceEvent::RoomKey(_)
| AnyToDeviceEvent::RoomKeyRequest(_)
| AnyToDeviceEvent::ForwardedRoomKey(_)
| AnyToDeviceEvent::RoomEncrypted(_)
| AnyToDeviceEvent::Custom(_) => None,
AnyToDeviceEvent::KeyVerificationRequest(e) => {
Some(RequestContent::from(&e.content).into())
}
AnyToDeviceEvent::KeyVerificationReady(e) => {
Some(ReadyContent::from(&e.content).into())
}
AnyToDeviceEvent::KeyVerificationStart(e) => {
Some(StartContent::from(&e.content).into())
}
AnyToDeviceEvent::KeyVerificationCancel(e) => {
Some(CancelContent::from(&e.content).into())
}
AnyToDeviceEvent::KeyVerificationAccept(e) => {
Some(AcceptContent::from(&e.content).into())
}
AnyToDeviceEvent::KeyVerificationKey(e) => {
Some(KeyContent::from(&e.content).into())
}
AnyToDeviceEvent::KeyVerificationMac(e) => {
Some(MacContent::from(&e.content).into())
}
AnyToDeviceEvent::KeyVerificationDone(e) => {
Some(DoneContent::from(&e.content).into())
}
},
}
}
}
impl<'a> From<&'a AnyMessageEvent> for AnyEvent<'a> {
fn from(e: &'a AnyMessageEvent) -> Self {
Self::Room(e)
}
}
impl<'a> From<&'a AnyToDeviceEvent> for AnyEvent<'a> {
fn from(e: &'a AnyToDeviceEvent) -> Self {
Self::ToDevice(e)
}
}
impl TryFrom<&AnyEvent<'_>> for FlowId {
type Error = ();
fn try_from(value: &AnyEvent<'_>) -> Result<Self, Self::Error> {
match value {
AnyEvent::Room(e) => FlowId::try_from(*e),
AnyEvent::ToDevice(e) => FlowId::try_from(*e),
}
}
}
impl TryFrom<&AnyMessageEvent> for FlowId {
type Error = ();
fn try_from(value: &AnyMessageEvent) -> Result<Self, Self::Error> {
match value {
AnyMessageEvent::CallAnswer(_)
| AnyMessageEvent::CallInvite(_)
| AnyMessageEvent::CallHangup(_)
| AnyMessageEvent::CallCandidates(_)
| AnyMessageEvent::Reaction(_)
| AnyMessageEvent::RoomEncrypted(_)
| AnyMessageEvent::RoomMessageFeedback(_)
| AnyMessageEvent::RoomRedaction(_)
| AnyMessageEvent::Sticker(_)
| AnyMessageEvent::Custom(_) => Err(()),
AnyMessageEvent::KeyVerificationReady(e) => {
Ok(FlowId::from((&e.room_id, &e.content.relation.event_id)))
}
AnyMessageEvent::RoomMessage(e) => Ok(FlowId::from((&e.room_id, &e.event_id))),
AnyMessageEvent::KeyVerificationStart(e) => {
Ok(FlowId::from((&e.room_id, &e.content.relation.event_id)))
}
AnyMessageEvent::KeyVerificationCancel(e) => {
Ok(FlowId::from((&e.room_id, &e.content.relation.event_id)))
}
AnyMessageEvent::KeyVerificationAccept(e) => {
Ok(FlowId::from((&e.room_id, &e.content.relation.event_id)))
}
AnyMessageEvent::KeyVerificationKey(e) => {
Ok(FlowId::from((&e.room_id, &e.content.relation.event_id)))
}
AnyMessageEvent::KeyVerificationMac(e) => {
Ok(FlowId::from((&e.room_id, &e.content.relation.event_id)))
}
AnyMessageEvent::KeyVerificationDone(e) => {
Ok(FlowId::from((&e.room_id, &e.content.relation.event_id)))
}
}
}
}
impl TryFrom<&AnyToDeviceEvent> for FlowId {
type Error = ();
fn try_from(value: &AnyToDeviceEvent) -> Result<Self, Self::Error> {
match value {
AnyToDeviceEvent::Dummy(_)
| AnyToDeviceEvent::RoomKey(_)
| AnyToDeviceEvent::RoomKeyRequest(_)
| AnyToDeviceEvent::ForwardedRoomKey(_)
| AnyToDeviceEvent::RoomEncrypted(_)
| AnyToDeviceEvent::Custom(_) => Err(()),
AnyToDeviceEvent::KeyVerificationRequest(e) => {
Ok(FlowId::from(e.content.transaction_id.to_owned()))
}
AnyToDeviceEvent::KeyVerificationReady(e) => {
Ok(FlowId::from(e.content.transaction_id.to_owned()))
}
AnyToDeviceEvent::KeyVerificationStart(e) => {
Ok(FlowId::from(e.content.transaction_id.to_owned()))
}
AnyToDeviceEvent::KeyVerificationCancel(e) => {
Ok(FlowId::from(e.content.transaction_id.to_owned()))
}
AnyToDeviceEvent::KeyVerificationAccept(e) => {
Ok(FlowId::from(e.content.transaction_id.to_owned()))
}
AnyToDeviceEvent::KeyVerificationKey(e) => {
Ok(FlowId::from(e.content.transaction_id.to_owned()))
}
AnyToDeviceEvent::KeyVerificationMac(e) => {
Ok(FlowId::from(e.content.transaction_id.to_owned()))
}
AnyToDeviceEvent::KeyVerificationDone(e) => {
Ok(FlowId::from(e.content.transaction_id.to_owned()))
}
}
}
}
#[derive(Debug)]
pub enum AnyVerificationContent<'a> {
Request(RequestContent<'a>),
Ready(ReadyContent<'a>),
Cancel(CancelContent<'a>),
Start(StartContent<'a>),
Done(DoneContent<'a>),
Accept(AcceptContent<'a>),
Key(KeyContent<'a>),
Mac(MacContent<'a>),
}
impl<'a> From<ReadyContent<'a>> for AnyVerificationContent<'a> {
fn from(c: ReadyContent<'a>) -> Self {
Self::Ready(c)
}
}
impl<'a> From<RequestContent<'a>> for AnyVerificationContent<'a> {
fn from(c: RequestContent<'a>) -> Self {
Self::Request(c)
}
}
impl<'a> From<StartContent<'a>> for AnyVerificationContent<'a> {
fn from(c: StartContent<'a>) -> Self {
Self::Start(c)
}
}
impl<'a> From<DoneContent<'a>> for AnyVerificationContent<'a> {
fn from(c: DoneContent<'a>) -> Self {
Self::Done(c)
}
}
impl<'a> From<CancelContent<'a>> for AnyVerificationContent<'a> {
fn from(c: CancelContent<'a>) -> Self {
Self::Cancel(c)
}
}
impl<'a> From<AcceptContent<'a>> for AnyVerificationContent<'a> {
fn from(c: AcceptContent<'a>) -> Self {
Self::Accept(c)
}
}
impl<'a> From<KeyContent<'a>> for AnyVerificationContent<'a> {
fn from(c: KeyContent<'a>) -> Self {
Self::Key(c)
}
}
impl<'a> From<MacContent<'a>> for AnyVerificationContent<'a> {
fn from(c: MacContent<'a>) -> Self {
Self::Mac(c)
}
}
#[derive(Debug)]
pub enum RequestContent<'a> {
ToDevice(&'a RequestToDeviceEventContent),
Room(&'a KeyVerificationRequestEventContent),
}
impl RequestContent<'_> {
pub fn from_device(&self) -> &DeviceId {
match self {
Self::ToDevice(t) => &t.from_device,
Self::Room(r) => &r.from_device,
}
}
pub fn methods(&self) -> &[VerificationMethod] {
match self {
Self::ToDevice(t) => &t.methods,
Self::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)
}
}
#[derive(Debug)]
pub enum ReadyContent<'a> {
ToDevice(&'a ReadyToDeviceEventContent),
Room(&'a ReadyEventContent),
}
impl ReadyContent<'_> {
pub fn from_device(&self) -> &DeviceId {
match self {
Self::ToDevice(t) => &t.from_device,
Self::Room(r) => &r.from_device,
}
}
pub fn methods(&self) -> &[VerificationMethod] {
match self {
Self::ToDevice(t) => &t.methods,
Self::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)
}
}
macro_rules! try_from_outgoing_content {
($type: ident, $enum_variant: ident) => {
impl<'a> TryFrom<&'a OutgoingContent> for $type<'a> {
type Error = ();
fn try_from(value: &'a OutgoingContent) -> Result<Self, Self::Error> {
match value {
OutgoingContent::Room(_, c) => {
if let AnyMessageEventContent::$enum_variant(c) = c {
Ok(Self::Room(c))
} else {
Err(())
}
}
OutgoingContent::ToDevice(c) => {
if let AnyToDeviceEventContent::$enum_variant(c) = c {
Ok(Self::ToDevice(c))
} else {
Err(())
}
}
}
}
}
};
}
try_from_outgoing_content!(ReadyContent, KeyVerificationReady);
try_from_outgoing_content!(StartContent, KeyVerificationStart);
try_from_outgoing_content!(AcceptContent, KeyVerificationAccept);
try_from_outgoing_content!(KeyContent, KeyVerificationKey);
try_from_outgoing_content!(MacContent, KeyVerificationMac);
try_from_outgoing_content!(CancelContent, KeyVerificationCancel);
try_from_outgoing_content!(DoneContent, KeyVerificationDone);
#[derive(Debug)]
pub enum StartContent<'a> {
ToDevice(&'a StartToDeviceEventContent),
Room(&'a StartEventContent),
}
impl<'a> StartContent<'a> {
pub fn from_device(&self) -> &DeviceId {
match self {
Self::ToDevice(c) => &c.from_device,
Self::Room(c) => &c.from_device,
}
}
pub fn flow_id(&self) -> &str {
match self {
Self::ToDevice(c) => &c.transaction_id,
Self::Room(c) => &c.relation.event_id.as_str(),
}
}
pub fn method(&self) -> &StartMethod {
match self {
Self::ToDevice(c) => &c.method,
Self::Room(c) => &c.method,
}
}
pub fn canonical_json(&self) -> CanonicalJsonValue {
let content = match self {
Self::ToDevice(c) => serde_json::to_value(c),
Self::Room(c) => serde_json::to_value(c),
};
content.expect("Can't serialize content").try_into().expect("Can't canonicalize content")
}
}
impl<'a> From<&'a StartEventContent> for StartContent<'a> {
fn from(c: &'a StartEventContent) -> Self {
Self::Room(c)
}
}
impl<'a> From<&'a StartToDeviceEventContent> for StartContent<'a> {
fn from(c: &'a StartToDeviceEventContent) -> Self {
Self::ToDevice(c)
}
}
impl<'a> From<&'a OwnedStartContent> for StartContent<'a> {
fn from(c: &'a OwnedStartContent) -> Self {
match c {
OwnedStartContent::ToDevice(c) => Self::ToDevice(c),
OwnedStartContent::Room(_, c) => Self::Room(c),
}
}
}
#[derive(Debug)]
pub enum DoneContent<'a> {
ToDevice(&'a DoneToDeviceEventContent),
Room(&'a DoneEventContent),
}
impl<'a> From<&'a DoneEventContent> for DoneContent<'a> {
fn from(c: &'a DoneEventContent) -> Self {
Self::Room(c)
}
}
impl<'a> From<&'a DoneToDeviceEventContent> for DoneContent<'a> {
fn from(c: &'a DoneToDeviceEventContent) -> Self {
Self::ToDevice(c)
}
}
impl<'a> DoneContent<'a> {
pub fn flow_id(&self) -> &str {
match self {
Self::ToDevice(c) => &c.transaction_id,
Self::Room(c) => &c.relation.event_id.as_str(),
}
}
}
#[derive(Clone, Debug)]
pub enum AcceptContent<'a> {
ToDevice(&'a AcceptToDeviceEventContent),
Room(&'a AcceptEventContent),
}
impl AcceptContent<'_> {
pub fn flow_id(&self) -> &str {
match self {
Self::ToDevice(c) => &c.transaction_id,
Self::Room(c) => c.relation.event_id.as_str(),
}
}
pub fn method(&self) -> &AcceptMethod {
match self {
Self::ToDevice(c) => &c.method,
Self::Room(c) => &c.method,
}
}
}
impl<'a> From<&'a AcceptToDeviceEventContent> for AcceptContent<'a> {
fn from(content: &'a AcceptToDeviceEventContent) -> Self {
Self::ToDevice(content)
}
}
impl<'a> From<&'a AcceptEventContent> for AcceptContent<'a> {
fn from(content: &'a AcceptEventContent) -> Self {
Self::Room(content)
}
}
impl<'a> From<&'a OwnedAcceptContent> for AcceptContent<'a> {
fn from(content: &'a OwnedAcceptContent) -> Self {
match content {
OwnedAcceptContent::ToDevice(c) => Self::ToDevice(c),
OwnedAcceptContent::Room(_, c) => Self::Room(c),
}
}
}
#[derive(Clone, Debug)]
pub enum KeyContent<'a> {
ToDevice(&'a KeyToDeviceEventContent),
Room(&'a KeyEventContent),
}
impl KeyContent<'_> {
pub fn flow_id(&self) -> &str {
match self {
Self::ToDevice(c) => &c.transaction_id,
Self::Room(c) => c.relation.event_id.as_str(),
}
}
pub fn public_key(&self) -> &str {
match self {
Self::ToDevice(c) => &c.key,
Self::Room(c) => &c.key,
}
}
}
impl<'a> From<&'a KeyToDeviceEventContent> for KeyContent<'a> {
fn from(content: &'a KeyToDeviceEventContent) -> Self {
Self::ToDevice(content)
}
}
impl<'a> From<&'a KeyEventContent> for KeyContent<'a> {
fn from(content: &'a KeyEventContent) -> Self {
Self::Room(content)
}
}
#[derive(Clone, Debug)]
pub enum MacContent<'a> {
ToDevice(&'a MacToDeviceEventContent),
Room(&'a MacEventContent),
}
impl MacContent<'_> {
pub fn flow_id(&self) -> &str {
match self {
Self::ToDevice(c) => &c.transaction_id,
Self::Room(c) => c.relation.event_id.as_str(),
}
}
pub fn mac(&self) -> &BTreeMap<String, String> {
match self {
Self::ToDevice(c) => &c.mac,
Self::Room(c) => &c.mac,
}
}
pub fn keys(&self) -> &str {
match self {
Self::ToDevice(c) => &c.keys,
Self::Room(c) => &c.keys,
}
}
}
impl<'a> From<&'a MacToDeviceEventContent> for MacContent<'a> {
fn from(content: &'a MacToDeviceEventContent) -> Self {
Self::ToDevice(content)
}
}
impl<'a> From<&'a MacEventContent> for MacContent<'a> {
fn from(content: &'a MacEventContent) -> Self {
Self::Room(content)
}
}
#[derive(Clone, Debug)]
pub enum CancelContent<'a> {
ToDevice(&'a CancelToDeviceEventContent),
Room(&'a CancelEventContent),
}
impl CancelContent<'_> {
pub fn cancel_code(&self) -> &CancelCode {
match self {
Self::ToDevice(c) => &c.code,
Self::Room(c) => &c.code,
}
}
}
impl<'a> From<&'a CancelEventContent> for CancelContent<'a> {
fn from(content: &'a CancelEventContent) -> Self {
Self::Room(content)
}
}
impl<'a> From<&'a CancelToDeviceEventContent> for CancelContent<'a> {
fn from(content: &'a CancelToDeviceEventContent) -> Self {
Self::ToDevice(content)
}
}
#[derive(Clone, Debug)]
pub enum OwnedStartContent {
ToDevice(StartToDeviceEventContent),
Room(RoomId, StartEventContent),
}
impl OwnedStartContent {
pub fn method(&self) -> &StartMethod {
match self {
Self::ToDevice(c) => &c.method,
Self::Room(_, c) => &c.method,
}
}
pub fn method_mut(&mut self) -> &mut StartMethod {
match self {
Self::ToDevice(c) => &mut c.method,
Self::Room(_, c) => &mut c.method,
}
}
pub fn as_start_content(&self) -> StartContent<'_> {
match self {
Self::ToDevice(c) => StartContent::ToDevice(c),
Self::Room(_, c) => StartContent::Room(c),
}
}
pub fn flow_id(&self) -> FlowId {
match self {
Self::ToDevice(c) => FlowId::ToDevice(c.transaction_id.clone()),
Self::Room(r, c) => FlowId::InRoom(r.clone(), c.relation.event_id.clone()),
}
}
pub fn canonical_json(self) -> CanonicalJsonValue {
let content = match self {
Self::ToDevice(c) => serde_json::to_value(c),
Self::Room(_, c) => serde_json::to_value(c),
};
content.expect("Can't serialize content").try_into().expect("Can't canonicalize content")
}
}
impl From<(RoomId, StartEventContent)> for OwnedStartContent {
fn from(tuple: (RoomId, StartEventContent)) -> Self {
Self::Room(tuple.0, tuple.1)
}
}
impl From<StartToDeviceEventContent> for OwnedStartContent {
fn from(content: StartToDeviceEventContent) -> Self {
Self::ToDevice(content)
}
}
#[derive(Clone, Debug)]
pub enum OwnedAcceptContent {
ToDevice(AcceptToDeviceEventContent),
Room(RoomId, AcceptEventContent),
}
impl From<AcceptToDeviceEventContent> for OwnedAcceptContent {
fn from(content: AcceptToDeviceEventContent) -> Self {
Self::ToDevice(content)
}
}
impl From<(RoomId, AcceptEventContent)> for OwnedAcceptContent {
fn from(content: (RoomId, AcceptEventContent)) -> Self {
Self::Room(content.0, content.1)
}
}
impl OwnedAcceptContent {
pub fn method_mut(&mut self) -> &mut AcceptMethod {
match self {
Self::ToDevice(c) => &mut c.method,
Self::Room(_, c) => &mut c.method,
}
}
}

View File

@ -16,20 +16,17 @@ use std::{convert::TryFrom, sync::Arc};
use dashmap::DashMap; use dashmap::DashMap;
use matrix_sdk_common::{ use matrix_sdk_common::{
events::{ identifiers::{DeviceId, EventId, UserId},
room::message::MessageType, AnyMessageEvent, AnySyncMessageEvent, AnySyncRoomEvent,
AnyToDeviceEvent,
},
identifiers::{DeviceId, EventId, RoomId, UserId},
locks::Mutex, locks::Mutex,
uuid::Uuid, uuid::Uuid,
}; };
use tracing::{info, trace, warn}; use tracing::info;
use super::{ use super::{
event_enums::{AnyEvent, AnyVerificationContent},
requests::VerificationRequest, requests::VerificationRequest,
sas::{content_to_request, OutgoingContent, Sas}, sas::{content_to_request, OutgoingContent, Sas},
VerificationResult, FlowId, VerificationResult,
}; };
use crate::{ use crate::{
olm::PrivateCrossSigningIdentity, olm::PrivateCrossSigningIdentity,
@ -59,10 +56,6 @@ impl VerificationCache {
self.room_sas_verifications.is_empty() && self.sas_verification.is_empty() self.room_sas_verifications.is_empty() && self.sas_verification.is_empty()
} }
pub fn get_room_sas(&self, event_id: &EventId) -> Option<Sas> {
self.room_sas_verifications.get(event_id).map(|s| s.clone())
}
pub fn insert_sas(&self, sas: Sas) { pub fn insert_sas(&self, sas: Sas) {
match sas.flow_id() { match sas.flow_id() {
super::FlowId::ToDevice(t) => self.sas_verification.insert(t.to_owned(), sas), super::FlowId::ToDevice(t) => self.sas_verification.insert(t.to_owned(), sas),
@ -194,7 +187,7 @@ impl VerificationMachine {
None, None,
); );
let request = match content.into() { let request = match content {
OutgoingContent::Room(r, c) => { OutgoingContent::Room(r, c) => {
RoomMessageRequest { room_id: r, txn_id: Uuid::new_v4(), content: c }.into() RoomMessageRequest { room_id: r, txn_id: Uuid::new_v4(), content: c }.into()
} }
@ -230,18 +223,6 @@ impl VerificationMachine {
self.verifications.queue_up_content(recipient, recipient_device, content) self.verifications.queue_up_content(recipient, recipient_device, content)
} }
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);
}
}
pub fn mark_request_as_sent(&self, uuid: &Uuid) { pub fn mark_request_as_sent(&self, uuid: &Uuid) {
self.verifications.mark_request_as_sent(uuid); self.verifications.mark_request_as_sent(uuid);
} }
@ -256,220 +237,155 @@ impl VerificationMachine {
} }
} }
pub async fn receive_room_event( async fn mark_sas_as_done(
&self, &self,
room_id: &RoomId, sas: Sas,
event: &AnySyncRoomEvent, out_content: Option<OutgoingContent>,
) -> Result<(), CryptoStoreError> { ) -> Result<(), CryptoStoreError> {
if let AnySyncRoomEvent::Message(m) = event { match sas.mark_as_done().await? {
// Since these are room events we will get events that we send out on VerificationResult::Ok => {
// our own as well. if let Some(c) = out_content {
if m.sender() == self.account.user_id() { self.queue_up_content(sas.other_user_id(), sas.other_device_id(), c);
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(());
} }
VerificationResult::Cancel(c) => {
match m { if let Some(r) = sas.cancel_with_code(c) {
AnySyncMessageEvent::RoomMessage(m) => { self.verifications.add_request(r.into());
if let MessageType::VerificationRequest(r) = &m.content.msgtype {
if self.account.user_id() == &r.to {
info!(
"Received a new verification request from {} {}",
m.sender, r.from_device
);
let request = VerificationRequest::from_room_request(
self.verifications.clone(),
self.account.clone(),
self.private_identity.lock().await.clone(),
self.store.clone(),
&m.sender,
&m.event_id,
room_id,
r,
);
self.requests.insert(request.flow_id().as_str().to_owned(), request);
}
}
}
AnySyncMessageEvent::KeyVerificationReady(e) => {
if let Some(request) = self.requests.get(e.content.relation.event_id.as_str()) {
if &e.sender == request.other_user() {
// TODO remove this unwrap.
request.receive_ready(&e.sender, &e.content).unwrap();
}
}
}
AnySyncMessageEvent::KeyVerificationStart(e) => {
if let Some(request) = self.requests.get(e.content.relation.event_id.as_str()) {
request.receive_start(&e.sender, &e.content).await?
}
}
AnySyncMessageEvent::KeyVerificationKey(e) => {
if let Some(s) = self.verifications.get_room_sas(&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.verifications.get_room_sas(&e.content.relation.event_id) {
self.receive_room_event_helper(
&s,
&m.clone().into_full_event(room_id.clone()),
);
}
} }
}
VerificationResult::SignatureUpload(r) => {
self.verifications.add_request(r.into());
AnySyncMessageEvent::KeyVerificationDone(e) => { if let Some(c) = out_content {
if let Some(s) = self.verifications.get_room_sas(&e.content.relation.event_id) { self.queue_up_content(sas.other_user_id(), sas.other_device_id(), c);
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 => {
if let Some(c) = content {
self.queue_up_content(
s.other_user_id(),
s.other_device_id(),
c,
);
}
}
VerificationResult::Cancel(c) => {
if let Some(r) = s.cancel_with_code(c) {
self.verifications.add_request(r.into());
}
}
VerificationResult::SignatureUpload(r) => {
self.verifications.add_request(r.into());
if let Some(c) = content {
self.queue_up_content(
s.other_user_id(),
s.other_device_id(),
c,
);
}
}
}
}
};
} }
_ => (),
} }
} }
Ok(()) Ok(())
} }
pub async fn receive_event(&self, event: &AnyToDeviceEvent) -> Result<(), CryptoStoreError> { pub async fn receive_any_event(
trace!("Received a key verification event {:?}", event); &self,
event: impl Into<AnyEvent<'_>>,
) -> Result<(), CryptoStoreError> {
let event = event.into();
match event { let flow_id = if let Ok(flow_id) = FlowId::try_from(&event) {
AnyToDeviceEvent::KeyVerificationRequest(e) => { flow_id
let request = VerificationRequest::from_request( } else {
self.verifications.clone(), // This isn't a verification event, return early.
self.account.clone(), return Ok(());
self.private_identity.lock().await.clone(), };
self.store.clone(),
&e.sender,
&e.content,
);
self.requests.insert(request.flow_id().as_str().to_string(), request); if let Some(content) = event.verification_content() {
} match &content {
AnyToDeviceEvent::KeyVerificationReady(e) => { AnyVerificationContent::Request(r) => {
if let Some(request) = self.requests.get(&e.content.transaction_id) { info!(
if &e.sender == request.other_user() { sender = event.sender().as_str(),
// TODO remove this unwrap. from_device = r.from_device().as_str(),
request.receive_ready(&e.sender, &e.content).unwrap(); "Received a new verification request",
}
}
}
AnyToDeviceEvent::KeyVerificationStart(e) => {
trace!(
"Received a m.key.verification start event from {} {}",
e.sender,
e.content.from_device
);
if let Some(verification) = self.get_request(&e.content.transaction_id) {
verification.receive_start(&e.sender, &e.content).await?;
} else if let Some(d) =
self.store.get_device(&e.sender, &e.content.from_device).await?
{
// TODO remove this soon, this has been deprecated by
// MSC3122 https://github.com/matrix-org/matrix-doc/pull/3122
let private_identity = self.private_identity.lock().await.clone();
match Sas::from_start_event(
e.content.clone(),
self.store.clone(),
self.account.clone(),
private_identity,
d,
self.store.get_user_identity(&e.sender).await?,
) {
Ok(s) => {
self.verifications
.sas_verification
.insert(e.content.transaction_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)
}
}
} else {
warn!(
"Received a key verification start event from an unknown device {} {}",
e.sender, e.content.from_device
); );
}
}
AnyToDeviceEvent::KeyVerificationCancel(e) => {
self.verifications.sas_verification.remove(&e.content.transaction_id);
}
AnyToDeviceEvent::KeyVerificationAccept(e) => {
if let Some(s) = self.get_sas(&e.content.transaction_id) {
self.receive_event_helper(&s, event)
};
}
AnyToDeviceEvent::KeyVerificationKey(e) => {
if let Some(s) = self.get_sas(&e.content.transaction_id) {
self.receive_event_helper(&s, event)
};
}
AnyToDeviceEvent::KeyVerificationMac(e) => {
if let Some(s) = self.get_sas(&e.content.transaction_id) {
self.receive_event_helper(&s, event);
if s.is_done() { let request = VerificationRequest::from_request(
match s.mark_as_done().await? { self.verifications.clone(),
VerificationResult::Ok => (), self.account.clone(),
VerificationResult::Cancel(c) => { self.private_identity.lock().await.clone(),
if let Some(r) = s.cancel_with_code(c) { self.store.clone(),
self.verifications.add_request(r.into()); event.sender(),
flow_id,
r,
);
self.requests.insert(request.flow_id().as_str().to_owned(), request);
}
AnyVerificationContent::Cancel(_) => {
todo!()
}
AnyVerificationContent::Ready(c) => {
if let Some(request) = self.requests.get(flow_id.as_str()) {
if request.flow_id() == &flow_id {
// TODO remove this unwrap.
request.receive_ready(event.sender(), c).unwrap();
} else {
todo!()
}
}
}
AnyVerificationContent::Start(c) => {
if let Some(request) = self.requests.get(flow_id.as_str()) {
request.receive_start(event.sender(), &c).await?
} else if let FlowId::ToDevice(_) = flow_id {
// TODO remove this soon, this has been deprecated by
// MSC3122 https://github.com/matrix-org/matrix-doc/pull/3122
if let Some(device) =
self.store.get_device(event.sender(), c.from_device()).await?
{
let private_identity = self.private_identity.lock().await.clone();
let identity = self.store.get_user_identity(event.sender()).await?;
match Sas::from_start_event(
flow_id,
c,
self.store.clone(),
self.account.clone(),
private_identity,
device,
identity,
false,
) {
Ok(sas) => {
self.verifications.insert_sas(sas);
} }
} Err(cancellation) => self.queue_up_content(
VerificationResult::SignatureUpload(r) => { event.sender(),
self.verifications.add_request(r.into()); c.from_device(),
cancellation,
),
} }
} }
} }
}; }
AnyVerificationContent::Accept(_) | AnyVerificationContent::Key(_) => {
if let Some(sas) = self.verifications.get_sas(flow_id.as_str()) {
if sas.flow_id() == &flow_id {
if let Some(content) = sas.receive_any_event(event.sender(), &content) {
self.queue_up_content(
sas.other_user_id(),
sas.other_device_id(),
content,
);
}
} else {
todo!()
}
}
}
AnyVerificationContent::Mac(_) => {
if let Some(s) = self.verifications.get_sas(flow_id.as_str()) {
if s.flow_id() == &flow_id {
let content = s.receive_any_event(event.sender(), &content);
if s.is_done() {
self.mark_sas_as_done(s, content).await?;
}
} else {
todo!()
}
}
}
AnyVerificationContent::Done(_) => {
if let Some(s) = self.verifications.get_sas(flow_id.as_str()) {
let content = s.receive_any_event(event.sender(), &content);
if s.is_done() {
self.mark_sas_as_done(s, content).await?;
}
}
}
} }
_ => (),
} }
Ok(()) Ok(())
} }
} }
@ -491,9 +407,12 @@ mod test {
use super::{Sas, VerificationMachine}; use super::{Sas, VerificationMachine};
use crate::{ use crate::{
olm::PrivateCrossSigningIdentity, olm::PrivateCrossSigningIdentity,
requests::OutgoingRequests,
store::{CryptoStore, MemoryStore}, store::{CryptoStore, MemoryStore},
verification::test::{get_content_from_request, wrap_any_to_device_content}, verification::{
event_enums::{AcceptContent, KeyContent, MacContent},
sas::OutgoingContent,
test::wrap_any_to_device_content,
},
ReadOnlyAccount, ReadOnlyDevice, ReadOnlyAccount, ReadOnlyDevice,
}; };
@ -538,7 +457,7 @@ mod test {
); );
machine machine
.receive_event(&wrap_any_to_device_content(bob_sas.user_id(), start_content.into())) .receive_any_event(&wrap_any_to_device_content(bob_sas.user_id(), start_content.into()))
.await .await
.unwrap(); .unwrap();
@ -559,53 +478,41 @@ 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();
let event = alice let request = alice.accept().unwrap();
.accept()
.map(|c| wrap_any_to_device_content(alice.user_id(), get_content_from_request(&c)))
.unwrap();
let event = bob let content = OutgoingContent::try_from(request).unwrap();
.receive_event(&event) let content = AcceptContent::try_from(&content).unwrap().into();
.map(|c| wrap_any_to_device_content(bob.user_id(), c))
.unwrap(); let content = bob.receive_any_event(alice.user_id(), &content).unwrap();
let event = wrap_any_to_device_content(bob.user_id(), content);
assert!(alice_machine.verifications.outgoing_requests.is_empty()); assert!(alice_machine.verifications.outgoing_requests.is_empty());
alice_machine.receive_event(&event).await.unwrap(); alice_machine.receive_any_event(&event).await.unwrap();
assert!(!alice_machine.verifications.outgoing_requests.is_empty()); assert!(!alice_machine.verifications.outgoing_requests.is_empty());
let request = alice_machine.verifications.outgoing_requests.iter().next().unwrap(); let request = alice_machine.verifications.outgoing_requests.iter().next().unwrap().clone();
let txn_id = *request.request_id(); let txn_id = *request.request_id();
let content = OutgoingContent::try_from(request).unwrap();
let content = KeyContent::try_from(&content).unwrap().into();
let r = if let OutgoingRequests::ToDeviceRequest(r) = request.request() {
r.clone()
} else {
panic!("Invalid request type");
};
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); alice_machine.mark_request_as_sent(&txn_id);
assert!(bob.receive_event(&event).is_none()); assert!(bob.receive_any_event(alice.user_id(), &content).is_none());
assert!(alice.emoji().is_some()); assert!(alice.emoji().is_some());
assert!(bob.emoji().is_some()); assert!(bob.emoji().is_some());
assert_eq!(alice.emoji(), bob.emoji()); assert_eq!(alice.emoji(), bob.emoji());
let event = wrap_any_to_device_content( let request = alice.confirm().await.unwrap().0.unwrap();
alice.user_id(), let content = OutgoingContent::try_from(request).unwrap();
get_content_from_request(&alice.confirm().await.unwrap().0.unwrap()), let content = MacContent::try_from(&content).unwrap().into();
); bob.receive_any_event(alice.user_id(), &content);
bob.receive_event(&event);
let event = wrap_any_to_device_content( let request = bob.confirm().await.unwrap().0.unwrap();
bob.user_id(), let content = OutgoingContent::try_from(request).unwrap();
get_content_from_request(&bob.confirm().await.unwrap().0.unwrap()), let content = MacContent::try_from(&content).unwrap().into();
); alice.receive_any_event(bob.user_id(), &content);
alice.receive_event(&event);
assert!(alice.is_done()); assert!(alice.is_done());
assert!(bob.is_done()); assert!(bob.is_done());

View File

@ -12,26 +12,30 @@
// 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.
mod event_enums;
mod machine; mod machine;
mod requests; mod requests;
mod sas; mod sas;
use std::sync::Arc; use std::sync::Arc;
pub use machine::{VerificationCache, VerificationMachine};
use matrix_sdk_common::{ use matrix_sdk_common::{
api::r0::keys::upload_signatures::Request as SignatureUploadRequest, api::r0::keys::upload_signatures::Request as SignatureUploadRequest,
events::key::verification::{ events::{
cancel::{CancelCode, CancelEventContent, CancelToDeviceEventContent}, key::verification::{
Relation, cancel::{CancelCode, CancelEventContent, CancelToDeviceEventContent},
Relation,
},
AnyMessageEventContent, AnyToDeviceEventContent,
}, },
identifiers::{DeviceId, EventId, RoomId, UserId}, identifiers::{DeviceId, EventId, RoomId, UserId},
}; };
pub use machine::{VerificationCache, VerificationMachine};
pub use requests::VerificationRequest; pub use requests::VerificationRequest;
pub use sas::{AcceptSettings, Sas}; pub use sas::{AcceptSettings, Sas};
use tracing::{error, info, trace, warn}; use tracing::{error, info, trace, warn};
use self::sas::OutgoingContent;
use crate::{ use crate::{
error::SignatureError, error::SignatureError,
olm::PrivateCrossSigningIdentity, olm::PrivateCrossSigningIdentity,
@ -39,8 +43,6 @@ use crate::{
CryptoStoreError, LocalTrust, ReadOnlyDevice, UserIdentities, CryptoStoreError, LocalTrust, ReadOnlyDevice, UserIdentities,
}; };
use self::sas::CancelContent;
/// The verification state indicating that the verification finished /// The verification state indicating that the verification finished
/// successfully. /// successfully.
/// ///
@ -82,22 +84,24 @@ impl Cancelled {
Self { cancel_code: code, reason } Self { cancel_code: code, reason }
} }
pub fn as_content(&self, flow_id: &FlowId) -> CancelContent { pub fn as_content(&self, flow_id: &FlowId) -> OutgoingContent {
match flow_id { match flow_id {
FlowId::ToDevice(s) => CancelToDeviceEventContent::new( FlowId::ToDevice(s) => {
s.clone(), AnyToDeviceEventContent::KeyVerificationCancel(CancelToDeviceEventContent::new(
self.reason.to_string(), s.clone(),
self.cancel_code.clone(), self.reason.to_string(),
) self.cancel_code.clone(),
.into(), ))
.into()
}
FlowId::InRoom(r, e) => ( FlowId::InRoom(r, e) => (
r.clone(), r.clone(),
CancelEventContent::new( AnyMessageEventContent::KeyVerificationCancel(CancelEventContent::new(
self.reason.to_string(), self.reason.to_string(),
self.cancel_code.clone(), self.cancel_code.clone(),
Relation::new(e.clone()), Relation::new(e.clone()),
), )),
) )
.into(), .into(),
} }
@ -139,6 +143,12 @@ impl From<(RoomId, EventId)> for FlowId {
} }
} }
impl From<(&RoomId, &EventId)> for FlowId {
fn from(ids: (&RoomId, &EventId)) -> Self {
FlowId::InRoom(ids.0.to_owned(), ids.1.to_owned())
}
}
/// A result of a verification flow. /// A result of a verification flow.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub enum VerificationResult { pub enum VerificationResult {
@ -158,7 +168,6 @@ pub struct IdentitiesBeingVerified {
identity_being_verified: Option<UserIdentities>, identity_being_verified: Option<UserIdentities>,
} }
#[allow(dead_code)]
impl IdentitiesBeingVerified { impl IdentitiesBeingVerified {
fn user_id(&self) -> &UserId { fn user_id(&self) -> &UserId {
self.private_identity.user_id() self.private_identity.user_id()
@ -298,7 +307,10 @@ impl IdentitiesBeingVerified {
.map_or(false, |i| i.master_key() == identity.master_key()) .map_or(false, |i| i.master_key() == identity.master_key())
{ {
if verified_identities.map_or(false, |i| i.contains(&identity)) { if verified_identities.map_or(false, |i| i.contains(&identity)) {
trace!("Marking user identity of {} as verified.", identity.user_id(),); trace!(
user_id = self.other_user_id().as_str(),
"Marking the user identity of as verified."
);
if let UserIdentities::Own(i) = &identity { if let UserIdentities::Own(i) = &identity {
i.mark_as_verified(); i.mark_as_verified();
@ -307,28 +319,28 @@ impl IdentitiesBeingVerified {
Ok(Some(identity)) Ok(Some(identity))
} else { } else {
info!( info!(
user_id = self.other_user_id().as_str(),
"The interactive verification process didn't verify \ "The interactive verification process didn't verify \
the user identity of {} {:?}", the user identity of the user that participated in \
identity.user_id(), the interactive verification",
verified_identities,
); );
Ok(None) Ok(None)
} }
} else { } else {
warn!( warn!(
"The master keys of {} have changed while an interactive \ user_id = self.other_user_id().as_str(),
"The master keys of the user have changed while an interactive \
verification was going on, not marking the identity as verified.", verification was going on, not marking the identity as verified.",
identity.user_id(),
); );
Ok(None) Ok(None)
} }
} else { } else {
info!( info!(
"The identity for {} was deleted while an interactive \ user_id = self.other_user_id().as_str(),
"The identity of the user was deleted while an interactive \
verification was going on.", verification was going on.",
self.other_user_id(),
); );
Ok(None) Ok(None)
} }

View File

@ -14,19 +14,15 @@
#![allow(dead_code)] #![allow(dead_code)]
use std::{ use std::sync::{Arc, Mutex};
convert::TryFrom,
sync::{Arc, Mutex},
};
use matrix_sdk_common::{ use matrix_sdk_common::{
api::r0::to_device::DeviceIdOrAllDevices, api::r0::to_device::DeviceIdOrAllDevices,
events::{ events::{
key::verification::{ key::verification::{
done::{DoneEventContent, DoneToDeviceEventContent},
ready::{ReadyEventContent, ReadyToDeviceEventContent}, ready::{ReadyEventContent, ReadyToDeviceEventContent},
request::RequestToDeviceEventContent, request::RequestToDeviceEventContent,
start::{StartEventContent, StartMethod, StartToDeviceEventContent}, start::StartMethod,
Relation, VerificationMethod, Relation, VerificationMethod,
}, },
room::message::KeyVerificationRequestEventContent, room::message::KeyVerificationRequestEventContent,
@ -36,10 +32,11 @@ use matrix_sdk_common::{
uuid::Uuid, uuid::Uuid,
MilliSecondsSinceUnixEpoch, MilliSecondsSinceUnixEpoch,
}; };
use tracing::{info, trace, warn}; use tracing::{info, warn};
use super::{ use super::{
sas::{content_to_request, OutgoingContent, StartContent as OwnedStartContent}, event_enums::{ReadyContent, RequestContent, StartContent},
sas::{content_to_request, OutgoingContent},
FlowId, VerificationCache, FlowId, VerificationCache,
}; };
use crate::{ use crate::{
@ -51,184 +48,6 @@ use crate::{
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(())
}
}
}
}
}
pub enum StartContent<'a> {
ToDevice(&'a StartToDeviceEventContent),
Room(&'a StartEventContent),
}
impl<'a> StartContent<'a> {
pub fn from_device(&self) -> &DeviceId {
match self {
StartContent::ToDevice(c) => &c.from_device,
StartContent::Room(c) => &c.from_device,
}
}
pub fn flow_id(&self) -> &str {
match self {
StartContent::ToDevice(c) => &c.transaction_id,
StartContent::Room(c) => &c.relation.event_id.as_str(),
}
}
pub fn method(&self) -> &StartMethod {
match self {
StartContent::ToDevice(c) => &c.method,
StartContent::Room(c) => &c.method,
}
}
}
impl<'a> From<&'a StartEventContent> for StartContent<'a> {
fn from(c: &'a StartEventContent) -> Self {
Self::Room(c)
}
}
impl<'a> From<&'a StartToDeviceEventContent> for StartContent<'a> {
fn from(c: &'a StartToDeviceEventContent) -> Self {
Self::ToDevice(c)
}
}
impl<'a> TryFrom<&'a OutgoingContent> for StartContent<'a> {
type Error = ();
fn try_from(value: &'a OutgoingContent) -> Result<Self, Self::Error> {
match value {
OutgoingContent::Room(_, c) => {
if let AnyMessageEventContent::KeyVerificationStart(c) = c {
Ok(StartContent::Room(c))
} else {
Err(())
}
}
OutgoingContent::ToDevice(c) => {
if let AnyToDeviceEventContent::KeyVerificationStart(c) = c {
Ok(StartContent::ToDevice(c))
} else {
Err(())
}
}
}
}
}
pub enum DoneContent<'a> {
ToDevice(&'a DoneToDeviceEventContent),
Room(&'a DoneEventContent),
}
impl<'a> From<&'a DoneEventContent> for DoneContent<'a> {
fn from(c: &'a DoneEventContent) -> Self {
Self::Room(c)
}
}
impl<'a> From<&'a DoneToDeviceEventContent> for DoneContent<'a> {
fn from(c: &'a DoneToDeviceEventContent) -> Self {
Self::ToDevice(c)
}
}
impl<'a> DoneContent<'a> {
pub fn flow_id(&self) -> &str {
match self {
Self::ToDevice(c) => &c.transaction_id,
Self::Room(c) => &c.relation.event_id.as_str(),
}
}
}
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
/// TODO /// TODO
pub struct VerificationRequest { pub struct VerificationRequest {
@ -311,56 +130,14 @@ impl VerificationRequest {
&self.flow_id &self.flow_id
} }
pub(crate) fn from_room_request(
cache: VerificationCache,
account: ReadOnlyAccount,
private_cross_signing_identity: PrivateCrossSigningIdentity,
store: Arc<Box<dyn CryptoStore>>,
sender: &UserId,
event_id: &EventId,
room_id: &RoomId,
content: &KeyVerificationRequestEventContent,
) -> Self {
let flow_id = FlowId::from((room_id.to_owned(), event_id.to_owned()));
Self::from_helper(
cache,
account,
private_cross_signing_identity,
store,
sender,
flow_id,
content.into(),
)
}
pub(crate) fn from_request( pub(crate) fn from_request(
cache: VerificationCache,
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(
cache,
account,
private_cross_signing_identity,
store,
sender,
flow_id,
content.into(),
)
}
fn from_helper(
cache: VerificationCache, cache: VerificationCache,
account: ReadOnlyAccount, account: ReadOnlyAccount,
private_cross_signing_identity: PrivateCrossSigningIdentity, private_cross_signing_identity: PrivateCrossSigningIdentity,
store: Arc<Box<dyn CryptoStore>>, store: Arc<Box<dyn CryptoStore>>,
sender: &UserId, sender: &UserId,
flow_id: FlowId, flow_id: FlowId,
content: RequestContent, content: &RequestContent,
) -> Self { ) -> Self {
Self { Self {
verification_cache: cache.clone(), verification_cache: cache.clone(),
@ -394,13 +171,8 @@ impl VerificationRequest {
} }
#[allow(clippy::unnecessary_wraps)] #[allow(clippy::unnecessary_wraps)]
pub(crate) fn receive_ready<'a>( pub(crate) fn receive_ready(&self, sender: &UserId, content: &ReadyContent) -> Result<(), ()> {
&self,
sender: &UserId,
content: impl Into<ReadyContent<'a>>,
) -> Result<(), ()> {
let mut inner = self.inner.lock().unwrap(); let mut inner = self.inner.lock().unwrap();
let content = content.into();
if let InnerRequest::Created(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));
@ -409,13 +181,11 @@ impl VerificationRequest {
Ok(()) Ok(())
} }
pub(crate) async fn receive_start<'a>( pub(crate) async fn receive_start(
&self, &self,
sender: &UserId, sender: &UserId,
content: impl Into<StartContent<'a>>, content: &StartContent<'_>,
) -> Result<(), CryptoStoreError> { ) -> Result<(), CryptoStoreError> {
let content = content.into();
let inner = self.inner.lock().unwrap().clone(); let inner = self.inner.lock().unwrap().clone();
if let InnerRequest::Ready(s) = inner { if let InnerRequest::Ready(s) = inner {
@ -502,9 +272,9 @@ impl InnerRequest {
} }
} }
fn to_started_sas<'a>( fn to_started_sas(
&self, &self,
content: impl Into<StartContent<'a>>, content: &StartContent,
other_device: ReadOnlyDevice, other_device: ReadOnlyDevice,
other_identity: Option<UserIdentities>, other_identity: Option<UserIdentities>,
) -> Result<Option<Sas>, OutgoingContent> { ) -> Result<Option<Sas>, OutgoingContent> {
@ -551,7 +321,7 @@ impl RequestState<Created> {
} }
} }
fn into_ready(self, _sender: &UserId, content: ReadyContent) -> RequestState<Ready> { fn into_ready(self, _sender: &UserId, content: &ReadyContent) -> 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 {
account: self.account, account: self.account,
@ -600,7 +370,7 @@ impl RequestState<Requested> {
store: Arc<Box<dyn CryptoStore>>, store: Arc<Box<dyn CryptoStore>>,
sender: &UserId, sender: &UserId,
flow_id: &FlowId, flow_id: &FlowId,
content: RequestContent, 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 {
@ -673,39 +443,27 @@ struct Ready {
impl RequestState<Ready> { impl RequestState<Ready> {
fn to_started_sas<'a>( fn to_started_sas<'a>(
&self, &self,
content: impl Into<StartContent<'a>>, content: &StartContent<'a>,
other_device: ReadOnlyDevice, other_device: ReadOnlyDevice,
other_identity: Option<UserIdentities>, other_identity: Option<UserIdentities>,
) -> Result<Sas, OutgoingContent> { ) -> Result<Sas, OutgoingContent> {
let content: OwnedStartContent = match content.into() {
StartContent::Room(c) => {
if let FlowId::InRoom(r, _) = &*self.flow_id {
(r.to_owned(), c.to_owned()).into()
} else {
// TODO cancel here
panic!("Missmatch between content and flow id");
}
}
StartContent::ToDevice(c) => c.clone().into(),
};
Sas::from_start_event( Sas::from_start_event(
(&*self.flow_id).to_owned(),
content, content,
self.store.clone(), self.store.clone(),
self.account.clone(), self.account.clone(),
self.private_cross_signing_identity.clone(), self.private_cross_signing_identity.clone(),
other_device, other_device,
other_identity, other_identity,
true,
) )
} }
async fn receive_start<'a>( async fn receive_start(
&self, &self,
sender: &UserId, sender: &UserId,
content: impl Into<StartContent<'a>>, content: &StartContent<'_>,
) -> Result<(), CryptoStoreError> { ) -> Result<(), CryptoStoreError> {
let content = content.into();
info!( info!(
sender = sender.as_str(), sender = sender.as_str(),
device = content.from_device().as_str(), device = content.from_device().as_str(),
@ -747,7 +505,7 @@ impl RequestState<Ready> {
} }
}, },
m => { m => {
warn!(method =? m, "Received a key verificaton start event with an unknown method") warn!(method =? m, "Received a key verificaton start event with an unsupported method")
} }
} }
@ -772,7 +530,7 @@ impl RequestState<Ready> {
other_identity, other_identity,
Some(t), Some(t),
); );
(sas, content.into()) (sas, content)
} }
FlowId::InRoom(r, e) => { FlowId::InRoom(r, e) => {
let (sas, content) = Sas::start_in_room( let (sas, content) = Sas::start_in_room(
@ -784,7 +542,7 @@ impl RequestState<Ready> {
store, store,
other_identity, other_identity,
); );
(sas, content.into()) (sas, content)
} }
} }
} }
@ -807,11 +565,15 @@ mod test {
use matrix_sdk_common::identifiers::{event_id, room_id, DeviceIdBox, UserId}; use matrix_sdk_common::identifiers::{event_id, room_id, DeviceIdBox, UserId};
use matrix_sdk_test::async_test; use matrix_sdk_test::async_test;
use super::{StartContent, VerificationRequest}; use super::VerificationRequest;
use crate::{ use crate::{
olm::{PrivateCrossSigningIdentity, ReadOnlyAccount}, olm::{PrivateCrossSigningIdentity, ReadOnlyAccount},
store::{Changes, CryptoStore, MemoryStore}, store::{Changes, CryptoStore, MemoryStore},
verification::{requests::ReadyContent, sas::OutgoingContent, VerificationCache}, verification::{
event_enums::{ReadyContent, StartContent},
sas::OutgoingContent,
FlowId, VerificationCache,
},
ReadOnlyDevice, ReadOnlyDevice,
}; };
@ -856,21 +618,22 @@ mod test {
&alice_id(), &alice_id(),
); );
let alice_request = VerificationRequest::from_room_request( let flow_id = FlowId::from((room_id, event_id));
let alice_request = VerificationRequest::from_request(
VerificationCache::new(), VerificationCache::new(),
alice, alice,
alice_identity, alice_identity,
alice_store.into(), alice_store.into(),
&bob_id(), &bob_id(),
&event_id, flow_id,
&room_id, &(&content).into(),
&content,
); );
let content: OutgoingContent = alice_request.accept().unwrap().into(); let content: OutgoingContent = alice_request.accept().unwrap().into();
let content = ReadyContent::try_from(&content).unwrap(); let content = ReadyContent::try_from(&content).unwrap();
bob_request.receive_ready(&alice_id(), content).unwrap(); 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());
@ -908,21 +671,22 @@ mod test {
&alice_id(), &alice_id(),
); );
let alice_request = VerificationRequest::from_room_request( let flow_id = FlowId::from((room_id, event_id));
let alice_request = VerificationRequest::from_request(
VerificationCache::new(), VerificationCache::new(),
alice, alice,
alice_identity, alice_identity,
alice_store.into(), alice_store.into(),
&bob_id(), &bob_id(),
&event_id, flow_id,
&room_id, &(&content).into(),
&content,
); );
let content: OutgoingContent = alice_request.accept().unwrap().into(); let content: OutgoingContent = alice_request.accept().unwrap().into();
let content = ReadyContent::try_from(&content).unwrap(); let content = ReadyContent::try_from(&content).unwrap();
bob_request.receive_ready(&alice_id(), content).unwrap(); 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());
@ -931,7 +695,7 @@ mod test {
let content = StartContent::try_from(&start_content).unwrap(); let content = StartContent::try_from(&start_content).unwrap();
let flow_id = content.flow_id().to_owned(); let flow_id = content.flow_id().to_owned();
alice_request.receive_start(bob_device.user_id(), content).await.unwrap(); alice_request.receive_start(bob_device.user_id(), &content).await.unwrap();
let alice_sas = alice_request.verification_cache.get_sas(&flow_id).unwrap(); let alice_sas = alice_request.verification_cache.get_sas(&flow_id).unwrap();
assert!(!bob_sas.is_cancelled()); assert!(!bob_sas.is_cancelled());

View File

@ -12,254 +12,26 @@
// 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 std::{collections::BTreeMap, convert::TryInto};
use matrix_sdk_common::{ use matrix_sdk_common::{
events::{ events::{AnyMessageEventContent, AnyToDeviceEventContent},
key::verification::{
accept::{AcceptEventContent, AcceptMethod, AcceptToDeviceEventContent},
cancel::{CancelEventContent, CancelToDeviceEventContent},
done::DoneEventContent,
key::{KeyEventContent, KeyToDeviceEventContent},
mac::{MacEventContent, MacToDeviceEventContent},
start::{StartEventContent, StartMethod, StartToDeviceEventContent},
},
AnyMessageEventContent, AnyToDeviceEventContent,
},
identifiers::RoomId, identifiers::RoomId,
CanonicalJsonValue,
}; };
use super::FlowId;
#[derive(Clone, Debug)]
pub enum StartContent {
ToDevice(StartToDeviceEventContent),
Room(RoomId, StartEventContent),
}
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(r, c) => FlowId::InRoom(r.clone(), c.relation.event_id.clone()),
}
}
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),
};
content.expect("Can't serialize content").try_into().expect("Can't canonicalize content")
}
}
impl From<(RoomId, StartEventContent)> for StartContent {
fn from(tuple: (RoomId, StartEventContent)) -> Self {
StartContent::Room(tuple.0, tuple.1)
}
}
impl From<StartToDeviceEventContent> for StartContent {
fn from(content: StartToDeviceEventContent) -> Self {
StartContent::ToDevice(content)
}
}
#[derive(Clone, Debug)]
pub enum AcceptContent {
ToDevice(AcceptToDeviceEventContent),
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<AcceptToDeviceEventContent> 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 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<KeyToDeviceEventContent> 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)
}
}
#[derive(Clone, Debug)]
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<String, String> {
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<MacToDeviceEventContent> 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)
}
}
#[derive(Clone, Debug)]
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<CancelToDeviceEventContent> for CancelContent {
fn from(content: CancelToDeviceEventContent) -> Self {
CancelContent::ToDevice(content)
}
}
#[derive(Clone, Debug)]
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)] #[derive(Clone, Debug)]
pub enum OutgoingContent { pub enum OutgoingContent {
Room(RoomId, AnyMessageEventContent), Room(RoomId, AnyMessageEventContent),
ToDevice(AnyToDeviceEventContent), ToDevice(AnyToDeviceEventContent),
} }
impl From<StartContent> for OutgoingContent { impl From<OwnedStartContent> for OutgoingContent {
fn from(content: StartContent) -> Self { fn from(content: OwnedStartContent) -> Self {
match content { match content {
StartContent::Room(r, c) => (r, AnyMessageEventContent::KeyVerificationStart(c)).into(), OwnedStartContent::Room(r, c) => {
StartContent::ToDevice(c) => AnyToDeviceEventContent::KeyVerificationStart(c).into(), (r, AnyMessageEventContent::KeyVerificationStart(c)).into()
} }
} OwnedStartContent::ToDevice(c) => {
} AnyToDeviceEventContent::KeyVerificationStart(c).into()
impl From<CancelContent> 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<KeyContent> 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<DoneContent> for OutgoingContent {
fn from(content: DoneContent) -> Self {
match content {
DoneContent::Room(r, c) => (r, AnyMessageEventContent::KeyVerificationDone(c)).into(),
} }
} }
} }
@ -277,62 +49,98 @@ impl From<(RoomId, AnyMessageEventContent)> for OutgoingContent {
} }
#[cfg(test)] #[cfg(test)]
use crate::OutgoingVerificationRequest; use std::convert::TryFrom;
use crate::verification::event_enums::OwnedStartContent;
#[cfg(test)]
use crate::{OutgoingRequest, OutgoingVerificationRequest, RoomMessageRequest, ToDeviceRequest};
#[cfg(test)] #[cfg(test)]
impl From<OutgoingVerificationRequest> for OutgoingContent { impl From<OutgoingVerificationRequest> for OutgoingContent {
fn from(request: OutgoingVerificationRequest) -> Self { fn from(request: OutgoingVerificationRequest) -> Self {
use matrix_sdk_common::events::EventType;
use serde_json::Value;
match request { match request {
OutgoingVerificationRequest::ToDevice(r) => { OutgoingVerificationRequest::ToDevice(r) => Self::try_from(r).unwrap(),
let json: Value = serde_json::from_str( OutgoingVerificationRequest::InRoom(r) => Self::from(r),
r.messages.values().next().unwrap().values().next().unwrap().get(), }
) }
.unwrap(); }
match r.event_type { #[cfg(test)]
EventType::KeyVerificationRequest => { impl From<RoomMessageRequest> for OutgoingContent {
AnyToDeviceEventContent::KeyVerificationRequest( fn from(value: RoomMessageRequest) -> Self {
serde_json::from_value(json).unwrap(), (value.room_id, value.content).into()
) }
} }
EventType::KeyVerificationReady => {
AnyToDeviceEventContent::KeyVerificationReady( #[cfg(test)]
serde_json::from_value(json).unwrap(), impl TryFrom<ToDeviceRequest> for OutgoingContent {
) type Error = ();
}
EventType::KeyVerificationDone => AnyToDeviceEventContent::KeyVerificationDone( fn try_from(value: ToDeviceRequest) -> Result<Self, Self::Error> {
serde_json::from_value(json).unwrap(), use matrix_sdk_common::events::EventType;
), use serde_json::Value;
EventType::KeyVerificationStart => {
AnyToDeviceEventContent::KeyVerificationStart( let json: Value = serde_json::from_str(
serde_json::from_value(json).unwrap(), value
) .messages
} .values()
EventType::KeyVerificationKey => AnyToDeviceEventContent::KeyVerificationKey( .next()
serde_json::from_value(json).unwrap(), .and_then(|m| m.values().next().map(|j| j.get()))
), .ok_or(())?,
EventType::KeyVerificationAccept => { )
AnyToDeviceEventContent::KeyVerificationAccept( .map_err(|_| ())?;
serde_json::from_value(json).unwrap(),
) match value.event_type {
} EventType::KeyVerificationRequest => {
EventType::KeyVerificationMac => AnyToDeviceEventContent::KeyVerificationMac( Ok(AnyToDeviceEventContent::KeyVerificationRequest(
serde_json::from_value(json).unwrap(), serde_json::from_value(json).map_err(|_| ())?,
), )
EventType::KeyVerificationCancel => { .into())
AnyToDeviceEventContent::KeyVerificationCancel( }
serde_json::from_value(json).unwrap(), EventType::KeyVerificationReady => Ok(AnyToDeviceEventContent::KeyVerificationReady(
) serde_json::from_value(json).map_err(|_| ())?,
} )
_ => unreachable!(), .into()),
} EventType::KeyVerificationDone => Ok(AnyToDeviceEventContent::KeyVerificationDone(
.into() serde_json::from_value(json).map_err(|_| ())?,
} )
.into()),
OutgoingVerificationRequest::InRoom(r) => (r.room_id, r.content).into(), EventType::KeyVerificationStart => Ok(AnyToDeviceEventContent::KeyVerificationStart(
serde_json::from_value(json).map_err(|_| ())?,
)
.into()),
EventType::KeyVerificationKey => Ok(AnyToDeviceEventContent::KeyVerificationKey(
serde_json::from_value(json).map_err(|_| ())?,
)
.into()),
EventType::KeyVerificationAccept => Ok(AnyToDeviceEventContent::KeyVerificationAccept(
serde_json::from_value(json).map_err(|_| ())?,
)
.into()),
EventType::KeyVerificationMac => Ok(AnyToDeviceEventContent::KeyVerificationMac(
serde_json::from_value(json).map_err(|_| ())?,
)
.into()),
EventType::KeyVerificationCancel => Ok(AnyToDeviceEventContent::KeyVerificationCancel(
serde_json::from_value(json).map_err(|_| ())?,
)
.into()),
_ => Err(()),
}
}
}
#[cfg(test)]
impl TryFrom<OutgoingRequest> for OutgoingContent {
type Error = ();
fn try_from(value: OutgoingRequest) -> Result<Self, ()> {
match value.request() {
crate::OutgoingRequests::KeysUpload(_) => Err(()),
crate::OutgoingRequests::KeysQuery(_) => Err(()),
crate::OutgoingRequests::ToDeviceRequest(r) => Self::try_from(r.clone()),
crate::OutgoingRequests::SignatureUpload(_) => Err(()),
crate::OutgoingRequests::RoomMessage(r) => Ok(Self::from(r.clone())),
} }
} }
} }

View File

@ -22,7 +22,7 @@ use matrix_sdk_common::{
mac::{MacEventContent, MacToDeviceEventContent}, mac::{MacEventContent, MacToDeviceEventContent},
Relation, Relation,
}, },
AnyToDeviceEventContent, EventType, AnyMessageEventContent, AnyToDeviceEventContent, EventType,
}, },
identifiers::{DeviceKeyAlgorithm, DeviceKeyId, UserId}, identifiers::{DeviceKeyAlgorithm, DeviceKeyId, UserId},
uuid::Uuid, uuid::Uuid,
@ -31,13 +31,11 @@ use olm_rs::sas::OlmSas;
use sha2::{Digest, Sha256}; use sha2::{Digest, Sha256};
use tracing::{trace, warn}; use tracing::{trace, warn};
use super::{ use super::{FlowId, OutgoingContent};
event_enums::{MacContent, StartContent},
FlowId,
};
use crate::{ use crate::{
identities::{ReadOnlyDevice, UserIdentities}, identities::{ReadOnlyDevice, UserIdentities},
utilities::encode, utilities::encode,
verification::event_enums::{MacContent, StartContent},
ReadOnlyAccount, ToDeviceRequest, ReadOnlyAccount, ToDeviceRequest,
}; };
@ -58,8 +56,8 @@ pub struct SasIds {
/// ///
/// * `content` - The `m.key.verification.start` event content that started the /// * `content` - The `m.key.verification.start` event content that started the
/// interactive verification process. /// interactive verification process.
pub fn calculate_commitment(public_key: &str, content: impl Into<StartContent>) -> String { pub fn calculate_commitment(public_key: &str, content: &StartContent) -> String {
let content = content.into().canonical_json(); let content = content.canonical_json();
let content_string = content.to_string(); let content_string = content.to_string();
encode(Sha256::new().chain(&public_key).chain(&content_string).finalize()) encode(Sha256::new().chain(&public_key).chain(&content_string).finalize())
@ -199,7 +197,7 @@ pub fn receive_mac_event(
); );
let mut keys = content.mac().keys().map(|k| k.as_str()).collect::<Vec<_>>(); let mut keys = content.mac().keys().map(|k| k.as_str()).collect::<Vec<_>>();
keys.sort(); keys.sort_unstable();
let keys = sas let keys = sas
.calculate_mac(&keys.join(","), &format!("{}KEY_IDS", &info)) .calculate_mac(&keys.join(","), &format!("{}KEY_IDS", &info))
@ -296,7 +294,7 @@ fn extra_mac_info_send(ids: &SasIds, flow_id: &str) -> String {
/// # Panics /// # Panics
/// ///
/// This will panic if the public key of the other side wasn't set. /// 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) -> MacContent { pub fn get_mac_content(sas: &OlmSas, ids: &SasIds, flow_id: &FlowId) -> OutgoingContent {
let mut mac: BTreeMap<String, String> = BTreeMap::new(); let mut mac: BTreeMap<String, String> = BTreeMap::new();
let key_id = DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, ids.account.device_id()); let key_id = DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, ids.account.device_id());
@ -317,10 +315,19 @@ pub fn get_mac_content(sas: &OlmSas, ids: &SasIds, flow_id: &FlowId) -> MacConte
.expect("Can't calculate SAS MAC"); .expect("Can't calculate SAS MAC");
match flow_id { match flow_id {
FlowId::ToDevice(s) => MacToDeviceEventContent::new(s.to_string(), mac, keys).into(), FlowId::ToDevice(s) => AnyToDeviceEventContent::KeyVerificationMac(
FlowId::InRoom(r, e) => { MacToDeviceEventContent::new(s.to_string(), mac, keys),
(r.clone(), MacEventContent::new(mac, keys, Relation::new(e.clone()))).into() )
} .into(),
FlowId::InRoom(r, e) => (
r.clone(),
AnyMessageEventContent::KeyVerificationMac(MacEventContent::new(
mac,
keys,
Relation::new(e.clone()),
)),
)
.into(),
} }
} }
@ -558,6 +565,7 @@ mod test {
bytes_to_decimal, bytes_to_emoji, bytes_to_emoji_index, calculate_commitment, bytes_to_decimal, bytes_to_emoji, bytes_to_emoji_index, calculate_commitment,
emoji_from_index, emoji_from_index,
}; };
use crate::verification::event_enums::StartContent;
#[test] #[test]
fn commitment_calculation() { fn commitment_calculation() {
@ -575,7 +583,8 @@ mod test {
}); });
let content: StartToDeviceEventContent = serde_json::from_value(content).unwrap(); let content: StartToDeviceEventContent = serde_json::from_value(content).unwrap();
let calculated_commitment = calculate_commitment(public_key, content); let content = StartContent::from(&content);
let calculated_commitment = calculate_commitment(public_key, &content);
assert_eq!(commitment, &calculated_commitment); assert_eq!(commitment, &calculated_commitment);
} }

View File

@ -17,23 +17,23 @@ use std::sync::Arc;
use std::time::Instant; use std::time::Instant;
use matrix_sdk_common::{ use matrix_sdk_common::{
events::{ events::key::verification::{cancel::CancelCode, ShortAuthenticationString},
key::verification::{cancel::CancelCode, ShortAuthenticationString}, identifiers::{EventId, RoomId, UserId},
AnyMessageEvent, AnyToDeviceEvent,
},
identifiers::{EventId, RoomId},
}; };
use super::{ use super::{
event_enums::{AcceptContent, CancelContent, MacContent, OutgoingContent}, event_enums::OutgoingContent,
sas_state::{ sas_state::{
Accepted, Confirmed, Created, KeyReceived, MacReceived, SasState, Started, WaitingForDone, Accepted, Confirmed, Created, KeyReceived, MacReceived, SasState, Started, WaitingForDone,
}, },
FlowId, StartContent, FlowId,
}; };
use crate::{ use crate::{
identities::{ReadOnlyDevice, UserIdentities}, identities::{ReadOnlyDevice, UserIdentities},
verification::{Cancelled, Done}, verification::{
event_enums::{AnyVerificationContent, OwnedAcceptContent, StartContent},
Cancelled, Done,
},
ReadOnlyAccount, ReadOnlyAccount,
}; };
@ -57,10 +57,10 @@ impl InnerSas {
other_device: ReadOnlyDevice, other_device: ReadOnlyDevice,
other_identity: Option<UserIdentities>, other_identity: Option<UserIdentities>,
transaction_id: Option<String>, transaction_id: Option<String>,
) -> (InnerSas, StartContent) { ) -> (InnerSas, OutgoingContent) {
let sas = SasState::<Created>::new(account, other_device, other_identity, transaction_id); 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.into())
} }
pub fn supports_emoji(&self) -> bool { pub fn supports_emoji(&self) -> bool {
@ -100,7 +100,7 @@ impl InnerSas {
account: ReadOnlyAccount, account: ReadOnlyAccount,
other_device: ReadOnlyDevice, other_device: ReadOnlyDevice,
other_identity: Option<UserIdentities>, other_identity: Option<UserIdentities>,
) -> (InnerSas, StartContent) { ) -> (InnerSas, OutgoingContent) {
let sas = SasState::<Created>::new_in_room( let sas = SasState::<Created>::new_in_room(
room_id, room_id,
event_id, event_id,
@ -109,23 +109,31 @@ impl InnerSas {
other_identity, other_identity,
); );
let content = sas.as_content(); let content = sas.as_content();
(InnerSas::Created(sas), content) (InnerSas::Created(sas), content.into())
} }
pub fn from_start_event( pub fn from_start_event(
account: ReadOnlyAccount, account: ReadOnlyAccount,
other_device: ReadOnlyDevice, other_device: ReadOnlyDevice,
content: impl Into<StartContent>, flow_id: FlowId,
content: &StartContent,
other_identity: Option<UserIdentities>, other_identity: Option<UserIdentities>,
) -> Result<InnerSas, CancelContent> { started_from_request: bool,
match SasState::<Started>::from_start_event(account, other_device, other_identity, content) ) -> Result<InnerSas, OutgoingContent> {
{ match SasState::<Started>::from_start_event(
account,
other_device,
other_identity,
flow_id,
content,
started_from_request,
) {
Ok(s) => Ok(InnerSas::Started(s)), Ok(s) => Ok(InnerSas::Started(s)),
Err(s) => Err(s.as_content()), Err(s) => Err(s.as_content()),
} }
} }
pub fn accept(&self) -> Option<AcceptContent> { pub fn accept(&self) -> Option<OwnedAcceptContent> {
if let InnerSas::Started(s) = self { if let InnerSas::Started(s) = self {
Some(s.as_content()) Some(s.as_content())
} else { } else {
@ -150,7 +158,7 @@ impl InnerSas {
} }
} }
pub fn cancel(self, code: CancelCode) -> (InnerSas, Option<CancelContent>) { pub fn cancel(self, code: CancelCode) -> (InnerSas, Option<OutgoingContent>) {
let sas = match self { let sas = match self {
InnerSas::Created(s) => s.cancel(code), InnerSas::Created(s) => s.cancel(code),
InnerSas::Started(s) => s.cancel(code), InnerSas::Started(s) => s.cancel(code),
@ -165,7 +173,7 @@ impl InnerSas {
(InnerSas::Cancelled(sas), Some(content)) (InnerSas::Cancelled(sas), Some(content))
} }
pub fn confirm(self) -> (InnerSas, Option<MacContent>) { pub fn confirm(self) -> (InnerSas, Option<OutgoingContent>) {
match self { match self {
InnerSas::KeyReceived(s) => { InnerSas::KeyReceived(s) => {
let sas = s.confirm(); let sas = s.confirm();
@ -189,149 +197,104 @@ impl InnerSas {
} }
} }
#[allow(dead_code)] pub fn receive_any_event(
pub fn receive_room_event(
self, self,
event: &AnyMessageEvent, sender: &UserId,
) -> (InnerSas, Option<OutgoingContent>) { content: &AnyVerificationContent,
match event { ) -> (Self, Option<OutgoingContent>) {
AnyMessageEvent::KeyVerificationKey(e) => match self { match content {
InnerSas::Accepted(s) => { AnyVerificationContent::Accept(c) => {
match s.into_key_received(&e.sender, (e.room_id.clone(), e.content.clone())) {
Ok(s) => (InnerSas::KeyReceived(s), None),
Err(s) => {
let content = s.as_content();
(InnerSas::Cancelled(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::KeyReceived(s), Some(content.into()))
}
Err(s) => {
let content = s.as_content();
(InnerSas::Cancelled(s), Some(content.into()))
}
}
}
_ => (self, None),
},
AnyMessageEvent::KeyVerificationMac(e) => match self {
InnerSas::KeyReceived(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::Cancelled(s), Some(content.into()))
}
}
}
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::Cancelled(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) => {
let content = s.as_content();
(InnerSas::Cancelled(s), Some(content.into()))
}
}
}
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::Cancelled(s), Some(content.into()))
}
}
}
_ => (self, None),
},
_ => (self, None),
}
}
pub fn receive_event(self, event: &AnyToDeviceEvent) -> (InnerSas, Option<OutgoingContent>) {
match event {
AnyToDeviceEvent::KeyVerificationAccept(e) => {
if let InnerSas::Created(s) = self { if let InnerSas::Created(s) = self {
match s.into_accepted(&e.sender, e.content.clone()) { match s.into_accepted(sender, c) {
Ok(s) => { Ok(s) => {
let content = s.as_content(); let content = s.as_content();
(InnerSas::Accepted(s), Some(content.into())) (InnerSas::Accepted(s), Some(content))
} }
Err(s) => { Err(s) => {
let content = s.as_content(); let content = s.as_content();
(InnerSas::Cancelled(s), Some(content.into())) (InnerSas::Cancelled(s), Some(content))
} }
} }
} else { } else {
(self, None) (self, None)
} }
} }
AnyToDeviceEvent::KeyVerificationKey(e) => match self { AnyVerificationContent::Cancel(c) => {
InnerSas::Accepted(s) => match s.into_key_received(&e.sender, e.content.clone()) { let (sas, _) = self.cancel(c.cancel_code().to_owned());
(sas, None)
}
AnyVerificationContent::Key(c) => match self {
InnerSas::Accepted(s) => match s.into_key_received(sender, c) {
Ok(s) => (InnerSas::KeyReceived(s), None), Ok(s) => (InnerSas::KeyReceived(s), None),
Err(s) => { Err(s) => {
let content = s.as_content(); let content = s.as_content();
(InnerSas::Cancelled(s), Some(content.into())) (InnerSas::Cancelled(s), Some(content))
} }
}, },
InnerSas::Started(s) => match s.into_key_received(&e.sender, e.content.clone()) { InnerSas::Started(s) => match s.into_key_received(sender, c) {
Ok(s) => { Ok(s) => {
let content = s.as_content(); let content = s.as_content();
(InnerSas::KeyReceived(s), Some(content.into())) (InnerSas::KeyReceived(s), Some(content))
} }
Err(s) => { Err(s) => {
let content = s.as_content(); let content = s.as_content();
(InnerSas::Cancelled(s), Some(content.into())) (InnerSas::Cancelled(s), Some(content))
} }
}, },
_ => (self, None), _ => (self, None),
}, },
AnyToDeviceEvent::KeyVerificationMac(e) => match self { AnyVerificationContent::Mac(c) => match self {
InnerSas::KeyReceived(s) => { InnerSas::KeyReceived(s) => match s.into_mac_received(sender, c) {
match s.into_mac_received(&e.sender, e.content.clone()) { Ok(s) => (InnerSas::MacReceived(s), None),
Ok(s) => (InnerSas::MacReceived(s), None), Err(s) => {
let content = s.as_content();
(InnerSas::Cancelled(s), Some(content))
}
},
InnerSas::Confirmed(s) =>
// TODO remove the else branch when we remove the ability to
// start from a `m.key.verification.start` event.
{
match if s.started_from_request {
s.into_waiting_for_done(sender, c)
.map(|s| (Some(s.done_content()), InnerSas::WaitingForDone(s)))
} else {
s.into_done(sender, c).map(|s| (None, InnerSas::Done(s)))
} {
Ok((c, s)) => (s, c),
Err(s) => { Err(s) => {
let content = s.as_content(); let content = s.as_content();
(InnerSas::Cancelled(s), Some(content.into())) (InnerSas::Cancelled(s), Some(content))
} }
} }
} }
InnerSas::Confirmed(s) => match s.into_done(&e.sender, e.content.clone()) { _ => (self, None),
},
AnyVerificationContent::Done(c) => match self {
InnerSas::WaitingForDone(s) => match s.into_done(sender, c) {
Ok(s) => (InnerSas::Done(s), None), Ok(s) => (InnerSas::Done(s), None),
Err(s) => { Err(s) => {
let content = s.as_content(); let content = s.as_content();
(InnerSas::Cancelled(s), Some(content.into())) (InnerSas::Cancelled(s), Some(content))
} }
}, },
InnerSas::WaitingForDoneUnconfirmed(s) => match s.into_done(sender, c) {
Ok(s) => {
let content = s.done_content();
(InnerSas::Done(s), Some(content))
}
Err(s) => {
let content = s.as_content();
(InnerSas::Cancelled(s), Some(content))
}
},
_ => (self, None), _ => (self, None),
}, },
_ => (self, None), AnyVerificationContent::Request(_)
| AnyVerificationContent::Ready(_)
| AnyVerificationContent::Start(_) => (self, None),
} }
} }

View File

@ -21,8 +21,7 @@ use std::sync::{Arc, Mutex};
#[cfg(test)] #[cfg(test)]
use std::time::Instant; use std::time::Instant;
use event_enums::AcceptContent; pub use event_enums::OutgoingContent;
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;
use matrix_sdk_common::{ use matrix_sdk_common::{
@ -33,13 +32,16 @@ use matrix_sdk_common::{
cancel::CancelCode, cancel::CancelCode,
ShortAuthenticationString, ShortAuthenticationString,
}, },
AnyMessageEvent, AnyMessageEventContent, AnyToDeviceEvent, AnyToDeviceEventContent, AnyMessageEventContent, AnyToDeviceEventContent,
}, },
identifiers::{DeviceId, EventId, RoomId, UserId}, identifiers::{DeviceId, EventId, RoomId, UserId},
uuid::Uuid, uuid::Uuid,
}; };
use super::{FlowId, IdentitiesBeingVerified, VerificationResult}; use super::{
event_enums::{AnyVerificationContent, OwnedAcceptContent, StartContent},
FlowId, IdentitiesBeingVerified, VerificationResult,
};
use crate::{ use crate::{
identities::{ReadOnlyDevice, UserIdentities}, identities::{ReadOnlyDevice, UserIdentities},
olm::PrivateCrossSigningIdentity, olm::PrivateCrossSigningIdentity,
@ -111,10 +113,10 @@ impl Sas {
let flow_id = inner_sas.verification_flow_id(); let flow_id = inner_sas.verification_flow_id();
let identities = IdentitiesBeingVerified { let identities = IdentitiesBeingVerified {
private_identity: private_identity.clone(), private_identity,
store: store.clone(), store: store.clone(),
device_being_verified: other_device.clone(), device_being_verified: other_device,
identity_being_verified: other_identity.clone(), identity_being_verified: other_identity,
}; };
Sas { Sas {
@ -142,7 +144,7 @@ impl Sas {
store: Arc<Box<dyn CryptoStore>>, store: Arc<Box<dyn CryptoStore>>,
other_identity: Option<UserIdentities>, other_identity: Option<UserIdentities>,
transaction_id: Option<String>, transaction_id: Option<String>,
) -> (Sas, StartContent) { ) -> (Sas, OutgoingContent) {
let (inner, content) = InnerSas::start( let (inner, content) = InnerSas::start(
account.clone(), account.clone(),
other_device.clone(), other_device.clone(),
@ -181,7 +183,7 @@ 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>,
) -> (Sas, StartContent) { ) -> (Sas, OutgoingContent) {
let (inner, content) = InnerSas::start_in_room( let (inner, content) = InnerSas::start_in_room(
flow_id, flow_id,
room_id, room_id,
@ -214,18 +216,22 @@ impl Sas {
/// * `event` - The m.key.verification.start event that was sent to us by /// * `event` - The m.key.verification.start event that was sent to us by
/// the other side. /// the other side.
pub(crate) fn from_start_event( pub(crate) fn from_start_event(
content: impl Into<StartContent>, flow_id: FlowId,
content: &StartContent,
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>,
started_from_request: bool,
) -> Result<Sas, OutgoingContent> { ) -> Result<Sas, OutgoingContent> {
let inner = InnerSas::from_start_event( let inner = InnerSas::from_start_event(
account.clone(), account.clone(),
other_device.clone(), other_device.clone(),
flow_id,
content, content,
other_identity.clone(), other_identity.clone(),
started_from_request,
)?; )?;
Ok(Self::start_helper( Ok(Self::start_helper(
@ -257,11 +263,11 @@ impl Sas {
settings: AcceptSettings, settings: AcceptSettings,
) -> Option<OutgoingVerificationRequest> { ) -> Option<OutgoingVerificationRequest> {
self.inner.lock().unwrap().accept().map(|c| match settings.apply(c) { self.inner.lock().unwrap().accept().map(|c| match settings.apply(c) {
AcceptContent::ToDevice(c) => { OwnedAcceptContent::ToDevice(c) => {
let content = AnyToDeviceEventContent::KeyVerificationAccept(c); let content = AnyToDeviceEventContent::KeyVerificationAccept(c);
self.content_to_request(content).into() self.content_to_request(content).into()
} }
AcceptContent::Room(room_id, content) => RoomMessageRequest { OwnedAcceptContent::Room(room_id, content) => RoomMessageRequest {
room_id, room_id,
txn_id: Uuid::new_v4(), txn_id: Uuid::new_v4(),
content: AnyMessageEventContent::KeyVerificationAccept(content), content: AnyMessageEventContent::KeyVerificationAccept(content),
@ -293,15 +299,10 @@ impl Sas {
}; };
let mac_request = content.map(|c| match c { let mac_request = content.map(|c| match c {
event_enums::MacContent::ToDevice(c) => { OutgoingContent::ToDevice(c) => self.content_to_request(c).into(),
self.content_to_request(AnyToDeviceEventContent::KeyVerificationMac(c)).into() OutgoingContent::Room(r, c) => {
RoomMessageRequest { room_id: r, txn_id: Uuid::new_v4(), content: c }.into()
} }
event_enums::MacContent::Room(r, c) => RoomMessageRequest {
room_id: r,
txn_id: Uuid::new_v4(),
content: AnyMessageEventContent::KeyVerificationMac(c),
}
.into(),
}); });
if done { if done {
@ -337,15 +338,10 @@ impl Sas {
let (sas, content) = sas.cancel(code); let (sas, content) = sas.cancel(code);
*guard = sas; *guard = sas;
content.map(|c| match c { content.map(|c| match c {
CancelContent::Room(room_id, content) => RoomMessageRequest { OutgoingContent::Room(room_id, content) => {
room_id, RoomMessageRequest { room_id, txn_id: Uuid::new_v4(), content }.into()
txn_id: Uuid::new_v4(),
content: AnyMessageEventContent::KeyVerificationCancel(content),
}
.into(),
CancelContent::ToDevice(c) => {
self.content_to_request(AnyToDeviceEventContent::KeyVerificationCancel(c)).into()
} }
OutgoingContent::ToDevice(c) => self.content_to_request(c).into(),
}) })
} }
@ -406,19 +402,14 @@ impl Sas {
self.inner.lock().unwrap().decimals() self.inner.lock().unwrap().decimals()
} }
pub(crate) fn receive_room_event(&self, event: &AnyMessageEvent) -> Option<OutgoingContent> { pub(crate) fn receive_any_event(
&self,
sender: &UserId,
content: &AnyVerificationContent,
) -> Option<OutgoingContent> {
let mut guard = self.inner.lock().unwrap(); let mut guard = self.inner.lock().unwrap();
let sas: InnerSas = (*guard).clone(); let sas: InnerSas = (*guard).clone();
let (sas, content) = sas.receive_room_event(event); let (sas, content) = sas.receive_any_event(sender, content);
*guard = sas;
content
}
pub(crate) fn receive_event(&self, event: &AnyToDeviceEvent) -> Option<OutgoingContent> {
let mut guard = self.inner.lock().unwrap();
let sas: InnerSas = (*guard).clone();
let (sas, content) = sas.receive_event(event);
*guard = sas; *guard = sas;
content content
@ -465,13 +456,16 @@ impl AcceptSettings {
Self { allowed_methods: methods } Self { allowed_methods: methods }
} }
fn apply(self, mut content: AcceptContent) -> AcceptContent { fn apply(self, mut content: OwnedAcceptContent) -> OwnedAcceptContent {
match &mut content { match &mut content {
AcceptContent::ToDevice(AcceptToDeviceEventContent { OwnedAcceptContent::ToDevice(AcceptToDeviceEventContent {
method: AcceptMethod::MSasV1(c), method: AcceptMethod::MSasV1(c),
.. ..
}) })
| AcceptContent::Room(_, AcceptEventContent { method: AcceptMethod::MSasV1(c), .. }) => { | OwnedAcceptContent::Room(
_,
AcceptEventContent { method: AcceptMethod::MSasV1(c), .. },
) => {
c.short_authentication_string.retain(|sas| self.allowed_methods.contains(sas)); c.short_authentication_string.retain(|sas| self.allowed_methods.contains(sas));
content content
} }
@ -490,7 +484,10 @@ mod test {
use crate::{ use crate::{
olm::PrivateCrossSigningIdentity, olm::PrivateCrossSigningIdentity,
store::{CryptoStore, MemoryStore}, store::{CryptoStore, MemoryStore},
verification::test::{get_content_from_request, wrap_any_to_device_content}, verification::{
event_enums::{AcceptContent, KeyContent, MacContent, StartContent},
sas::OutgoingContent,
},
ReadOnlyAccount, ReadOnlyDevice, ReadOnlyAccount, ReadOnlyDevice,
}; };
@ -534,47 +531,51 @@ mod test {
None, None,
); );
let flow_id = alice.flow_id().to_owned();
let content = StartContent::try_from(&content).unwrap();
let bob = Sas::from_start_event( let bob = Sas::from_start_event(
content, flow_id,
&content,
bob_store, bob_store,
bob, bob,
PrivateCrossSigningIdentity::empty(bob_id()), PrivateCrossSigningIdentity::empty(bob_id()),
alice_device, alice_device,
None, None,
false,
) )
.unwrap(); .unwrap();
let event = wrap_any_to_device_content(
bob.user_id(),
get_content_from_request(&bob.accept().unwrap()),
);
let content = alice.receive_event(&event); let request = bob.accept().unwrap();
let content = OutgoingContent::try_from(request).unwrap();
let content = AcceptContent::try_from(&content).unwrap();
let content = alice.receive_any_event(bob.user_id(), &content.into()).unwrap();
assert!(!alice.can_be_presented()); assert!(!alice.can_be_presented());
assert!(!bob.can_be_presented()); assert!(!bob.can_be_presented());
let event = wrap_any_to_device_content(alice.user_id(), content.unwrap()); let content = KeyContent::try_from(&content).unwrap();
let event = wrap_any_to_device_content(bob.user_id(), bob.receive_event(&event).unwrap()); let content = bob.receive_any_event(alice.user_id(), &content.into()).unwrap();
assert!(bob.can_be_presented()); assert!(bob.can_be_presented());
alice.receive_event(&event); let content = KeyContent::try_from(&content).unwrap();
alice.receive_any_event(bob.user_id(), &content.into());
assert!(alice.can_be_presented()); assert!(alice.can_be_presented());
assert_eq!(alice.emoji().unwrap(), bob.emoji().unwrap()); assert_eq!(alice.emoji().unwrap(), bob.emoji().unwrap());
assert_eq!(alice.decimals().unwrap(), bob.decimals().unwrap()); assert_eq!(alice.decimals().unwrap(), bob.decimals().unwrap());
let event = wrap_any_to_device_content( let request = alice.confirm().await.unwrap().0.unwrap();
alice.user_id(), let content = OutgoingContent::try_from(request).unwrap();
get_content_from_request(&alice.confirm().await.unwrap().0.unwrap()), let content = MacContent::try_from(&content).unwrap();
); bob.receive_any_event(alice.user_id(), &content.into());
bob.receive_event(&event);
let event = wrap_any_to_device_content( let request = bob.confirm().await.unwrap().0.unwrap();
bob.user_id(), let content = OutgoingContent::try_from(request).unwrap();
get_content_from_request(&bob.confirm().await.unwrap().0.unwrap()), let content = MacContent::try_from(&content).unwrap();
); alice.receive_any_event(bob.user_id(), &content.into());
alice.receive_event(&event);
assert!(alice.verified_devices().unwrap().contains(&alice.other_device())); assert!(alice.verified_devices().unwrap().contains(&alice.other_device()));
assert!(bob.verified_devices().unwrap().contains(&bob.other_device())); assert!(bob.verified_devices().unwrap().contains(&bob.other_device()));

View File

@ -20,20 +20,23 @@ use std::{
}; };
use matrix_sdk_common::{ use matrix_sdk_common::{
events::key::verification::{ events::{
accept::{ key::verification::{
AcceptEventContent, AcceptMethod, AcceptToDeviceEventContent, accept::{
SasV1Content as AcceptV1Content, SasV1ContentInit as AcceptV1ContentInit, AcceptEventContent, AcceptMethod, AcceptToDeviceEventContent,
SasV1Content as AcceptV1Content, SasV1ContentInit as AcceptV1ContentInit,
},
cancel::CancelCode,
done::{DoneEventContent, DoneToDeviceEventContent},
key::{KeyEventContent, KeyToDeviceEventContent},
start::{
SasV1Content, SasV1ContentInit, StartEventContent, StartMethod,
StartToDeviceEventContent,
},
HashAlgorithm, KeyAgreementProtocol, MessageAuthenticationCode, Relation,
ShortAuthenticationString, VerificationMethod,
}, },
cancel::CancelCode, AnyMessageEventContent, AnyToDeviceEventContent,
done::DoneEventContent,
key::{KeyEventContent, KeyToDeviceEventContent},
start::{
SasV1Content, SasV1ContentInit, StartEventContent, StartMethod,
StartToDeviceEventContent,
},
HashAlgorithm, KeyAgreementProtocol, MessageAuthenticationCode, Relation,
ShortAuthenticationString, VerificationMethod,
}, },
identifiers::{DeviceId, EventId, RoomId, UserId}, identifiers::{DeviceId, EventId, RoomId, UserId},
uuid::Uuid, uuid::Uuid,
@ -42,17 +45,21 @@ use olm_rs::sas::OlmSas;
use tracing::info; use tracing::info;
use super::{ use super::{
event_enums::{
AcceptContent, CancelContent, DoneContent, KeyContent, MacContent, StartContent,
},
helpers::{ helpers::{
calculate_commitment, get_decimal, get_emoji, get_emoji_index, get_mac_content, calculate_commitment, get_decimal, get_emoji, get_emoji_index, get_mac_content,
receive_mac_event, SasIds, receive_mac_event, SasIds,
}, },
OutgoingContent,
}; };
use crate::{ use crate::{
identities::{ReadOnlyDevice, UserIdentities}, identities::{ReadOnlyDevice, UserIdentities},
verification::{Cancelled, Done, FlowId}, verification::{
event_enums::{
AcceptContent, DoneContent, KeyContent, MacContent, OwnedAcceptContent,
OwnedStartContent, StartContent,
},
Cancelled, Done, FlowId,
},
ReadOnlyAccount, ReadOnlyAccount,
}; };
@ -196,6 +203,9 @@ pub struct SasState<S: Clone> {
/// The SAS state we're in. /// The SAS state we're in.
pub state: Arc<S>, pub state: Arc<S>,
/// Did the SAS verification start from a `m.verification.request`.
pub started_from_request: bool,
} }
#[cfg(not(tarpaulin_include))] #[cfg(not(tarpaulin_include))]
@ -227,7 +237,7 @@ pub struct Started {
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Accepted { pub struct Accepted {
pub accepted_protocols: Arc<AcceptedProtocols>, pub accepted_protocols: Arc<AcceptedProtocols>,
start_content: Arc<StartContent>, start_content: Arc<OwnedStartContent>,
commitment: String, commitment: String,
} }
@ -296,6 +306,7 @@ impl<S: Clone> SasState<S> {
last_event_time: self.last_event_time, last_event_time: self.last_event_time,
verification_flow_id: self.verification_flow_id, verification_flow_id: self.verification_flow_id,
state: Arc::new(Cancelled::new(cancel_code)), state: Arc::new(Cancelled::new(cancel_code)),
started_from_request: self.started_from_request,
} }
} }
@ -344,9 +355,10 @@ impl SasState<Created> {
other_identity: Option<UserIdentities>, other_identity: Option<UserIdentities>,
transaction_id: Option<String>, transaction_id: Option<String>,
) -> SasState<Created> { ) -> SasState<Created> {
let started_from_request = transaction_id.is_some();
let flow_id = let flow_id =
FlowId::ToDevice(transaction_id.unwrap_or_else(|| Uuid::new_v4().to_string())); 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, started_from_request)
} }
/// Create a new SAS in-room verification flow. /// Create a new SAS in-room verification flow.
@ -369,7 +381,7 @@ impl SasState<Created> {
other_identity: Option<UserIdentities>, other_identity: Option<UserIdentities>,
) -> SasState<Created> { ) -> SasState<Created> {
let flow_id = FlowId::InRoom(room_id, event_id); let flow_id = FlowId::InRoom(room_id, event_id);
Self::new_helper(flow_id, account, other_device, other_identity) Self::new_helper(flow_id, account, other_device, other_identity, false)
} }
fn new_helper( fn new_helper(
@ -377,6 +389,7 @@ impl SasState<Created> {
account: ReadOnlyAccount, account: ReadOnlyAccount,
other_device: ReadOnlyDevice, other_device: ReadOnlyDevice,
other_identity: Option<UserIdentities>, other_identity: Option<UserIdentities>,
started_from_request: bool,
) -> SasState<Created> { ) -> SasState<Created> {
SasState { SasState {
inner: Arc::new(Mutex::new(OlmSas::new())), inner: Arc::new(Mutex::new(OlmSas::new())),
@ -385,6 +398,7 @@ impl SasState<Created> {
creation_time: Arc::new(Instant::now()), creation_time: Arc::new(Instant::now()),
last_event_time: Arc::new(Instant::now()), last_event_time: Arc::new(Instant::now()),
started_from_request,
state: Arc::new(Created { state: Arc::new(Created {
protocol_definitions: SasV1ContentInit { protocol_definitions: SasV1ContentInit {
@ -397,9 +411,9 @@ impl SasState<Created> {
} }
} }
pub fn as_content(&self) -> StartContent { pub fn as_content(&self) -> OwnedStartContent {
match self.verification_flow_id.as_ref() { match self.verification_flow_id.as_ref() {
FlowId::ToDevice(s) => StartContent::ToDevice(StartToDeviceEventContent::new( FlowId::ToDevice(s) => OwnedStartContent::ToDevice(StartToDeviceEventContent::new(
self.device_id().into(), self.device_id().into(),
s.to_string(), s.to_string(),
StartMethod::SasV1( StartMethod::SasV1(
@ -407,7 +421,7 @@ impl SasState<Created> {
.expect("Invalid initial protocol definitions."), .expect("Invalid initial protocol definitions."),
), ),
)), )),
FlowId::InRoom(r, e) => StartContent::Room( FlowId::InRoom(r, e) => OwnedStartContent::Room(
r.clone(), r.clone(),
StartEventContent::new( StartEventContent::new(
self.device_id().into(), self.device_id().into(),
@ -431,11 +445,9 @@ impl SasState<Created> {
pub fn into_accepted( pub fn into_accepted(
self, self,
sender: &UserId, sender: &UserId,
content: impl Into<AcceptContent>, content: &AcceptContent,
) -> Result<SasState<Accepted>, SasState<Cancelled>> { ) -> Result<SasState<Accepted>, SasState<Cancelled>> {
let content = content.into(); self.check_event(&sender, content.flow_id()).map_err(|c| self.clone().cancel(c))?;
self.check_event(&sender, content.flow_id().as_str())
.map_err(|c| self.clone().cancel(c))?;
if let AcceptMethod::MSasV1(content) = content.method() { if let AcceptMethod::MSasV1(content) = content.method() {
let accepted_protocols = let accepted_protocols =
@ -449,6 +461,7 @@ impl SasState<Created> {
verification_flow_id: self.verification_flow_id, verification_flow_id: self.verification_flow_id,
creation_time: self.creation_time, creation_time: self.creation_time,
last_event_time: self.last_event_time, last_event_time: self.last_event_time,
started_from_request: self.started_from_request,
state: Arc::new(Accepted { state: Arc::new(Accepted {
start_content, start_content,
commitment: content.commitment.clone(), commitment: content.commitment.clone(),
@ -479,22 +492,18 @@ impl SasState<Started> {
account: ReadOnlyAccount, account: ReadOnlyAccount,
other_device: ReadOnlyDevice, other_device: ReadOnlyDevice,
other_identity: Option<UserIdentities>, other_identity: Option<UserIdentities>,
content: impl Into<StartContent>, flow_id: FlowId,
) -> Result<SasState<Started>, SasState<Cancelled>> {
Self::from_start_helper(account, other_device, other_identity, &content.into())
}
fn from_start_helper(
account: ReadOnlyAccount,
other_device: ReadOnlyDevice,
other_identity: Option<UserIdentities>,
content: &StartContent, content: &StartContent,
started_from_request: bool,
) -> Result<SasState<Started>, SasState<Cancelled>> { ) -> Result<SasState<Started>, SasState<Cancelled>> {
let flow_id = Arc::new(flow_id);
let canceled = || SasState { let canceled = || SasState {
inner: Arc::new(Mutex::new(OlmSas::new())), inner: Arc::new(Mutex::new(OlmSas::new())),
creation_time: Arc::new(Instant::now()), creation_time: Arc::new(Instant::now()),
last_event_time: Arc::new(Instant::now()), last_event_time: Arc::new(Instant::now()),
started_from_request,
ids: SasIds { ids: SasIds {
account: account.clone(), account: account.clone(),
@ -502,7 +511,7 @@ impl SasState<Started> {
other_identity: other_identity.clone(), other_identity: other_identity.clone(),
}, },
verification_flow_id: content.flow_id().into(), verification_flow_id: flow_id.clone(),
state: Arc::new(Cancelled::new(CancelCode::UnknownMethod)), state: Arc::new(Cancelled::new(CancelCode::UnknownMethod)),
}; };
@ -510,7 +519,7 @@ impl SasState<Started> {
let sas = OlmSas::new(); let sas = OlmSas::new();
let pubkey = sas.public_key(); let pubkey = sas.public_key();
let commitment = calculate_commitment(&pubkey, content.clone()); let commitment = calculate_commitment(&pubkey, content);
info!( info!(
"Calculated commitment for pubkey {} and content {:?} {}", "Calculated commitment for pubkey {} and content {:?} {}",
@ -525,8 +534,9 @@ impl SasState<Started> {
creation_time: Arc::new(Instant::now()), creation_time: Arc::new(Instant::now()),
last_event_time: Arc::new(Instant::now()), last_event_time: Arc::new(Instant::now()),
started_from_request,
verification_flow_id: content.flow_id().into(), verification_flow_id: flow_id,
state: Arc::new(Started { state: Arc::new(Started {
accepted_protocols: accepted_protocols.into(), accepted_protocols: accepted_protocols.into(),
@ -548,7 +558,7 @@ impl SasState<Started> {
/// This should be sent out automatically if the SAS verification flow has /// This should be sent out automatically if the SAS verification flow has
/// been started because of a /// been started because of a
/// m.key.verification.request -> m.key.verification.ready flow. /// m.key.verification.request -> m.key.verification.ready flow.
pub fn as_content(&self) -> AcceptContent { pub fn as_content(&self) -> OwnedAcceptContent {
let method = AcceptMethod::MSasV1( let method = AcceptMethod::MSasV1(
AcceptV1ContentInit { AcceptV1ContentInit {
commitment: self.state.commitment.clone(), commitment: self.state.commitment.clone(),
@ -591,12 +601,9 @@ impl SasState<Started> {
pub fn into_key_received( pub fn into_key_received(
self, self,
sender: &UserId, sender: &UserId,
content: impl Into<KeyContent>, content: &KeyContent,
) -> Result<SasState<KeyReceived>, SasState<Cancelled>> { ) -> Result<SasState<KeyReceived>, SasState<Cancelled>> {
let content = content.into(); self.check_event(&sender, &content.flow_id()).map_err(|c| self.clone().cancel(c))?;
self.check_event(&sender, &content.flow_id().as_str())
.map_err(|c| self.clone().cancel(c))?;
let their_pubkey = content.public_key().to_owned(); let their_pubkey = content.public_key().to_owned();
@ -612,6 +619,7 @@ impl SasState<Started> {
verification_flow_id: self.verification_flow_id, verification_flow_id: self.verification_flow_id,
creation_time: self.creation_time, creation_time: self.creation_time,
last_event_time: self.last_event_time, last_event_time: self.last_event_time,
started_from_request: self.started_from_request,
state: Arc::new(KeyReceived { state: Arc::new(KeyReceived {
we_started: false, we_started: false,
their_pubkey, their_pubkey,
@ -633,15 +641,14 @@ impl SasState<Accepted> {
pub fn into_key_received( pub fn into_key_received(
self, self,
sender: &UserId, sender: &UserId,
content: impl Into<KeyContent>, content: &KeyContent,
) -> Result<SasState<KeyReceived>, SasState<Cancelled>> { ) -> Result<SasState<KeyReceived>, SasState<Cancelled>> {
let content = content.into(); self.check_event(&sender, content.flow_id()).map_err(|c| self.clone().cancel(c))?;
self.check_event(&sender, content.flow_id().as_str()) let commitment = calculate_commitment(
.map_err(|c| self.clone().cancel(c))?; content.public_key(),
&self.state.start_content.as_start_content(),
let commitment = );
calculate_commitment(content.public_key(), self.state.start_content.as_ref().clone());
if self.state.commitment != commitment { if self.state.commitment != commitment {
Err(self.cancel(CancelCode::InvalidMessage)) Err(self.cancel(CancelCode::InvalidMessage))
@ -660,6 +667,7 @@ impl SasState<Accepted> {
verification_flow_id: self.verification_flow_id, verification_flow_id: self.verification_flow_id,
creation_time: self.creation_time, creation_time: self.creation_time,
last_event_time: self.last_event_time, last_event_time: self.last_event_time,
started_from_request: self.started_from_request,
state: Arc::new(KeyReceived { state: Arc::new(KeyReceived {
their_pubkey, their_pubkey,
we_started: true, we_started: true,
@ -672,19 +680,21 @@ impl SasState<Accepted> {
/// Get the content for the key event. /// Get the content for the key event.
/// ///
/// The content needs to be automatically sent to the other side. /// The content needs to be automatically sent to the other side.
pub fn as_content(&self) -> KeyContent { pub fn as_content(&self) -> OutgoingContent {
match &*self.verification_flow_id { match &*self.verification_flow_id {
FlowId::ToDevice(s) => KeyToDeviceEventContent { FlowId::ToDevice(s) => {
transaction_id: s.to_string(), AnyToDeviceEventContent::KeyVerificationKey(KeyToDeviceEventContent {
key: self.inner.lock().unwrap().public_key(), transaction_id: s.to_string(),
key: self.inner.lock().unwrap().public_key(),
})
.into()
} }
.into(),
FlowId::InRoom(r, e) => ( FlowId::InRoom(r, e) => (
r.clone(), r.clone(),
KeyEventContent::new( AnyMessageEventContent::KeyVerificationKey(KeyEventContent::new(
self.inner.lock().unwrap().public_key(), self.inner.lock().unwrap().public_key(),
Relation::new(e.clone()), Relation::new(e.clone()),
), )),
) )
.into(), .into(),
} }
@ -696,19 +706,21 @@ impl SasState<KeyReceived> {
/// ///
/// The content needs to be automatically sent to the other side if and only /// The content needs to be automatically sent to the other side if and only
/// if we_started is false. /// if we_started is false.
pub fn as_content(&self) -> KeyContent { pub fn as_content(&self) -> OutgoingContent {
match &*self.verification_flow_id { match &*self.verification_flow_id {
FlowId::ToDevice(s) => KeyToDeviceEventContent { FlowId::ToDevice(s) => {
transaction_id: s.to_string(), AnyToDeviceEventContent::KeyVerificationKey(KeyToDeviceEventContent {
key: self.inner.lock().unwrap().public_key(), transaction_id: s.to_string(),
key: self.inner.lock().unwrap().public_key(),
})
.into()
} }
.into(),
FlowId::InRoom(r, e) => ( FlowId::InRoom(r, e) => (
r.clone(), r.clone(),
KeyEventContent::new( AnyMessageEventContent::KeyVerificationKey(KeyEventContent::new(
self.inner.lock().unwrap().public_key(), self.inner.lock().unwrap().public_key(),
Relation::new(e.clone()), Relation::new(e.clone()),
), )),
) )
.into(), .into(),
} }
@ -766,12 +778,9 @@ impl SasState<KeyReceived> {
pub fn into_mac_received( pub fn into_mac_received(
self, self,
sender: &UserId, sender: &UserId,
content: impl Into<MacContent>, content: &MacContent,
) -> Result<SasState<MacReceived>, SasState<Cancelled>> { ) -> Result<SasState<MacReceived>, SasState<Cancelled>> {
let content = content.into(); self.check_event(&sender, content.flow_id()).map_err(|c| self.clone().cancel(c))?;
self.check_event(&sender, content.flow_id().as_str())
.map_err(|c| self.clone().cancel(c))?;
let (devices, master_keys) = receive_mac_event( let (devices, master_keys) = receive_mac_event(
&self.inner.lock().unwrap(), &self.inner.lock().unwrap(),
@ -788,6 +797,7 @@ impl SasState<KeyReceived> {
creation_time: self.creation_time, creation_time: self.creation_time,
last_event_time: self.last_event_time, last_event_time: self.last_event_time,
ids: self.ids, ids: self.ids,
started_from_request: self.started_from_request,
state: Arc::new(MacReceived { state: Arc::new(MacReceived {
we_started: self.state.we_started, we_started: self.state.we_started,
their_pubkey: self.state.their_pubkey.clone(), their_pubkey: self.state.their_pubkey.clone(),
@ -805,6 +815,7 @@ impl SasState<KeyReceived> {
pub fn confirm(self) -> SasState<Confirmed> { pub fn confirm(self) -> SasState<Confirmed> {
SasState { SasState {
inner: self.inner, inner: self.inner,
started_from_request: self.started_from_request,
verification_flow_id: self.verification_flow_id, verification_flow_id: self.verification_flow_id,
creation_time: self.creation_time, creation_time: self.creation_time,
last_event_time: self.last_event_time, last_event_time: self.last_event_time,
@ -827,12 +838,9 @@ impl SasState<Confirmed> {
pub fn into_done( pub fn into_done(
self, self,
sender: &UserId, sender: &UserId,
content: impl Into<MacContent>, content: &MacContent,
) -> Result<SasState<Done>, SasState<Cancelled>> { ) -> Result<SasState<Done>, SasState<Cancelled>> {
let content = content.into(); self.check_event(sender, content.flow_id()).map_err(|c| self.clone().cancel(c))?;
self.check_event(&sender, &content.flow_id().as_str())
.map_err(|c| self.clone().cancel(c))?;
let (devices, master_keys) = receive_mac_event( let (devices, master_keys) = receive_mac_event(
&self.inner.lock().unwrap(), &self.inner.lock().unwrap(),
@ -848,6 +856,7 @@ impl SasState<Confirmed> {
creation_time: self.creation_time, creation_time: self.creation_time,
last_event_time: self.last_event_time, last_event_time: self.last_event_time,
verification_flow_id: self.verification_flow_id, verification_flow_id: self.verification_flow_id,
started_from_request: self.started_from_request,
ids: self.ids, ids: self.ids,
state: Arc::new(Done { state: Arc::new(Done {
@ -869,12 +878,9 @@ impl SasState<Confirmed> {
pub fn into_waiting_for_done( pub fn into_waiting_for_done(
self, self,
sender: &UserId, sender: &UserId,
content: impl Into<MacContent>, content: &MacContent,
) -> Result<SasState<WaitingForDone>, SasState<Cancelled>> { ) -> Result<SasState<WaitingForDone>, SasState<Cancelled>> {
let content = content.into(); self.check_event(&sender, &content.flow_id()).map_err(|c| self.clone().cancel(c))?;
self.check_event(&sender, &content.flow_id().as_str())
.map_err(|c| self.clone().cancel(c))?;
let (devices, master_keys) = receive_mac_event( let (devices, master_keys) = receive_mac_event(
&self.inner.lock().unwrap(), &self.inner.lock().unwrap(),
@ -890,6 +896,7 @@ impl SasState<Confirmed> {
creation_time: self.creation_time, creation_time: self.creation_time,
last_event_time: self.last_event_time, last_event_time: self.last_event_time,
verification_flow_id: self.verification_flow_id, verification_flow_id: self.verification_flow_id,
started_from_request: self.started_from_request,
ids: self.ids, ids: self.ids,
state: Arc::new(WaitingForDone { state: Arc::new(WaitingForDone {
@ -902,7 +909,7 @@ impl SasState<Confirmed> {
/// Get the content for the mac event. /// Get the content for the mac event.
/// ///
/// The content needs to be automatically sent to the other side. /// The content needs to be automatically sent to the other side.
pub fn as_content(&self) -> MacContent { pub fn as_content(&self) -> OutgoingContent {
get_mac_content(&self.inner.lock().unwrap(), &self.ids, &self.verification_flow_id) get_mac_content(&self.inner.lock().unwrap(), &self.ids, &self.verification_flow_id)
} }
} }
@ -917,6 +924,7 @@ impl SasState<MacReceived> {
inner: self.inner, inner: self.inner,
verification_flow_id: self.verification_flow_id, verification_flow_id: self.verification_flow_id,
creation_time: self.creation_time, creation_time: self.creation_time,
started_from_request: self.started_from_request,
last_event_time: self.last_event_time, last_event_time: self.last_event_time,
ids: self.ids, ids: self.ids,
state: Arc::new(Done { state: Arc::new(Done {
@ -937,6 +945,7 @@ impl SasState<MacReceived> {
inner: self.inner, inner: self.inner,
verification_flow_id: self.verification_flow_id, verification_flow_id: self.verification_flow_id,
creation_time: self.creation_time, creation_time: self.creation_time,
started_from_request: self.started_from_request,
last_event_time: self.last_event_time, last_event_time: self.last_event_time,
ids: self.ids, ids: self.ids,
state: Arc::new(WaitingForDone { state: Arc::new(WaitingForDone {
@ -994,18 +1003,23 @@ impl SasState<WaitingForDone> {
/// ///
/// The content needs to be automatically sent to the other side if it /// The content needs to be automatically sent to the other side if it
/// wasn't already sent. /// wasn't already sent.
pub fn as_content(&self) -> MacContent { pub fn as_content(&self) -> OutgoingContent {
get_mac_content(&self.inner.lock().unwrap(), &self.ids, &self.verification_flow_id) get_mac_content(&self.inner.lock().unwrap(), &self.ids, &self.verification_flow_id)
} }
pub fn done_content(&self) -> DoneContent { pub fn done_content(&self) -> OutgoingContent {
match self.verification_flow_id.as_ref() { match self.verification_flow_id.as_ref() {
FlowId::ToDevice(_) => { FlowId::ToDevice(t) => AnyToDeviceEventContent::KeyVerificationDone(
unreachable!("The done content isn't supported yet for to-device verifications") DoneToDeviceEventContent::new(t.to_owned()),
} )
FlowId::InRoom(r, e) => { .into(),
(r.clone(), DoneEventContent::new(Relation::new(e.clone()))).into() FlowId::InRoom(r, e) => (
} r.clone(),
AnyMessageEventContent::KeyVerificationDone(DoneEventContent::new(Relation::new(
e.clone(),
))),
)
.into(),
} }
} }
@ -1019,18 +1033,16 @@ impl SasState<WaitingForDone> {
pub fn into_done( pub fn into_done(
self, self,
sender: &UserId, sender: &UserId,
content: impl Into<DoneContent>, content: &DoneContent,
) -> Result<SasState<Done>, SasState<Cancelled>> { ) -> Result<SasState<Done>, SasState<Cancelled>> {
let content = content.into(); self.check_event(&sender, content.flow_id()).map_err(|c| self.clone().cancel(c))?;
self.check_event(&sender, &content.flow_id().as_str())
.map_err(|c| self.clone().cancel(c))?;
Ok(SasState { Ok(SasState {
inner: self.inner, inner: self.inner,
creation_time: self.creation_time, creation_time: self.creation_time,
last_event_time: self.last_event_time, last_event_time: self.last_event_time,
verification_flow_id: self.verification_flow_id, verification_flow_id: self.verification_flow_id,
started_from_request: self.started_from_request,
ids: self.ids, ids: self.ids,
state: Arc::new(Done { state: Arc::new(Done {
@ -1046,18 +1058,23 @@ impl SasState<Done> {
/// ///
/// The content needs to be automatically sent to the other side if it /// The content needs to be automatically sent to the other side if it
/// wasn't already sent. /// wasn't already sent.
pub fn as_content(&self) -> MacContent { pub fn as_content(&self) -> OutgoingContent {
get_mac_content(&self.inner.lock().unwrap(), &self.ids, &self.verification_flow_id) get_mac_content(&self.inner.lock().unwrap(), &self.ids, &self.verification_flow_id)
} }
pub fn done_content(&self) -> DoneContent { pub fn done_content(&self) -> OutgoingContent {
match self.verification_flow_id.as_ref() { match self.verification_flow_id.as_ref() {
FlowId::ToDevice(_) => { FlowId::ToDevice(t) => AnyToDeviceEventContent::KeyVerificationDone(
unreachable!("The done content isn't supported yet for to-device verifications") DoneToDeviceEventContent::new(t.to_owned()),
} )
FlowId::InRoom(r, e) => { .into(),
(r.clone(), DoneEventContent::new(Relation::new(e.clone()))).into() FlowId::InRoom(r, e) => (
} r.clone(),
AnyMessageEventContent::KeyVerificationDone(DoneEventContent::new(Relation::new(
e.clone(),
))),
)
.into(),
} }
} }
@ -1073,7 +1090,7 @@ impl SasState<Done> {
} }
impl SasState<Cancelled> { impl SasState<Cancelled> {
pub fn as_content(&self) -> CancelContent { pub fn as_content(&self) -> OutgoingContent {
self.state.as_content(&self.verification_flow_id) self.state.as_content(&self.verification_flow_id)
} }
} }
@ -1092,7 +1109,7 @@ mod test {
use super::{Accepted, Created, SasState, Started}; use super::{Accepted, Created, SasState, Started};
use crate::{ use crate::{
verification::sas::{event_enums::AcceptContent, StartContent}, verification::event_enums::{AcceptContent, KeyContent, MacContent, StartContent},
ReadOnlyAccount, ReadOnlyDevice, ReadOnlyAccount, ReadOnlyDevice,
}; };
@ -1122,9 +1139,16 @@ mod test {
let alice_sas = SasState::<Created>::new(alice.clone(), bob_device, None, 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();
let flow_id = start_content.flow_id();
let bob_sas = let bob_sas = SasState::<Started>::from_start_event(
SasState::<Started>::from_start_event(bob.clone(), alice_device, None, start_content); bob.clone(),
alice_device,
None,
flow_id,
&start_content.as_start_content(),
false,
);
(alice_sas, bob_sas.unwrap()) (alice_sas, bob_sas.unwrap())
} }
@ -1137,10 +1161,10 @@ mod test {
#[tokio::test] #[tokio::test]
async fn sas_accept() { async fn sas_accept() {
let (alice, bob) = get_sas_pair().await; let (alice, bob) = get_sas_pair().await;
let content = bob.as_content();
let content = AcceptContent::from(&content);
let event = bob.as_content(); alice.into_accepted(bob.user_id(), &content).unwrap();
alice.into_accepted(bob.user_id(), event).unwrap();
} }
#[tokio::test] #[tokio::test]
@ -1148,15 +1172,18 @@ mod test {
let (alice, bob) = get_sas_pair().await; let (alice, bob) = get_sas_pair().await;
let content = bob.as_content(); let content = bob.as_content();
let content = AcceptContent::from(&content);
let alice: SasState<Accepted> = alice.into_accepted(bob.user_id(), content).unwrap(); let alice: SasState<Accepted> = alice.into_accepted(bob.user_id(), &content).unwrap();
let content = alice.as_content(); let content = alice.as_content();
let content = KeyContent::try_from(&content).unwrap();
let bob = bob.into_key_received(alice.user_id(), content).unwrap(); let bob = bob.into_key_received(alice.user_id(), &content).unwrap();
let content = bob.as_content(); let content = bob.as_content();
let content = KeyContent::try_from(&content).unwrap();
let alice = alice.into_key_received(bob.user_id(), content).unwrap(); let alice = alice.into_key_received(bob.user_id(), &content).unwrap();
assert_eq!(alice.get_decimal(), bob.get_decimal()); assert_eq!(alice.get_decimal(), bob.get_decimal());
assert_eq!(alice.get_emoji(), bob.get_emoji()); assert_eq!(alice.get_emoji(), bob.get_emoji());
@ -1167,15 +1194,18 @@ mod test {
let (alice, bob) = get_sas_pair().await; let (alice, bob) = get_sas_pair().await;
let content = bob.as_content(); let content = bob.as_content();
let content = AcceptContent::from(&content);
let alice: SasState<Accepted> = alice.into_accepted(bob.user_id(), content).unwrap(); let alice: SasState<Accepted> = alice.into_accepted(bob.user_id(), &content).unwrap();
let content = alice.as_content(); let content = alice.as_content();
let content = KeyContent::try_from(&content).unwrap();
let bob = bob.into_key_received(alice.user_id(), content).unwrap(); let bob = bob.into_key_received(alice.user_id(), &content).unwrap();
let content = bob.as_content(); let content = bob.as_content();
let content = KeyContent::try_from(&content).unwrap();
let alice = alice.into_key_received(bob.user_id(), content).unwrap(); let alice = alice.into_key_received(bob.user_id(), &content).unwrap();
assert_eq!(alice.get_decimal(), bob.get_decimal()); assert_eq!(alice.get_decimal(), bob.get_decimal());
assert_eq!(alice.get_emoji(), bob.get_emoji()); assert_eq!(alice.get_emoji(), bob.get_emoji());
@ -1185,14 +1215,16 @@ mod test {
let bob = bob.confirm(); let bob = bob.confirm();
let content = bob.as_content(); let content = bob.as_content();
let content = MacContent::try_from(&content).unwrap();
let alice = alice.into_mac_received(bob.user_id(), content).unwrap(); let alice = alice.into_mac_received(bob.user_id(), &content).unwrap();
assert!(!alice.get_emoji().is_empty()); assert!(!alice.get_emoji().is_empty());
assert_eq!(alice.get_decimal(), bob_decimals); assert_eq!(alice.get_decimal(), bob_decimals);
let alice = alice.confirm(); let alice = alice.confirm();
let content = alice.as_content(); let content = alice.as_content();
let bob = bob.into_done(alice.user_id(), content).unwrap(); let content = MacContent::try_from(&content).unwrap();
let bob = bob.into_done(alice.user_id(), &content).unwrap();
assert!(bob.verified_devices().contains(&bob.other_device())); assert!(bob.verified_devices().contains(&bob.other_device()));
assert!(alice.verified_devices().contains(&alice.other_device())); assert!(alice.verified_devices().contains(&alice.other_device()));
@ -1203,11 +1235,7 @@ mod test {
let (alice, bob) = get_sas_pair().await; let (alice, bob) = get_sas_pair().await;
let mut content = bob.as_content(); let mut content = bob.as_content();
let mut method = content.method_mut();
let mut method = match &mut content {
AcceptContent::ToDevice(c) => &mut c.method,
AcceptContent::Room(_, c) => &mut c.method,
};
match &mut method { match &mut method {
AcceptMethod::MSasV1(ref mut c) => { AcceptMethod::MSasV1(ref mut c) => {
@ -1216,14 +1244,18 @@ mod test {
_ => panic!("Unknown accept event content"), _ => panic!("Unknown accept event content"),
} }
let alice: SasState<Accepted> = alice.into_accepted(bob.user_id(), content).unwrap(); let content = AcceptContent::from(&content);
let alice: SasState<Accepted> = alice.into_accepted(bob.user_id(), &content).unwrap();
let content = alice.as_content(); let content = alice.as_content();
let bob = bob.into_key_received(alice.user_id(), content).unwrap(); let content = KeyContent::try_from(&content).unwrap();
let bob = bob.into_key_received(alice.user_id(), &content).unwrap();
let content = bob.as_content(); let content = bob.as_content();
let content = KeyContent::try_from(&content).unwrap();
alice alice
.into_key_received(bob.user_id(), content) .into_key_received(bob.user_id(), &content)
.expect_err("Didn't cancel on invalid commitment"); .expect_err("Didn't cancel on invalid commitment");
} }
@ -1232,8 +1264,9 @@ mod test {
let (alice, bob) = get_sas_pair().await; let (alice, bob) = get_sas_pair().await;
let content = bob.as_content(); let content = bob.as_content();
let content = AcceptContent::from(&content);
let sender = UserId::try_from("@malory:example.org").unwrap(); let sender = UserId::try_from("@malory:example.org").unwrap();
alice.into_accepted(&sender, content).expect_err("Didn't cancel on a invalid sender"); alice.into_accepted(&sender, &content).expect_err("Didn't cancel on a invalid sender");
} }
#[tokio::test] #[tokio::test]
@ -1241,11 +1274,7 @@ mod test {
let (alice, bob) = get_sas_pair().await; let (alice, bob) = get_sas_pair().await;
let mut content = bob.as_content(); let mut content = bob.as_content();
let mut method = content.method_mut();
let mut method = match &mut content {
AcceptContent::ToDevice(c) => &mut c.method,
AcceptContent::Room(_, c) => &mut c.method,
};
match &mut method { match &mut method {
AcceptMethod::MSasV1(ref mut c) => { AcceptMethod::MSasV1(ref mut c) => {
@ -1254,8 +1283,10 @@ mod test {
_ => panic!("Unknown accept event content"), _ => panic!("Unknown accept event content"),
} }
let content = AcceptContent::from(&content);
alice alice
.into_accepted(bob.user_id(), content) .into_accepted(bob.user_id(), &content)
.expect_err("Didn't cancel on an invalid SAS method"); .expect_err("Didn't cancel on an invalid SAS method");
} }
@ -1264,19 +1295,17 @@ mod test {
let (alice, bob) = get_sas_pair().await; let (alice, bob) = get_sas_pair().await;
let mut content = bob.as_content(); let mut content = bob.as_content();
let method = content.method_mut();
let method = match &mut content {
AcceptContent::ToDevice(c) => &mut c.method,
AcceptContent::Room(_, c) => &mut c.method,
};
*method = AcceptMethod::Custom(CustomContent { *method = AcceptMethod::Custom(CustomContent {
method: "m.sas.custom".to_string(), method: "m.sas.custom".to_string(),
data: Default::default(), data: Default::default(),
}); });
let content = AcceptContent::from(&content);
alice alice
.into_accepted(bob.user_id(), content) .into_accepted(bob.user_id(), &content)
.expect_err("Didn't cancel on an unknown SAS method"); .expect_err("Didn't cancel on an unknown SAS method");
} }
@ -1291,11 +1320,7 @@ mod test {
let alice_sas = SasState::<Created>::new(alice.clone(), bob_device, None, 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();
let method = start_content.method_mut();
let method = match &mut start_content {
StartContent::ToDevice(c) => &mut c.method,
StartContent::Room(_, c) => &mut c.method,
};
match method { match method {
StartMethod::SasV1(ref mut c) => { StartMethod::SasV1(ref mut c) => {
@ -1304,27 +1329,38 @@ mod test {
_ => panic!("Unknown SAS start method"), _ => panic!("Unknown SAS start method"),
} }
let flow_id = start_content.flow_id();
let content = StartContent::from(&start_content);
SasState::<Started>::from_start_event( SasState::<Started>::from_start_event(
bob.clone(), bob.clone(),
alice_device.clone(), alice_device.clone(),
None, None,
start_content, flow_id,
&content,
false,
) )
.expect_err("Didn't cancel on invalid MAC method"); .expect_err("Didn't cancel on invalid MAC method");
let mut start_content = alice_sas.as_content(); let mut start_content = alice_sas.as_content();
let method = start_content.method_mut();
let method = match &mut start_content {
StartContent::ToDevice(c) => &mut c.method,
StartContent::Room(_, c) => &mut c.method,
};
*method = StartMethod::Custom(CustomStartContent { *method = StartMethod::Custom(CustomStartContent {
method: "m.sas.custom".to_string(), method: "m.sas.custom".to_string(),
data: Default::default(), data: Default::default(),
}); });
SasState::<Started>::from_start_event(bob.clone(), alice_device, None, start_content) let flow_id = start_content.flow_id();
.expect_err("Didn't cancel on unknown sas method"); let content = StartContent::from(&start_content);
SasState::<Started>::from_start_event(
bob.clone(),
alice_device,
None,
flow_id,
&content,
false,
)
.expect_err("Didn't cancel on unknown sas method");
} }
} }