Skip to content

Commit 798464b

Browse files
committed
Verification: Handle Accept after Start on both sides
Previously, when a sas-workflow was started by the other party (e.g. from a verification request), but the own start request was answered with an "accept" this accept would be ignored. However, this is incorrect since the official guide on implementing verification actually states that both parties are expected to send start-requests, where of those only one request is actually accepted (depending on user id and possibly the device id).
1 parent 5003ed1 commit 798464b

File tree

2 files changed

+100
-28
lines changed

2 files changed

+100
-28
lines changed

crates/matrix-sdk-crypto/src/verification/sas/inner_sas.rs

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ impl InnerSas {
181181
methods: Vec<ShortAuthenticationString>,
182182
) -> Option<(InnerSas, OwnedAcceptContent)> {
183183
if let InnerSas::Started(s) = self {
184-
let sas = s.into_accepted(methods);
184+
let sas = s.into_we_accepted(methods);
185185
let content = sas.as_content();
186186
Some((InnerSas::WeAccepted(sas), content))
187187
} else {
@@ -258,22 +258,29 @@ impl InnerSas {
258258
content: &AnyVerificationContent,
259259
) -> (Self, Option<OutgoingContent>) {
260260
match content {
261-
AnyVerificationContent::Accept(c) => {
262-
if let InnerSas::Created(s) = self {
263-
match s.into_accepted(sender, c) {
264-
Ok(s) => {
265-
let content = s.as_content();
266-
(InnerSas::Accepted(s), Some(content))
267-
}
268-
Err(s) => {
269-
let content = s.as_content();
270-
(InnerSas::Cancelled(s), Some(content))
271-
}
261+
AnyVerificationContent::Accept(c) => match self {
262+
InnerSas::Created(s) => match s.into_accepted(sender, c) {
263+
Ok(s) => {
264+
let content = s.as_content();
265+
(InnerSas::Accepted(s), Some(content))
272266
}
273-
} else {
274-
(self, None)
275-
}
276-
}
267+
Err(s) => {
268+
let content = s.as_content();
269+
(InnerSas::Cancelled(s), Some(content))
270+
}
271+
},
272+
InnerSas::Started(s) => match s.into_accepted(sender, c) {
273+
Ok(s) => {
274+
let content = s.as_content();
275+
(InnerSas::Accepted(s), Some(content))
276+
}
277+
Err(s) => {
278+
let content = s.as_content();
279+
(InnerSas::Cancelled(s), Some(content))
280+
}
281+
},
282+
_ => (self, None),
283+
},
277284
AnyVerificationContent::Cancel(c) => {
278285
let (sas, _) = self.cancel(false, c.cancel_code().to_owned());
279286
(sas, None)

crates/matrix-sdk-crypto/src/verification/sas/sas_state.rs

Lines changed: 77 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,17 @@ const MACS: &[MessageAuthenticationCode] = &[MessageAuthenticationCode::HkdfHmac
7070
const STRINGS: &[ShortAuthenticationString] =
7171
&[ShortAuthenticationString::Decimal, ShortAuthenticationString::Emoji];
7272

73+
fn the_protocol_definitions() -> SasV1Content {
74+
SasV1ContentInit {
75+
short_authentication_string: STRINGS.to_vec(),
76+
key_agreement_protocols: KEY_AGREEMENT_PROTOCOLS.to_vec(),
77+
message_authentication_codes: MACS.to_vec(),
78+
hashes: HASHES.to_vec(),
79+
}
80+
.try_into()
81+
.expect("Invalid protocol definition.")
82+
}
83+
7384
// The max time a SAS flow can take from start to done.
7485
const MAX_AGE: Duration = Duration::from_secs(60 * 5);
7586

@@ -420,16 +431,7 @@ impl SasState<Created> {
420431
last_event_time: Arc::new(Instant::now()),
421432
started_from_request,
422433

423-
state: Arc::new(Created {
424-
protocol_definitions: SasV1ContentInit {
425-
short_authentication_string: STRINGS.to_vec(),
426-
key_agreement_protocols: KEY_AGREEMENT_PROTOCOLS.to_vec(),
427-
message_authentication_codes: MACS.to_vec(),
428-
hashes: HASHES.to_vec(),
429-
}
430-
.try_into()
431-
.expect("Invalid protocol definition."),
432-
}),
434+
state: Arc::new(Created { protocol_definitions: the_protocol_definitions() }),
433435
}
434436
}
435437

@@ -571,7 +573,7 @@ impl SasState<Started> {
571573
}
572574
}
573575

574-
pub fn into_accepted(self, methods: Vec<ShortAuthenticationString>) -> SasState<WeAccepted> {
576+
pub fn into_we_accepted(self, methods: Vec<ShortAuthenticationString>) -> SasState<WeAccepted> {
575577
let mut accepted_protocols = self.state.accepted_protocols.as_ref().to_owned();
576578
accepted_protocols.short_auth_string = methods;
577579

@@ -594,6 +596,69 @@ impl SasState<Started> {
594596
}),
595597
}
596598
}
599+
600+
fn as_content(&self) -> OwnedStartContent {
601+
match self.verification_flow_id.as_ref() {
602+
FlowId::ToDevice(s) => {
603+
OwnedStartContent::ToDevice(ToDeviceKeyVerificationStartEventContent::new(
604+
self.device_id().into(),
605+
s.to_string(),
606+
StartMethod::SasV1(the_protocol_definitions()),
607+
))
608+
}
609+
FlowId::InRoom(r, e) => OwnedStartContent::Room(
610+
r.clone(),
611+
KeyVerificationStartEventContent::new(
612+
self.device_id().into(),
613+
StartMethod::SasV1(the_protocol_definitions()),
614+
Relation::new(e.clone()),
615+
),
616+
),
617+
}
618+
}
619+
620+
/// Receive a m.key.verification.accept event, changing the state into
621+
/// an Accepted one.
622+
///
623+
/// Note: Even though the other side has started the (or rather "a") sas
624+
/// verification, it can still accept one, if we have sent one
625+
/// simultaneously. In this case we just go on with the verification
626+
/// that *we* started.
627+
///
628+
/// # Arguments
629+
///
630+
/// * `event` - The m.key.verification.accept event that was sent to us by
631+
/// the other side.
632+
pub fn into_accepted(
633+
self,
634+
sender: &UserId,
635+
content: &AcceptContent,
636+
) -> Result<SasState<Accepted>, SasState<Cancelled>> {
637+
self.check_event(sender, content.flow_id()).map_err(|c| self.clone().cancel(true, c))?;
638+
639+
if let AcceptMethod::SasV1(content) = content.method() {
640+
let accepted_protocols = AcceptedProtocols::try_from(content.clone())
641+
.map_err(|c| self.clone().cancel(true, c))?;
642+
643+
let start_content = self.as_content().into();
644+
645+
Ok(SasState {
646+
inner: self.inner,
647+
ids: self.ids,
648+
verification_flow_id: self.verification_flow_id,
649+
creation_time: self.creation_time,
650+
last_event_time: Instant::now().into(),
651+
started_from_request: self.started_from_request,
652+
state: Arc::new(Accepted {
653+
start_content,
654+
commitment: content.commitment.clone(),
655+
accepted_protocols: accepted_protocols.into(),
656+
}),
657+
})
658+
} else {
659+
Err(self.cancel(true, CancelCode::UnknownMethod))
660+
}
661+
}
597662
}
598663

599664
impl SasState<WeAccepted> {
@@ -1191,7 +1256,7 @@ mod test {
11911256
&start_content.as_start_content(),
11921257
false,
11931258
);
1194-
let bob_sas = bob_sas.unwrap().into_accepted(vec![ShortAuthenticationString::Emoji]);
1259+
let bob_sas = bob_sas.unwrap().into_we_accepted(vec![ShortAuthenticationString::Emoji]);
11951260

11961261
(alice_sas, bob_sas)
11971262
}

0 commit comments

Comments
 (0)