crypto: Send out done events for in-room verifications.
This commit is contained in:
parent
79102b3390
commit
4ad4ad1e94
4 changed files with 260 additions and 7 deletions
|
@ -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,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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(),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
Loading…
Reference in a new issue