crypto: Send out done events for in-room verifications.

This commit is contained in:
Damir Jelić 2020-12-17 15:50:13 +01:00
parent 79102b3390
commit 4ad4ad1e94
4 changed files with 260 additions and 7 deletions

View file

@ -222,7 +222,7 @@ impl VerificationMachine {
event: &AnySyncRoomEvent,
) -> Result<(), CryptoStoreError> {
if let AnySyncRoomEvent::Message(m) = event {
// Since this are room events we will get events that we send out on
// Since these are room events we will get events that we send out on
// our own as well.
if m.sender() == self.account.user_id() {
return Ok(());
@ -308,10 +308,25 @@ impl VerificationMachine {
&s,
&m.clone().into_full_event(room_id.clone()),
);
}
}
AnySyncMessageEvent::KeyVerificationDone(e) => {
if let Some(s) = self.room_verifications.get(&e.content.relation.event_id) {
let content =
s.receive_room_event(&m.clone().into_full_event(room_id.clone()));
if s.is_done() {
match s.mark_as_done().await? {
VerificationResult::Ok => (),
VerificationResult::Ok => {
if let Some(c) = content {
self.queue_up_content(
s.other_user_id(),
s.other_device_id(),
c,
);
}
}
VerificationResult::Cancel(r) => {
self.outgoing_to_device_messages
.insert(r.request_id(), r.into());
@ -321,6 +336,14 @@ impl VerificationMachine {
self.outgoing_to_device_messages
.insert(request.request_id, request);
if let Some(c) = content {
self.queue_up_content(
s.other_user_id(),
s.other_device_id(),
c,
);
}
}
}
}

View file

@ -21,6 +21,7 @@ use matrix_sdk_common::{
key::verification::{
accept::{AcceptEventContent, AcceptToDeviceEventContent},
cancel::{CancelEventContent, CancelToDeviceEventContent},
done::DoneEventContent,
key::{KeyEventContent, KeyToDeviceEventContent},
mac::{MacEventContent, MacToDeviceEventContent},
start::{StartEventContent, StartMethod, StartToDeviceEventContent},
@ -187,6 +188,24 @@ impl From<CancelToDeviceEventContent> for CancelContent {
}
}
pub enum DoneContent {
Room(RoomId, DoneEventContent),
}
impl DoneContent {
pub fn flow_id(&self) -> FlowId {
match self {
DoneContent::Room(r, c) => FlowId::InRoom(r.clone(), c.relation.event_id.clone()),
}
}
}
impl From<(RoomId, DoneEventContent)> for DoneContent {
fn from(content: (RoomId, DoneEventContent)) -> Self {
DoneContent::Room(content.0, content.1)
}
}
#[derive(Clone, Debug)]
pub enum OutgoingContent {
Room(RoomId, AnyMessageEventContent),
@ -222,6 +241,14 @@ impl From<KeyContent> for OutgoingContent {
}
}
impl From<DoneContent> for OutgoingContent {
fn from(content: DoneContent) -> Self {
match content {
DoneContent::Room(r, c) => (r, AnyMessageEventContent::KeyVerificationDone(c)).into(),
}
}
}
impl From<AnyToDeviceEventContent> for OutgoingContent {
fn from(content: AnyToDeviceEventContent) -> Self {
OutgoingContent::ToDevice(content)

View file

@ -30,6 +30,7 @@ use matrix_sdk_common::{
},
identifiers::{EventId, RoomId, UserId},
};
use tracing::trace;
use crate::{
identities::{ReadOnlyDevice, UserIdentities},
@ -40,7 +41,7 @@ use super::{
event_enums::{AcceptContent, CancelContent, MacContent, OutgoingContent},
sas_state::{
Accepted, Canceled, Confirmed, Created, Done, FlowId, KeyReceived, MacReceived, SasState,
Started,
Started, WaitingForDone,
},
StartContent,
};
@ -53,6 +54,8 @@ pub enum InnerSas {
KeyRecieved(SasState<KeyReceived>),
Confirmed(SasState<Confirmed>),
MacReceived(SasState<MacReceived>),
WaitingForDone(SasState<WaitingForDone>),
WaitingForDoneUnconfirmed(SasState<WaitingForDone>),
Done(SasState<Done>),
Canceled(SasState<Canceled>),
}
@ -125,6 +128,8 @@ impl InnerSas {
InnerSas::Confirmed(s) => s.set_creation_time(time),
InnerSas::MacReceived(s) => s.set_creation_time(time),
InnerSas::Done(s) => s.set_creation_time(time),
InnerSas::WaitingForDone(s) => s.set_creation_time(time),
InnerSas::WaitingForDoneUnconfirmed(s) => s.set_creation_time(time),
}
}
@ -151,9 +156,17 @@ impl InnerSas {
(InnerSas::Confirmed(sas), Some(content))
}
InnerSas::MacReceived(s) => {
let sas = s.confirm();
let content = sas.as_content();
(InnerSas::Done(sas), Some(content))
if s.is_dm_verification() {
let sas = s.confirm_and_wait_for_done();
let content = sas.as_content();
(InnerSas::WaitingForDoneUnconfirmed(sas), Some(content))
} else {
let sas = s.confirm();
let content = sas.as_content();
(InnerSas::Done(sas), Some(content))
}
}
_ => (self, None),
}
@ -201,6 +214,22 @@ impl InnerSas {
}
}
InnerSas::Confirmed(s) => {
match s.into_waiting_for_done(&e.sender, (e.room_id.clone(), e.content.clone()))
{
Ok(s) => {
let content = s.done_content();
(InnerSas::WaitingForDone(s), Some(content.into()))
}
Err(s) => {
let content = s.as_content();
(InnerSas::Canceled(s), Some(content.into()))
}
}
}
_ => (self, None),
},
AnyMessageEvent::KeyVerificationDone(e) => match self {
InnerSas::WaitingForDone(s) => {
match s.into_done(&e.sender, (e.room_id.clone(), e.content.clone())) {
Ok(s) => (InnerSas::Done(s), None),
Err(s) => {
@ -209,6 +238,19 @@ impl InnerSas {
}
}
}
InnerSas::WaitingForDoneUnconfirmed(s) => {
match s.into_done(&e.sender, (e.room_id.clone(), e.content.clone())) {
Ok(s) => {
let content = s.done_content();
(InnerSas::Done(s), Some(content.into()))
}
Err(s) => {
let content = s.as_content();
(InnerSas::Canceled(s), Some(content.into()))
}
}
}
_ => (self, None),
},
_ => (self, None),
@ -300,6 +342,8 @@ impl InnerSas {
InnerSas::KeyRecieved(s) => s.timed_out(),
InnerSas::Confirmed(s) => s.timed_out(),
InnerSas::MacReceived(s) => s.timed_out(),
InnerSas::WaitingForDone(s) => s.timed_out(),
InnerSas::WaitingForDoneUnconfirmed(s) => s.timed_out(),
InnerSas::Done(s) => s.timed_out(),
}
}
@ -313,6 +357,8 @@ impl InnerSas {
InnerSas::KeyRecieved(s) => s.verification_flow_id.clone(),
InnerSas::Confirmed(s) => s.verification_flow_id.clone(),
InnerSas::MacReceived(s) => s.verification_flow_id.clone(),
InnerSas::WaitingForDone(s) => s.verification_flow_id.clone(),
InnerSas::WaitingForDoneUnconfirmed(s) => s.verification_flow_id.clone(),
InnerSas::Done(s) => s.verification_flow_id.clone(),
}
}

View file

@ -29,6 +29,7 @@ use matrix_sdk_common::{
MSasV1Content as AcceptV1Content, MSasV1ContentInit as AcceptV1ContentInit,
},
cancel::{CancelCode, CancelEventContent, CancelToDeviceEventContent},
done::DoneEventContent,
key::{KeyEventContent, KeyToDeviceEventContent},
mac::MacToDeviceEventContent,
start::{
@ -47,7 +48,8 @@ use tracing::error;
use super::{
event_enums::{
AcceptContent, CancelContent, KeyContent, MacContent, OutgoingContent, StartContent,
AcceptContent, CancelContent, DoneContent, KeyContent, MacContent, OutgoingContent,
StartContent,
},
helpers::{
calculate_commitment, get_decimal, get_emoji, get_mac_content, receive_mac_event, SasIds,
@ -251,6 +253,15 @@ pub struct MacReceived {
verified_master_keys: Arc<[UserIdentities]>,
}
/// The SAS state we're going to be in after we receive a MAC event in a DM. DMs
/// require a final message `m.key.verification.done` message to conclude the
/// verificaton. This state waits for such a message.
#[derive(Clone, Debug)]
pub struct WaitingForDone {
verified_devices: Arc<[ReadOnlyDevice]>,
verified_master_keys: Arc<[UserIdentities]>,
}
/// The SAS state indicating that the verification finished successfully.
///
/// We can now mark the device in our verified devices lits as verified and sign
@ -300,6 +311,11 @@ impl<S: Clone> SasState<S> {
self.creation_time.elapsed() > MAX_AGE || self.last_event_time.elapsed() > MAX_EVENT_TIMEOUT
}
/// Is this verification happening inside a DM.
pub fn is_dm_verification(&self) -> bool {
matches!(&*self.verification_flow_id, FlowId::InRoom(_, _))
}
#[cfg(test)]
#[allow(dead_code)]
pub fn set_creation_time(&mut self, time: Instant) {
@ -880,6 +896,48 @@ impl SasState<Confirmed> {
})
}
/// Receive a m.key.verification.mac event, changing the state into
/// a `WaitingForDone` one. This method should be used instead of
/// `into_done()` if the verification started with a
/// `m.key.verification.request`.
///
/// # Arguments
///
/// * `event` - The m.key.verification.mac event that was sent to us by
/// the other side.
pub fn into_waiting_for_done(
self,
sender: &UserId,
content: impl Into<MacContent>,
) -> Result<SasState<WaitingForDone>, SasState<Canceled>> {
let content = content.into();
self.check_event(&sender, &content.flow_id().as_str())
.map_err(|c| self.clone().cancel(c))?;
let (devices, master_keys) = receive_mac_event(
&self.inner.lock().unwrap(),
&self.ids,
&self.verification_flow_id.as_str(),
sender,
&content,
)
.map_err(|c| self.clone().cancel(c))?;
Ok(SasState {
inner: self.inner,
creation_time: self.creation_time,
last_event_time: self.last_event_time,
verification_flow_id: self.verification_flow_id,
ids: self.ids,
state: Arc::new(WaitingForDone {
verified_devices: devices.into(),
verified_master_keys: master_keys.into(),
}),
})
}
/// Get the content for the mac event.
///
/// The content needs to be automatically sent to the other side.
@ -911,6 +969,26 @@ impl SasState<MacReceived> {
}
}
/// Confirm that the short auth string matches but wait for the other side
/// to confirm that it's done.
///
/// This needs to be done by the user, this will put us in the `WaitForDone`
/// state where we wait for the other side to confirm that the MAC event was
/// successfully received.
pub fn confirm_and_wait_for_done(self) -> SasState<WaitingForDone> {
SasState {
inner: self.inner,
verification_flow_id: self.verification_flow_id,
creation_time: self.creation_time,
last_event_time: self.last_event_time,
ids: self.ids,
state: Arc::new(WaitingForDone {
verified_devices: self.state.verified_devices.clone(),
verified_master_keys: self.state.verified_master_keys.clone(),
}),
}
}
/// Get the emoji version of the short authentication string.
///
/// Returns a vector of tuples where the first element is the emoji and the
@ -940,6 +1018,68 @@ impl SasState<MacReceived> {
}
}
impl SasState<WaitingForDone> {
/// Get the content for the mac event.
///
/// The content needs to be automatically sent to the other side if it
/// wasn't already sent.
pub fn as_content(&self) -> MacContent {
get_mac_content(
&self.inner.lock().unwrap(),
&self.ids,
&self.verification_flow_id,
)
}
pub fn done_content(&self) -> DoneContent {
match self.verification_flow_id.as_ref() {
FlowId::ToDevice(_) => {
unreachable!("The done content isn't supported yet for to-device verifications")
}
FlowId::InRoom(r, e) => (
r.clone(),
DoneEventContent {
relation: Relation {
event_id: e.clone(),
},
},
)
.into(),
}
}
/// Receive a m.key.verification.mac event, changing the state into
/// a `Done` one
///
/// # Arguments
///
/// * `event` - The m.key.verification.mac event that was sent to us by
/// the other side.
pub fn into_done(
self,
sender: &UserId,
content: impl Into<DoneContent>,
) -> Result<SasState<Done>, SasState<Canceled>> {
let content = content.into();
self.check_event(&sender, &content.flow_id().as_str())
.map_err(|c| self.clone().cancel(c))?;
Ok(SasState {
inner: self.inner,
creation_time: self.creation_time,
last_event_time: self.last_event_time,
verification_flow_id: self.verification_flow_id,
ids: self.ids,
state: Arc::new(Done {
verified_devices: self.state.verified_devices.clone(),
verified_master_keys: self.state.verified_master_keys.clone(),
}),
})
}
}
impl SasState<Done> {
/// Get the content for the mac event.
///
@ -953,6 +1093,23 @@ impl SasState<Done> {
)
}
pub fn done_content(&self) -> DoneContent {
match self.verification_flow_id.as_ref() {
FlowId::ToDevice(_) => {
unreachable!("The done content isn't supported yet for to-device verifications")
}
FlowId::InRoom(r, e) => (
r.clone(),
DoneEventContent {
relation: Relation {
event_id: e.clone(),
},
},
)
.into(),
}
}
/// Get the list of verified devices.
pub fn verified_devices(&self) -> Arc<[ReadOnlyDevice]> {
self.state.verified_devices.clone()