crypto: Add another SAS state so we know when both parties accepted

master
Damir Jelić 2021-06-29 09:16:22 +02:00
parent 113587247e
commit 9052843acb
3 changed files with 88 additions and 18 deletions

View File

@ -24,6 +24,7 @@ use ruma::{
use super::{
sas_state::{
Accepted, Confirmed, Created, KeyReceived, MacReceived, SasState, Started, WaitingForDone,
WeAccepted,
},
FlowId,
};
@ -41,6 +42,7 @@ pub enum InnerSas {
Created(SasState<Created>),
Started(SasState<Started>),
Accepted(SasState<Accepted>),
WeAccepted(SasState<WeAccepted>),
KeyReceived(SasState<KeyReceived>),
Confirmed(SasState<Confirmed>),
MacReceived(SasState<MacReceived>),
@ -65,6 +67,7 @@ impl InnerSas {
match self {
InnerSas::Created(s) => s.started_from_request,
InnerSas::Started(s) => s.started_from_request,
InnerSas::WeAccepted(s) => s.started_from_request,
InnerSas::Accepted(s) => s.started_from_request,
InnerSas::KeyReceived(s) => s.started_from_request,
InnerSas::Confirmed(s) => s.started_from_request,
@ -75,6 +78,19 @@ impl InnerSas {
}
}
pub fn has_been_accepted(&self) -> bool {
match self {
InnerSas::Created(_) | InnerSas::Started(_) | InnerSas::Cancelled(_) => false,
InnerSas::Accepted(_)
| InnerSas::WeAccepted(_)
| InnerSas::KeyReceived(_)
| InnerSas::Confirmed(_)
| InnerSas::MacReceived(_)
| InnerSas::WaitingForDone(_)
| InnerSas::Done(_) => true,
}
}
pub fn supports_emoji(&self) -> bool {
match self {
InnerSas::Created(_) => false,
@ -83,6 +99,11 @@ impl InnerSas {
.accepted_protocols
.short_auth_string
.contains(&ShortAuthenticationString::Emoji),
InnerSas::WeAccepted(s) => s
.state
.accepted_protocols
.short_auth_string
.contains(&ShortAuthenticationString::Emoji),
InnerSas::Accepted(s) => s
.state
.accepted_protocols
@ -144,9 +165,11 @@ impl InnerSas {
}
}
pub fn accept(&self) -> Option<OwnedAcceptContent> {
pub fn accept(self) -> Option<(InnerSas, OwnedAcceptContent)> {
if let InnerSas::Started(s) = self {
Some(s.as_content())
let sas = s.into_accepted();
let content = sas.as_content();
Some((InnerSas::WeAccepted(sas), content))
} else {
None
}
@ -165,6 +188,7 @@ impl InnerSas {
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::WeAccepted(s) => s.set_creation_time(time),
}
}
@ -177,6 +201,7 @@ impl InnerSas {
InnerSas::Created(s) => s.cancel(cancelled_by_us, code),
InnerSas::Started(s) => s.cancel(cancelled_by_us, code),
InnerSas::Accepted(s) => s.cancel(cancelled_by_us, code),
InnerSas::WeAccepted(s) => s.cancel(cancelled_by_us, code),
InnerSas::KeyReceived(s) => s.cancel(cancelled_by_us, code),
InnerSas::MacReceived(s) => s.cancel(cancelled_by_us, code),
_ => return (self, None),
@ -245,7 +270,7 @@ impl InnerSas {
(InnerSas::Cancelled(s), Some(content))
}
},
InnerSas::Started(s) => match s.into_key_received(sender, c) {
InnerSas::WeAccepted(s) => match s.into_key_received(sender, c) {
Ok(s) => {
let content = s.as_content();
(InnerSas::KeyReceived(s), Some(content))
@ -347,6 +372,7 @@ impl InnerSas {
InnerSas::MacReceived(s) => s.timed_out(),
InnerSas::WaitingForDone(s) => s.timed_out(),
InnerSas::Done(s) => s.timed_out(),
InnerSas::WeAccepted(s) => s.timed_out(),
}
}
@ -361,6 +387,7 @@ impl InnerSas {
InnerSas::MacReceived(s) => s.verification_flow_id.clone(),
InnerSas::WaitingForDone(s) => s.verification_flow_id.clone(),
InnerSas::Done(s) => s.verification_flow_id.clone(),
InnerSas::WeAccepted(s) => s.verification_flow_id.clone(),
}
}

View File

@ -119,6 +119,11 @@ impl Sas {
self.inner.lock().unwrap().have_we_confirmed()
}
/// Has the verification been accepted by both parties.
pub fn has_been_accepted(&self) -> bool {
self.inner.lock().unwrap().has_been_accepted()
}
/// Get the cancel code of this SAS verification if it has been cancelled
pub fn cancel_code(&self) -> Option<CancelCode> {
self.inner.lock().unwrap().cancel_code()
@ -310,18 +315,28 @@ impl Sas {
&self,
settings: AcceptSettings,
) -> Option<OutgoingVerificationRequest> {
self.inner.lock().unwrap().accept().map(|c| match settings.apply(c) {
OwnedAcceptContent::ToDevice(c) => {
let content = AnyToDeviceEventContent::KeyVerificationAccept(c);
self.content_to_request(content).into()
}
OwnedAcceptContent::Room(room_id, content) => RoomMessageRequest {
room_id,
txn_id: Uuid::new_v4(),
content: AnyMessageEventContent::KeyVerificationAccept(content),
}
.into(),
})
let mut guard = self.inner.lock().unwrap();
let sas: InnerSas = (*guard).clone();
if let Some((sas, content)) = sas.accept() {
*guard = sas;
let content = settings.apply(content);
Some(match content {
OwnedAcceptContent::ToDevice(c) => {
let content = AnyToDeviceEventContent::KeyVerificationAccept(c);
self.content_to_request(content).into()
}
OwnedAcceptContent::Room(room_id, content) => RoomMessageRequest {
room_id,
txn_id: Uuid::new_v4(),
content: AnyMessageEventContent::KeyVerificationAccept(content),
}
.into(),
})
} else {
None
}
}
/// Confirm the Sas verification.

View File

@ -241,6 +241,15 @@ pub struct Accepted {
commitment: String,
}
/// The SAS state we're going to be in after we accepted our
/// verification start event.
#[derive(Clone, Debug)]
pub struct WeAccepted {
we_started: bool,
pub accepted_protocols: Arc<AcceptedProtocols>,
commitment: String,
}
/// The SAS state we're going to be in after we received the public key of the
/// other participant.
///
@ -548,6 +557,24 @@ impl SasState<Started> {
}
}
pub fn into_accepted(self) -> SasState<WeAccepted> {
SasState {
inner: self.inner,
ids: self.ids,
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(WeAccepted {
we_started: false,
accepted_protocols: self.state.accepted_protocols.clone(),
commitment: self.state.commitment.clone(),
}),
}
}
}
impl SasState<WeAccepted> {
/// Get the content for the accept event.
///
/// The content needs to be sent to the other device.
@ -1093,7 +1120,7 @@ mod test {
};
use serde_json::json;
use super::{Accepted, Created, SasState, Started};
use super::{Accepted, Created, SasState, Started, WeAccepted};
use crate::{
verification::event_enums::{AcceptContent, KeyContent, MacContent, StartContent},
ReadOnlyAccount, ReadOnlyDevice,
@ -1115,7 +1142,7 @@ mod test {
"BOBDEVCIE".into()
}
async fn get_sas_pair() -> (SasState<Created>, SasState<Started>) {
async fn get_sas_pair() -> (SasState<Created>, SasState<WeAccepted>) {
let alice = ReadOnlyAccount::new(&alice_id(), &alice_device_id());
let alice_device = ReadOnlyDevice::from_account(&alice).await;
@ -1135,8 +1162,9 @@ mod test {
&start_content.as_start_content(),
false,
);
let bob_sas = bob_sas.unwrap().into_accepted();
(alice_sas, bob_sas.unwrap())
(alice_sas, bob_sas)
}
#[tokio::test]