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::{
room::encrypted::{EncryptedEventContent, EncryptedEventScheme},
room_key::RoomKeyToDeviceEventContent,
AnyMessageEventContent, AnyToDeviceEvent, SyncMessageEvent, ToDeviceEvent,
AnyMessageEventContent, AnyRoomEvent, AnyToDeviceEvent, SyncMessageEvent, ToDeviceEvent,
},
identifiers::{
DeviceId, DeviceIdBox, DeviceKeyAlgorithm, EventEncryptionAlgorithm, RoomId, UserId,
@ -703,7 +703,7 @@ impl OlmMachine {
}
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);
}
}
@ -986,7 +986,11 @@ impl OlmMachine {
trace!("Successfully decrypted a Megolm event {:?}", decrypted_event);
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 =

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 matrix_sdk_common::{
events::{
room::message::MessageType, AnyMessageEvent, AnySyncMessageEvent, AnySyncRoomEvent,
AnyToDeviceEvent,
},
identifiers::{DeviceId, EventId, RoomId, UserId},
identifiers::{DeviceId, EventId, UserId},
locks::Mutex,
uuid::Uuid,
};
use tracing::{info, trace, warn};
use tracing::info;
use super::{
event_enums::{AnyEvent, AnyVerificationContent},
requests::VerificationRequest,
sas::{content_to_request, OutgoingContent, Sas},
VerificationResult,
FlowId, VerificationResult,
};
use crate::{
olm::PrivateCrossSigningIdentity,
@ -59,10 +56,6 @@ impl VerificationCache {
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) {
match sas.flow_id() {
super::FlowId::ToDevice(t) => self.sas_verification.insert(t.to_owned(), sas),
@ -194,7 +187,7 @@ impl VerificationMachine {
None,
);
let request = match content.into() {
let request = match content {
OutgoingContent::Room(r, c) => {
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)
}
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) {
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,
room_id: &RoomId,
event: &AnySyncRoomEvent,
sas: Sas,
out_content: Option<OutgoingContent>,
) -> Result<(), CryptoStoreError> {
if let AnySyncRoomEvent::Message(m) = event {
// Since these are room events we will get events that we send out on
// our own as well.
if m.sender() == self.account.user_id() {
if let AnySyncMessageEvent::KeyVerificationReady(_e) = m {
// TODO if there is a verification request, go into passive
// mode since another device is handling this request.
match sas.mark_as_done().await? {
VerificationResult::Ok => {
if let Some(c) = out_content {
self.queue_up_content(sas.other_user_id(), sas.other_device_id(), c);
}
return Ok(());
}
match m {
AnySyncMessageEvent::RoomMessage(m) => {
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::Cancel(c) => {
if let Some(r) = sas.cancel_with_code(c) {
self.verifications.add_request(r.into());
}
}
VerificationResult::SignatureUpload(r) => {
self.verifications.add_request(r.into());
AnySyncMessageEvent::KeyVerificationDone(e) => {
if let Some(s) = self.verifications.get_room_sas(&e.content.relation.event_id) {
let content =
s.receive_room_event(&m.clone().into_full_event(room_id.clone()));
if s.is_done() {
match s.mark_as_done().await? {
VerificationResult::Ok => {
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,
);
}
}
}
}
};
if let Some(c) = out_content {
self.queue_up_content(sas.other_user_id(), sas.other_device_id(), c);
}
_ => (),
}
}
Ok(())
}
pub async fn receive_event(&self, event: &AnyToDeviceEvent) -> Result<(), CryptoStoreError> {
trace!("Received a key verification event {:?}", event);
pub async fn receive_any_event(
&self,
event: impl Into<AnyEvent<'_>>,
) -> Result<(), CryptoStoreError> {
let event = event.into();
match event {
AnyToDeviceEvent::KeyVerificationRequest(e) => {
let request = VerificationRequest::from_request(
self.verifications.clone(),
self.account.clone(),
self.private_identity.lock().await.clone(),
self.store.clone(),
&e.sender,
&e.content,
);
let flow_id = if let Ok(flow_id) = FlowId::try_from(&event) {
flow_id
} else {
// This isn't a verification event, return early.
return Ok(());
};
self.requests.insert(request.flow_id().as_str().to_string(), request);
}
AnyToDeviceEvent::KeyVerificationReady(e) => {
if let Some(request) = self.requests.get(&e.content.transaction_id) {
if &e.sender == request.other_user() {
// TODO remove this unwrap.
request.receive_ready(&e.sender, &e.content).unwrap();
}
}
}
AnyToDeviceEvent::KeyVerificationStart(e) => {
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
if let Some(content) = event.verification_content() {
match &content {
AnyVerificationContent::Request(r) => {
info!(
sender = event.sender().as_str(),
from_device = r.from_device().as_str(),
"Received a new verification request",
);
}
}
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() {
match s.mark_as_done().await? {
VerificationResult::Ok => (),
VerificationResult::Cancel(c) => {
if let Some(r) = s.cancel_with_code(c) {
self.verifications.add_request(r.into());
let request = VerificationRequest::from_request(
self.verifications.clone(),
self.account.clone(),
self.private_identity.lock().await.clone(),
self.store.clone(),
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);
}
}
VerificationResult::SignatureUpload(r) => {
self.verifications.add_request(r.into());
Err(cancellation) => self.queue_up_content(
event.sender(),
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(())
}
}
@ -491,9 +407,12 @@ mod test {
use super::{Sas, VerificationMachine};
use crate::{
olm::PrivateCrossSigningIdentity,
requests::OutgoingRequests,
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,
};
@ -538,7 +457,7 @@ mod test {
);
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
.unwrap();
@ -559,53 +478,41 @@ mod test {
let alice = alice_machine.get_sas(bob.flow_id().as_str()).unwrap();
let event = alice
.accept()
.map(|c| wrap_any_to_device_content(alice.user_id(), get_content_from_request(&c)))
.unwrap();
let request = alice.accept().unwrap();
let event = bob
.receive_event(&event)
.map(|c| wrap_any_to_device_content(bob.user_id(), c))
.unwrap();
let content = OutgoingContent::try_from(request).unwrap();
let content = AcceptContent::try_from(&content).unwrap().into();
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());
alice_machine.receive_event(&event).await.unwrap();
alice_machine.receive_any_event(&event).await.unwrap();
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 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);
assert!(bob.receive_event(&event).is_none());
assert!(bob.receive_any_event(alice.user_id(), &content).is_none());
assert!(alice.emoji().is_some());
assert!(bob.emoji().is_some());
assert_eq!(alice.emoji(), bob.emoji());
let event = wrap_any_to_device_content(
alice.user_id(),
get_content_from_request(&alice.confirm().await.unwrap().0.unwrap()),
);
bob.receive_event(&event);
let request = alice.confirm().await.unwrap().0.unwrap();
let content = OutgoingContent::try_from(request).unwrap();
let content = MacContent::try_from(&content).unwrap().into();
bob.receive_any_event(alice.user_id(), &content);
let event = wrap_any_to_device_content(
bob.user_id(),
get_content_from_request(&bob.confirm().await.unwrap().0.unwrap()),
);
alice.receive_event(&event);
let request = bob.confirm().await.unwrap().0.unwrap();
let content = OutgoingContent::try_from(request).unwrap();
let content = MacContent::try_from(&content).unwrap().into();
alice.receive_any_event(bob.user_id(), &content);
assert!(alice.is_done());
assert!(bob.is_done());

View File

@ -12,26 +12,30 @@
// See the License for the specific language governing permissions and
// limitations under the License.
mod event_enums;
mod machine;
mod requests;
mod sas;
use std::sync::Arc;
pub use machine::{VerificationCache, VerificationMachine};
use matrix_sdk_common::{
api::r0::keys::upload_signatures::Request as SignatureUploadRequest,
events::key::verification::{
cancel::{CancelCode, CancelEventContent, CancelToDeviceEventContent},
Relation,
events::{
key::verification::{
cancel::{CancelCode, CancelEventContent, CancelToDeviceEventContent},
Relation,
},
AnyMessageEventContent, AnyToDeviceEventContent,
},
identifiers::{DeviceId, EventId, RoomId, UserId},
};
pub use machine::{VerificationCache, VerificationMachine};
pub use requests::VerificationRequest;
pub use sas::{AcceptSettings, Sas};
use tracing::{error, info, trace, warn};
use self::sas::OutgoingContent;
use crate::{
error::SignatureError,
olm::PrivateCrossSigningIdentity,
@ -39,8 +43,6 @@ use crate::{
CryptoStoreError, LocalTrust, ReadOnlyDevice, UserIdentities,
};
use self::sas::CancelContent;
/// The verification state indicating that the verification finished
/// successfully.
///
@ -82,22 +84,24 @@ impl Cancelled {
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 {
FlowId::ToDevice(s) => CancelToDeviceEventContent::new(
s.clone(),
self.reason.to_string(),
self.cancel_code.clone(),
)
.into(),
FlowId::ToDevice(s) => {
AnyToDeviceEventContent::KeyVerificationCancel(CancelToDeviceEventContent::new(
s.clone(),
self.reason.to_string(),
self.cancel_code.clone(),
))
.into()
}
FlowId::InRoom(r, e) => (
r.clone(),
CancelEventContent::new(
AnyMessageEventContent::KeyVerificationCancel(CancelEventContent::new(
self.reason.to_string(),
self.cancel_code.clone(),
Relation::new(e.clone()),
),
)),
)
.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.
#[derive(Clone, Debug)]
pub enum VerificationResult {
@ -158,7 +168,6 @@ pub struct IdentitiesBeingVerified {
identity_being_verified: Option<UserIdentities>,
}
#[allow(dead_code)]
impl IdentitiesBeingVerified {
fn user_id(&self) -> &UserId {
self.private_identity.user_id()
@ -298,7 +307,10 @@ impl IdentitiesBeingVerified {
.map_or(false, |i| i.master_key() == identity.master_key())
{
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 {
i.mark_as_verified();
@ -307,28 +319,28 @@ impl IdentitiesBeingVerified {
Ok(Some(identity))
} else {
info!(
user_id = self.other_user_id().as_str(),
"The interactive verification process didn't verify \
the user identity of {} {:?}",
identity.user_id(),
verified_identities,
the user identity of the user that participated in \
the interactive verification",
);
Ok(None)
}
} else {
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.",
identity.user_id(),
);
Ok(None)
}
} else {
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.",
self.other_user_id(),
);
Ok(None)
}

View File

@ -14,19 +14,15 @@
#![allow(dead_code)]
use std::{
convert::TryFrom,
sync::{Arc, Mutex},
};
use std::sync::{Arc, Mutex};
use matrix_sdk_common::{
api::r0::to_device::DeviceIdOrAllDevices,
events::{
key::verification::{
done::{DoneEventContent, DoneToDeviceEventContent},
ready::{ReadyEventContent, ReadyToDeviceEventContent},
request::RequestToDeviceEventContent,
start::{StartEventContent, StartMethod, StartToDeviceEventContent},
start::StartMethod,
Relation, VerificationMethod,
},
room::message::KeyVerificationRequestEventContent,
@ -36,10 +32,11 @@ use matrix_sdk_common::{
uuid::Uuid,
MilliSecondsSinceUnixEpoch,
};
use tracing::{info, trace, warn};
use tracing::{info, warn};
use super::{
sas::{content_to_request, OutgoingContent, StartContent as OwnedStartContent},
event_enums::{ReadyContent, RequestContent, StartContent},
sas::{content_to_request, OutgoingContent},
FlowId, VerificationCache,
};
use crate::{
@ -51,184 +48,6 @@ use crate::{
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)]
/// TODO
pub struct VerificationRequest {
@ -311,56 +130,14 @@ impl VerificationRequest {
&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(
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,
account: ReadOnlyAccount,
private_cross_signing_identity: PrivateCrossSigningIdentity,
store: Arc<Box<dyn CryptoStore>>,
sender: &UserId,
flow_id: FlowId,
content: RequestContent,
content: &RequestContent,
) -> Self {
Self {
verification_cache: cache.clone(),
@ -394,13 +171,8 @@ impl VerificationRequest {
}
#[allow(clippy::unnecessary_wraps)]
pub(crate) fn receive_ready<'a>(
&self,
sender: &UserId,
content: impl Into<ReadyContent<'a>>,
) -> Result<(), ()> {
pub(crate) fn receive_ready(&self, sender: &UserId, content: &ReadyContent) -> Result<(), ()> {
let mut inner = self.inner.lock().unwrap();
let content = content.into();
if let InnerRequest::Created(s) = &*inner {
*inner = InnerRequest::Ready(s.clone().into_ready(sender, content));
@ -409,13 +181,11 @@ impl VerificationRequest {
Ok(())
}
pub(crate) async fn receive_start<'a>(
pub(crate) async fn receive_start(
&self,
sender: &UserId,
content: impl Into<StartContent<'a>>,
content: &StartContent<'_>,
) -> Result<(), CryptoStoreError> {
let content = content.into();
let inner = self.inner.lock().unwrap().clone();
if let InnerRequest::Ready(s) = inner {
@ -502,9 +272,9 @@ impl InnerRequest {
}
}
fn to_started_sas<'a>(
fn to_started_sas(
&self,
content: impl Into<StartContent<'a>>,
content: &StartContent,
other_device: ReadOnlyDevice,
other_identity: Option<UserIdentities>,
) -> 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.
RequestState {
account: self.account,
@ -600,7 +370,7 @@ impl RequestState<Requested> {
store: Arc<Box<dyn CryptoStore>>,
sender: &UserId,
flow_id: &FlowId,
content: RequestContent,
content: &RequestContent,
) -> RequestState<Requested> {
// TODO only create this if we suport the methods
RequestState {
@ -673,39 +443,27 @@ struct Ready {
impl RequestState<Ready> {
fn to_started_sas<'a>(
&self,
content: impl Into<StartContent<'a>>,
content: &StartContent<'a>,
other_device: ReadOnlyDevice,
other_identity: Option<UserIdentities>,
) -> 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(
(&*self.flow_id).to_owned(),
content,
self.store.clone(),
self.account.clone(),
self.private_cross_signing_identity.clone(),
other_device,
other_identity,
true,
)
}
async fn receive_start<'a>(
async fn receive_start(
&self,
sender: &UserId,
content: impl Into<StartContent<'a>>,
content: &StartContent<'_>,
) -> Result<(), CryptoStoreError> {
let content = content.into();
info!(
sender = sender.as_str(),
device = content.from_device().as_str(),
@ -747,7 +505,7 @@ impl RequestState<Ready> {
}
},
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,
Some(t),
);
(sas, content.into())
(sas, content)
}
FlowId::InRoom(r, e) => {
let (sas, content) = Sas::start_in_room(
@ -784,7 +542,7 @@ impl RequestState<Ready> {
store,
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_test::async_test;
use super::{StartContent, VerificationRequest};
use super::VerificationRequest;
use crate::{
olm::{PrivateCrossSigningIdentity, ReadOnlyAccount},
store::{Changes, CryptoStore, MemoryStore},
verification::{requests::ReadyContent, sas::OutgoingContent, VerificationCache},
verification::{
event_enums::{ReadyContent, StartContent},
sas::OutgoingContent,
FlowId, VerificationCache,
},
ReadOnlyDevice,
};
@ -856,21 +618,22 @@ mod test {
&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(),
alice,
alice_identity,
alice_store.into(),
&bob_id(),
&event_id,
&room_id,
&content,
flow_id,
&(&content).into(),
);
let content: OutgoingContent = alice_request.accept().unwrap().into();
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!(alice_request.is_ready());
@ -908,21 +671,22 @@ mod test {
&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(),
alice,
alice_identity,
alice_store.into(),
&bob_id(),
&event_id,
&room_id,
&content,
flow_id,
&(&content).into(),
);
let content: OutgoingContent = alice_request.accept().unwrap().into();
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!(alice_request.is_ready());
@ -931,7 +695,7 @@ mod test {
let content = StartContent::try_from(&start_content).unwrap();
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();
assert!(!bob_sas.is_cancelled());

View File

@ -12,254 +12,26 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use std::{collections::BTreeMap, convert::TryInto};
use matrix_sdk_common::{
events::{
key::verification::{
accept::{AcceptEventContent, AcceptMethod, AcceptToDeviceEventContent},
cancel::{CancelEventContent, CancelToDeviceEventContent},
done::DoneEventContent,
key::{KeyEventContent, KeyToDeviceEventContent},
mac::{MacEventContent, MacToDeviceEventContent},
start::{StartEventContent, StartMethod, StartToDeviceEventContent},
},
AnyMessageEventContent, AnyToDeviceEventContent,
},
events::{AnyMessageEventContent, AnyToDeviceEventContent},
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)]
pub enum OutgoingContent {
Room(RoomId, AnyMessageEventContent),
ToDevice(AnyToDeviceEventContent),
}
impl From<StartContent> for OutgoingContent {
fn from(content: StartContent) -> Self {
impl From<OwnedStartContent> for OutgoingContent {
fn from(content: OwnedStartContent) -> Self {
match content {
StartContent::Room(r, c) => (r, AnyMessageEventContent::KeyVerificationStart(c)).into(),
StartContent::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()
OwnedStartContent::Room(r, c) => {
(r, AnyMessageEventContent::KeyVerificationStart(c)).into()
}
OwnedStartContent::ToDevice(c) => {
AnyToDeviceEventContent::KeyVerificationStart(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)]
use crate::OutgoingVerificationRequest;
use std::convert::TryFrom;
use crate::verification::event_enums::OwnedStartContent;
#[cfg(test)]
use crate::{OutgoingRequest, OutgoingVerificationRequest, RoomMessageRequest, ToDeviceRequest};
#[cfg(test)]
impl From<OutgoingVerificationRequest> for OutgoingContent {
fn from(request: OutgoingVerificationRequest) -> Self {
use matrix_sdk_common::events::EventType;
use serde_json::Value;
match request {
OutgoingVerificationRequest::ToDevice(r) => {
let json: Value = serde_json::from_str(
r.messages.values().next().unwrap().values().next().unwrap().get(),
)
.unwrap();
match r.event_type {
EventType::KeyVerificationRequest => {
AnyToDeviceEventContent::KeyVerificationRequest(
serde_json::from_value(json).unwrap(),
)
}
EventType::KeyVerificationReady => {
AnyToDeviceEventContent::KeyVerificationReady(
serde_json::from_value(json).unwrap(),
)
}
EventType::KeyVerificationDone => AnyToDeviceEventContent::KeyVerificationDone(
serde_json::from_value(json).unwrap(),
),
EventType::KeyVerificationStart => {
AnyToDeviceEventContent::KeyVerificationStart(
serde_json::from_value(json).unwrap(),
)
}
EventType::KeyVerificationKey => AnyToDeviceEventContent::KeyVerificationKey(
serde_json::from_value(json).unwrap(),
),
EventType::KeyVerificationAccept => {
AnyToDeviceEventContent::KeyVerificationAccept(
serde_json::from_value(json).unwrap(),
)
}
EventType::KeyVerificationMac => AnyToDeviceEventContent::KeyVerificationMac(
serde_json::from_value(json).unwrap(),
),
EventType::KeyVerificationCancel => {
AnyToDeviceEventContent::KeyVerificationCancel(
serde_json::from_value(json).unwrap(),
)
}
_ => unreachable!(),
}
.into()
}
OutgoingVerificationRequest::InRoom(r) => (r.room_id, r.content).into(),
OutgoingVerificationRequest::ToDevice(r) => Self::try_from(r).unwrap(),
OutgoingVerificationRequest::InRoom(r) => Self::from(r),
}
}
}
#[cfg(test)]
impl From<RoomMessageRequest> for OutgoingContent {
fn from(value: RoomMessageRequest) -> Self {
(value.room_id, value.content).into()
}
}
#[cfg(test)]
impl TryFrom<ToDeviceRequest> for OutgoingContent {
type Error = ();
fn try_from(value: ToDeviceRequest) -> Result<Self, Self::Error> {
use matrix_sdk_common::events::EventType;
use serde_json::Value;
let json: Value = serde_json::from_str(
value
.messages
.values()
.next()
.and_then(|m| m.values().next().map(|j| j.get()))
.ok_or(())?,
)
.map_err(|_| ())?;
match value.event_type {
EventType::KeyVerificationRequest => {
Ok(AnyToDeviceEventContent::KeyVerificationRequest(
serde_json::from_value(json).map_err(|_| ())?,
)
.into())
}
EventType::KeyVerificationReady => Ok(AnyToDeviceEventContent::KeyVerificationReady(
serde_json::from_value(json).map_err(|_| ())?,
)
.into()),
EventType::KeyVerificationDone => Ok(AnyToDeviceEventContent::KeyVerificationDone(
serde_json::from_value(json).map_err(|_| ())?,
)
.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},
Relation,
},
AnyToDeviceEventContent, EventType,
AnyMessageEventContent, AnyToDeviceEventContent, EventType,
},
identifiers::{DeviceKeyAlgorithm, DeviceKeyId, UserId},
uuid::Uuid,
@ -31,13 +31,11 @@ use olm_rs::sas::OlmSas;
use sha2::{Digest, Sha256};
use tracing::{trace, warn};
use super::{
event_enums::{MacContent, StartContent},
FlowId,
};
use super::{FlowId, OutgoingContent};
use crate::{
identities::{ReadOnlyDevice, UserIdentities},
utilities::encode,
verification::event_enums::{MacContent, StartContent},
ReadOnlyAccount, ToDeviceRequest,
};
@ -58,8 +56,8 @@ pub struct SasIds {
///
/// * `content` - The `m.key.verification.start` event content that started the
/// interactive verification process.
pub fn calculate_commitment(public_key: &str, content: impl Into<StartContent>) -> String {
let content = content.into().canonical_json();
pub fn calculate_commitment(public_key: &str, content: &StartContent) -> String {
let content = content.canonical_json();
let content_string = content.to_string();
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<_>>();
keys.sort();
keys.sort_unstable();
let keys = sas
.calculate_mac(&keys.join(","), &format!("{}KEY_IDS", &info))
@ -296,7 +294,7 @@ fn extra_mac_info_send(ids: &SasIds, flow_id: &str) -> String {
/// # Panics
///
/// This will panic if the public key of the other side wasn't set.
pub fn get_mac_content(sas: &OlmSas, ids: &SasIds, flow_id: &FlowId) -> MacContent {
pub fn get_mac_content(sas: &OlmSas, ids: &SasIds, flow_id: &FlowId) -> OutgoingContent {
let mut mac: BTreeMap<String, String> = BTreeMap::new();
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");
match flow_id {
FlowId::ToDevice(s) => MacToDeviceEventContent::new(s.to_string(), mac, keys).into(),
FlowId::InRoom(r, e) => {
(r.clone(), MacEventContent::new(mac, keys, Relation::new(e.clone()))).into()
}
FlowId::ToDevice(s) => AnyToDeviceEventContent::KeyVerificationMac(
MacToDeviceEventContent::new(s.to_string(), mac, keys),
)
.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,
emoji_from_index,
};
use crate::verification::event_enums::StartContent;
#[test]
fn commitment_calculation() {
@ -575,7 +583,8 @@ mod test {
});
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);
}

View File

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

View File

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

View File

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