1use std::{matches, sync::Arc, time::Duration};
16
17use matrix_sdk_common::locks::Mutex;
18use ruma::{
19 DeviceId, OwnedTransactionId, TransactionId, UserId,
20 events::{
21 AnyMessageLikeEventContent, AnyToDeviceEventContent,
22 key::verification::{
23 HashAlgorithm, KeyAgreementProtocol, MessageAuthenticationCode,
24 ShortAuthenticationString,
25 accept::{
26 AcceptMethod, KeyVerificationAcceptEventContent, SasV1Content as AcceptV1Content,
27 SasV1ContentInit as AcceptV1ContentInit, ToDeviceKeyVerificationAcceptEventContent,
28 },
29 cancel::CancelCode,
30 done::{KeyVerificationDoneEventContent, ToDeviceKeyVerificationDoneEventContent},
31 key::{KeyVerificationKeyEventContent, ToDeviceKeyVerificationKeyEventContent},
32 start::{
33 KeyVerificationStartEventContent, SasV1Content, SasV1ContentInit, StartMethod,
34 ToDeviceKeyVerificationStartEventContent,
35 },
36 },
37 relation::Reference,
38 },
39 serde::Base64,
40 time::Instant,
41};
42use serde::{Deserialize, Serialize};
43use tracing::info;
44use vodozemac::{
45 Curve25519PublicKey,
46 sas::{EstablishedSas, Mac, Sas},
47};
48
49use super::{
50 OutgoingContent,
51 helpers::{
52 SasIds, calculate_commitment, get_decimal, get_emoji, get_emoji_index, get_mac_content,
53 receive_mac_event,
54 },
55};
56use crate::{
57 OwnUserIdentityData,
58 identities::{DeviceData, UserIdentityData},
59 olm::StaticAccountData,
60 verification::{
61 Cancelled, Emoji, FlowId,
62 cache::RequestInfo,
63 event_enums::{
64 AcceptContent, DoneContent, KeyContent, MacContent, OwnedAcceptContent,
65 OwnedStartContent, StartContent,
66 },
67 },
68};
69
70const KEY_AGREEMENT_PROTOCOLS: &[KeyAgreementProtocol] =
71 &[KeyAgreementProtocol::Curve25519HkdfSha256];
72const HASHES: &[HashAlgorithm] = &[HashAlgorithm::Sha256];
73const STRINGS: &[ShortAuthenticationString] =
74 &[ShortAuthenticationString::Decimal, ShortAuthenticationString::Emoji];
75
76fn the_protocol_definitions(
77 short_auth_strings: Option<Vec<ShortAuthenticationString>>,
78) -> SasV1Content {
79 SasV1ContentInit {
80 short_authentication_string: short_auth_strings.unwrap_or_else(|| STRINGS.to_owned()),
81 key_agreement_protocols: KEY_AGREEMENT_PROTOCOLS.to_vec(),
82 message_authentication_codes: vec![
83 #[allow(deprecated)]
84 MessageAuthenticationCode::HkdfHmacSha256,
85 MessageAuthenticationCode::HkdfHmacSha256V2,
86 MessageAuthenticationCode::from("org.matrix.msc3783.hkdf-hmac-sha256"),
88 ],
89 hashes: HASHES.to_vec(),
90 }
91 .into()
92}
93
94const MAX_AGE: Duration = Duration::from_secs(60 * 5);
96
97const MAX_EVENT_TIMEOUT: Duration = Duration::from_secs(60);
99
100#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq)]
104pub enum SupportedMacMethod {
105 #[serde(rename = "hkdf-hmac-sha256")]
106 HkdfHmacSha256,
107 #[serde(rename = "hkdf-hmac-sha256.v2")]
108 HkdfHmacSha256V2,
109 #[serde(rename = "org.matrix.msc3783.hkdf-hmac-sha256")]
110 Msc3783HkdfHmacSha256V2,
111}
112
113impl AsRef<str> for SupportedMacMethod {
114 fn as_ref(&self) -> &str {
115 match self {
116 SupportedMacMethod::HkdfHmacSha256 => "hkdf-hmac-sha256",
117 SupportedMacMethod::HkdfHmacSha256V2 => "hkdf-hmac-sha256.v2",
118 SupportedMacMethod::Msc3783HkdfHmacSha256V2 => "org.matrix.msc3783.hkdf-hmac-sha256",
119 }
120 }
121}
122
123impl From<SupportedMacMethod> for MessageAuthenticationCode {
124 fn from(m: SupportedMacMethod) -> Self {
125 MessageAuthenticationCode::from(m.as_ref())
126 }
127}
128
129impl TryFrom<&MessageAuthenticationCode> for SupportedMacMethod {
130 type Error = ();
131
132 fn try_from(value: &MessageAuthenticationCode) -> Result<Self, Self::Error> {
133 match value.as_str() {
134 "hkdf-hmac-sha256" => Ok(Self::HkdfHmacSha256),
135 "org.matrix.msc3783.hkdf-hmac-sha256" => Ok(Self::Msc3783HkdfHmacSha256V2),
136 "hkdf-hmac-sha256.v2" => Ok(Self::HkdfHmacSha256V2),
137 _ => Err(()),
138 }
139 }
140}
141
142impl SupportedMacMethod {
143 pub fn verify_mac(
149 &self,
150 sas: &EstablishedSas,
151 input: &str,
152 info: &str,
153 mac: &Base64,
154 ) -> Result<(), CancelCode> {
155 match self {
156 SupportedMacMethod::HkdfHmacSha256 => {
157 let calculated_mac = sas.calculate_mac_invalid_base64(input, info);
158 let calculated_mac = Base64::parse(calculated_mac)
159 .expect("We can always decode a Mac from vodozemac");
160
161 if calculated_mac != *mac { Err(CancelCode::KeyMismatch) } else { Ok(()) }
162 }
163 SupportedMacMethod::HkdfHmacSha256V2 | SupportedMacMethod::Msc3783HkdfHmacSha256V2 => {
164 let mac = Mac::from_slice(mac.as_bytes());
165 sas.verify_mac(input, info, &mac).map_err(|_| CancelCode::MismatchedSas)
166 }
167 }
168 }
169
170 pub fn calculate_mac(&self, sas: &EstablishedSas, input: &str, info: &str) -> Base64 {
176 match self {
177 SupportedMacMethod::HkdfHmacSha256 => {
178 Base64::parse(sas.calculate_mac_invalid_base64(input, info))
179 .expect("We can always decode our newly generated Mac")
180 }
181 SupportedMacMethod::HkdfHmacSha256V2 | SupportedMacMethod::Msc3783HkdfHmacSha256V2 => {
182 let mac = sas.calculate_mac(input, info);
183 Base64::new(mac.as_bytes().to_vec())
184 }
185 }
186 }
187}
188
189#[derive(Clone, Debug, PartialEq, Eq)]
192pub struct AcceptedProtocols {
193 pub key_agreement_protocol: KeyAgreementProtocol,
195 pub hash: HashAlgorithm,
197 pub message_auth_code: SupportedMacMethod,
199 pub short_auth_string: Vec<ShortAuthenticationString>,
202}
203
204impl TryFrom<AcceptV1Content> for AcceptedProtocols {
205 type Error = CancelCode;
206
207 fn try_from(content: AcceptV1Content) -> Result<Self, Self::Error> {
208 if !KEY_AGREEMENT_PROTOCOLS.contains(&content.key_agreement_protocol)
209 || !HASHES.contains(&content.hash)
210 || (!content.short_authentication_string.contains(&ShortAuthenticationString::Emoji)
211 && !content
212 .short_authentication_string
213 .contains(&ShortAuthenticationString::Decimal))
214 {
215 Err(CancelCode::UnknownMethod)
216 } else {
217 let message_auth_code = (&content.message_authentication_code)
218 .try_into()
219 .map_err(|_| CancelCode::UnknownMethod)?;
220
221 Ok(Self {
222 hash: content.hash,
223 key_agreement_protocol: content.key_agreement_protocol,
224 message_auth_code,
225 short_auth_string: content.short_authentication_string,
226 })
227 }
228 }
229}
230
231impl TryFrom<&SasV1Content> for AcceptedProtocols {
232 type Error = CancelCode;
233
234 fn try_from(method_content: &SasV1Content) -> Result<Self, Self::Error> {
235 if !method_content
236 .key_agreement_protocols
237 .contains(&KeyAgreementProtocol::Curve25519HkdfSha256)
238 || !method_content.hashes.contains(&HashAlgorithm::Sha256)
239 || (!method_content
240 .short_authentication_string
241 .contains(&ShortAuthenticationString::Decimal)
242 && !method_content
243 .short_authentication_string
244 .contains(&ShortAuthenticationString::Emoji))
245 {
246 Err(CancelCode::UnknownMethod)
247 } else {
248 let mac_methods: Vec<SupportedMacMethod> = method_content
249 .message_authentication_codes
250 .iter()
251 .filter_map(|m| SupportedMacMethod::try_from(m).ok())
252 .collect();
253
254 let message_auth_code =
255 if mac_methods.contains(&SupportedMacMethod::HkdfHmacSha256V2) {
256 Some(SupportedMacMethod::HkdfHmacSha256V2)
257 } else if mac_methods.contains(&SupportedMacMethod::Msc3783HkdfHmacSha256V2) {
258 Some(SupportedMacMethod::Msc3783HkdfHmacSha256V2)
259 } else {
260 mac_methods.first().copied()
261 }
262 .ok_or(CancelCode::UnknownMethod)?;
263
264 let mut short_auth_string = vec![];
265
266 if method_content
267 .short_authentication_string
268 .contains(&ShortAuthenticationString::Decimal)
269 {
270 short_auth_string.push(ShortAuthenticationString::Decimal)
271 }
272
273 if method_content
274 .short_authentication_string
275 .contains(&ShortAuthenticationString::Emoji)
276 {
277 short_auth_string.push(ShortAuthenticationString::Emoji);
278 }
279
280 Ok(Self {
281 hash: HashAlgorithm::Sha256,
282 key_agreement_protocol: KeyAgreementProtocol::Curve25519HkdfSha256,
283 message_auth_code,
284 short_auth_string,
285 })
286 }
287 }
288}
289
290#[cfg(not(tarpaulin_include))]
291impl Default for AcceptedProtocols {
292 fn default() -> Self {
293 AcceptedProtocols {
294 hash: HashAlgorithm::Sha256,
295 key_agreement_protocol: KeyAgreementProtocol::Curve25519HkdfSha256,
296 message_auth_code: SupportedMacMethod::HkdfHmacSha256V2,
297 short_auth_string: vec![
298 ShortAuthenticationString::Decimal,
299 ShortAuthenticationString::Emoji,
300 ],
301 }
302 }
303}
304
305#[derive(Clone)]
310pub struct SasState<S: Clone> {
311 inner: Arc<Mutex<Option<Sas>>>,
313
314 our_public_key: Curve25519PublicKey,
316
317 ids: Box<SasIds>,
320
321 creation_time: Arc<Instant>,
325
326 last_event_time: Arc<Instant>,
328
329 pub verification_flow_id: Arc<FlowId>,
334
335 pub state: Arc<S>,
337
338 pub started_from_request: bool,
340}
341
342impl<S: Clone> SasState<S> {
343 fn handle_key_content(
344 &self,
345 sender: &UserId,
346 content: &KeyContent<'_>,
347 ) -> Result<EstablishedSas, CancelCode> {
348 self.check_event(sender, content.flow_id())?;
349
350 let their_public_key = Curve25519PublicKey::from_slice(content.public_key().as_bytes())
351 .map_err(|_| CancelCode::from("Invalid public key"))?;
352
353 if let Some(sas) = self.inner.lock().take() {
354 sas.diffie_hellman(their_public_key).map_err(|_| "Invalid public key".into())
355 } else {
356 Err(CancelCode::UnexpectedMessage)
357 }
358 }
359}
360
361#[cfg(not(tarpaulin_include))]
362impl<S: Clone + std::fmt::Debug> std::fmt::Debug for SasState<S> {
363 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
364 f.debug_struct("SasState")
365 .field("ids", &self.ids)
366 .field("flow_id", &self.verification_flow_id)
367 .field("state", &self.state)
368 .finish()
369 }
370}
371
372#[derive(Clone, Debug)]
374pub struct Created {
375 pub protocol_definitions: SasV1Content,
376}
377
378#[derive(Clone, Debug)]
380pub struct Started {
381 commitment: Base64,
382 pub protocol_definitions: SasV1Content,
383 pub accepted_protocols: AcceptedProtocols,
384}
385
386#[derive(Clone, Debug)]
389pub struct Accepted {
390 pub accepted_protocols: AcceptedProtocols,
391 start_content: Arc<OwnedStartContent>,
392 pub request_id: OwnedTransactionId,
393 commitment: Base64,
394}
395
396#[derive(Clone, Debug)]
399pub struct WeAccepted {
400 we_started: bool,
401 pub accepted_protocols: AcceptedProtocols,
402 commitment: Base64,
403}
404
405#[derive(Clone, Debug)]
410pub struct KeyReceived {
411 sas: Arc<Mutex<EstablishedSas>>,
412 we_started: bool,
413 pub request_id: OwnedTransactionId,
414 pub accepted_protocols: AcceptedProtocols,
415}
416
417#[derive(Clone, Debug)]
418pub struct KeySent {
419 we_started: bool,
420 start_content: Arc<OwnedStartContent>,
421 commitment: Base64,
422 pub accepted_protocols: AcceptedProtocols,
423}
424
425#[derive(Clone, Debug)]
426pub struct KeysExchanged {
427 sas: Arc<Mutex<EstablishedSas>>,
428 we_started: bool,
429 pub accepted_protocols: AcceptedProtocols,
430}
431
432#[derive(Clone, Debug)]
436pub struct Confirmed {
437 sas: Arc<Mutex<EstablishedSas>>,
438 pub accepted_protocols: AcceptedProtocols,
439}
440
441#[derive(Clone, Debug)]
445pub struct MacReceived {
446 sas: Arc<Mutex<EstablishedSas>>,
447 we_started: bool,
448 verified_devices: Arc<[DeviceData]>,
449 verified_master_keys: Arc<[UserIdentityData]>,
450 pub accepted_protocols: AcceptedProtocols,
451}
452
453#[derive(Clone, Debug)]
457pub struct WaitingForDone {
458 sas: Arc<Mutex<EstablishedSas>>,
459 verified_devices: Arc<[DeviceData]>,
460 verified_master_keys: Arc<[UserIdentityData]>,
461 pub accepted_protocols: AcceptedProtocols,
462}
463
464#[derive(Clone, Debug)]
470pub struct Done {
471 sas: Arc<Mutex<EstablishedSas>>,
472 verified_devices: Arc<[DeviceData]>,
473 verified_master_keys: Arc<[UserIdentityData]>,
474 pub accepted_protocols: AcceptedProtocols,
475}
476
477impl<S: Clone> SasState<S> {
478 #[cfg(test)]
480 pub fn user_id(&self) -> &UserId {
481 &self.ids.account.user_id
482 }
483
484 pub fn device_id(&self) -> &DeviceId {
486 &self.ids.account.device_id
487 }
488
489 #[cfg(test)]
490 pub fn other_device(&self) -> DeviceData {
491 self.ids.other_device.clone()
492 }
493
494 pub fn cancel(self, cancelled_by_us: bool, cancel_code: CancelCode) -> SasState<Cancelled> {
495 SasState {
496 inner: self.inner,
497 our_public_key: self.our_public_key,
498 ids: self.ids,
499 creation_time: self.creation_time,
500 last_event_time: self.last_event_time,
501 verification_flow_id: self.verification_flow_id,
502 state: Arc::new(Cancelled::new(cancelled_by_us, cancel_code)),
503 started_from_request: self.started_from_request,
504 }
505 }
506
507 pub fn timed_out(&self) -> bool {
509 self.creation_time.elapsed() > MAX_AGE || self.last_event_time.elapsed() > MAX_EVENT_TIMEOUT
510 }
511
512 #[allow(dead_code)]
514 pub fn is_dm_verification(&self) -> bool {
515 matches!(&*self.verification_flow_id, FlowId::InRoom(_, _))
516 }
517
518 #[cfg(test)]
519 #[allow(dead_code)]
520 pub fn set_creation_time(&mut self, time: Instant) {
521 self.creation_time = Arc::new(time);
522 }
523
524 fn check_event(&self, sender: &UserId, flow_id: &str) -> Result<(), CancelCode> {
525 if *flow_id != *self.verification_flow_id.as_str() {
526 Err(CancelCode::UnknownTransaction)
527 } else if sender != self.ids.other_device.user_id() {
528 Err(CancelCode::UserMismatch)
529 } else if self.timed_out() {
530 Err(CancelCode::Timeout)
531 } else {
532 Ok(())
533 }
534 }
535}
536
537impl SasState<Created> {
538 pub fn new(
548 account: StaticAccountData,
549 other_device: DeviceData,
550 own_identity: Option<OwnUserIdentityData>,
551 other_identity: Option<UserIdentityData>,
552 flow_id: FlowId,
553 started_from_request: bool,
554 short_auth_strings: Option<Vec<ShortAuthenticationString>>,
555 ) -> SasState<Created> {
556 Self::new_helper(
557 flow_id,
558 account,
559 other_device,
560 own_identity,
561 other_identity,
562 started_from_request,
563 short_auth_strings,
564 )
565 }
566
567 fn new_helper(
568 flow_id: FlowId,
569 account: StaticAccountData,
570 other_device: DeviceData,
571 own_identity: Option<OwnUserIdentityData>,
572 other_identity: Option<UserIdentityData>,
573 started_from_request: bool,
574 short_auth_strings: Option<Vec<ShortAuthenticationString>>,
575 ) -> SasState<Created> {
576 let sas = Sas::new();
577 let our_public_key = sas.public_key();
578
579 let protocol_definitions = the_protocol_definitions(short_auth_strings);
580
581 SasState {
582 inner: Arc::new(Mutex::new(Some(sas))),
583 our_public_key,
584 ids: Box::new(SasIds { account, other_device, other_identity, own_identity }),
585 verification_flow_id: flow_id.into(),
586
587 creation_time: Arc::new(Instant::now()),
588 last_event_time: Arc::new(Instant::now()),
589 started_from_request,
590
591 state: Arc::new(Created { protocol_definitions }),
592 }
593 }
594
595 pub fn as_content(&self) -> OwnedStartContent {
596 match self.verification_flow_id.as_ref() {
597 FlowId::ToDevice(s) => {
598 OwnedStartContent::ToDevice(ToDeviceKeyVerificationStartEventContent::new(
599 self.device_id().into(),
600 s.clone(),
601 StartMethod::SasV1(self.state.protocol_definitions.clone()),
602 ))
603 }
604 FlowId::InRoom(r, e) => OwnedStartContent::Room(
605 r.clone(),
606 KeyVerificationStartEventContent::new(
607 self.device_id().into(),
608 StartMethod::SasV1(self.state.protocol_definitions.clone()),
609 Reference::new(e.clone()),
610 ),
611 ),
612 }
613 }
614
615 pub fn into_accepted(
623 self,
624 sender: &UserId,
625 content: &AcceptContent<'_>,
626 ) -> Result<SasState<Accepted>, SasState<Cancelled>> {
627 self.check_event(sender, content.flow_id()).map_err(|c| self.clone().cancel(true, c))?;
628
629 let AcceptMethod::SasV1(content) = content.method() else {
630 return Err(self.cancel(true, CancelCode::UnknownMethod));
631 };
632
633 let accepted_protocols = AcceptedProtocols::try_from(content.clone())
634 .map_err(|c| self.clone().cancel(true, c))?;
635
636 let start_content = self.as_content().into();
637
638 Ok(SasState {
639 inner: self.inner,
640 our_public_key: self.our_public_key,
641 ids: self.ids,
642 verification_flow_id: self.verification_flow_id,
643 creation_time: self.creation_time,
644 last_event_time: Instant::now().into(),
645 started_from_request: self.started_from_request,
646 state: Arc::new(Accepted {
647 start_content,
648 commitment: content.commitment.clone(),
649 request_id: TransactionId::new(),
650 accepted_protocols,
651 }),
652 })
653 }
654}
655
656impl SasState<Started> {
657 pub fn from_start_event(
669 account: StaticAccountData,
670 other_device: DeviceData,
671 own_identity: Option<OwnUserIdentityData>,
672 other_identity: Option<UserIdentityData>,
673 flow_id: FlowId,
674 content: &StartContent<'_>,
675 started_from_request: bool,
676 ) -> Result<SasState<Started>, SasState<Cancelled>> {
677 let flow_id = Arc::new(flow_id);
678
679 let sas = Sas::new();
680 let our_public_key = sas.public_key();
681
682 let canceled = || SasState {
683 inner: Arc::new(Mutex::new(None)),
684 our_public_key,
685
686 creation_time: Arc::new(Instant::now()),
687 last_event_time: Arc::new(Instant::now()),
688 started_from_request,
689
690 ids: Box::new(SasIds {
691 account: account.clone(),
692 other_device: other_device.clone(),
693 own_identity: own_identity.clone(),
694 other_identity: other_identity.clone(),
695 }),
696
697 verification_flow_id: flow_id.clone(),
698 state: Arc::new(Cancelled::new(true, CancelCode::UnknownMethod)),
699 };
700
701 let state = match content.method() {
702 StartMethod::SasV1(method_content) => {
703 let commitment = calculate_commitment(our_public_key, content);
704
705 info!(
706 public_key = our_public_key.to_base64(),
707 ?commitment,
708 ?content,
709 "Calculated SAS commitment",
710 );
711
712 let Ok(accepted_protocols) = AcceptedProtocols::try_from(method_content) else {
713 return Err(canceled());
714 };
715
716 Started {
717 protocol_definitions: method_content.to_owned(),
718 accepted_protocols,
719 commitment,
720 }
721 }
722 _ => return Err(canceled()),
723 };
724
725 Ok(SasState {
726 inner: Arc::new(Mutex::new(Some(sas))),
727 our_public_key,
728
729 ids: Box::new(SasIds { account, other_device, other_identity, own_identity }),
730
731 creation_time: Arc::new(Instant::now()),
732 last_event_time: Arc::new(Instant::now()),
733 started_from_request,
734
735 verification_flow_id: flow_id,
736
737 state: Arc::new(state),
738 })
739 }
740
741 #[cfg(test)]
742 fn into_we_accepted_with_mac_method(
743 self,
744 methods: Vec<ShortAuthenticationString>,
745 mac_method: Option<SupportedMacMethod>,
746 ) -> SasState<WeAccepted> {
747 let mut accepted_protocols = self.state.accepted_protocols.to_owned();
748
749 if let Some(mac_method) = mac_method {
750 accepted_protocols.message_auth_code = mac_method;
751 }
752
753 self.into_we_accepted_helper(accepted_protocols, methods)
754 }
755
756 fn into_we_accepted_helper(
757 self,
758 mut accepted_protocols: AcceptedProtocols,
759 methods: Vec<ShortAuthenticationString>,
760 ) -> SasState<WeAccepted> {
761 accepted_protocols.short_auth_string = methods;
762
763 if !accepted_protocols.short_auth_string.contains(&ShortAuthenticationString::Decimal) {
765 accepted_protocols.short_auth_string.push(ShortAuthenticationString::Decimal);
766 }
767
768 SasState {
769 inner: self.inner,
770 our_public_key: self.our_public_key,
771 ids: self.ids,
772 verification_flow_id: self.verification_flow_id,
773 creation_time: self.creation_time,
774 last_event_time: self.last_event_time,
775 started_from_request: self.started_from_request,
776 state: Arc::new(WeAccepted {
777 we_started: false,
778 accepted_protocols,
779 commitment: self.state.commitment.clone(),
780 }),
781 }
782 }
783
784 pub fn into_we_accepted(self, methods: Vec<ShortAuthenticationString>) -> SasState<WeAccepted> {
785 let accepted_protocols = self.state.accepted_protocols.to_owned();
786 self.into_we_accepted_helper(accepted_protocols, methods)
787 }
788
789 fn as_content(&self) -> OwnedStartContent {
790 match self.verification_flow_id.as_ref() {
791 FlowId::ToDevice(s) => {
792 OwnedStartContent::ToDevice(ToDeviceKeyVerificationStartEventContent::new(
793 self.device_id().into(),
794 s.clone(),
795 StartMethod::SasV1(self.state.protocol_definitions.to_owned()),
796 ))
797 }
798 FlowId::InRoom(r, e) => OwnedStartContent::Room(
799 r.clone(),
800 KeyVerificationStartEventContent::new(
801 self.device_id().into(),
802 StartMethod::SasV1(self.state.protocol_definitions.to_owned()),
803 Reference::new(e.clone()),
804 ),
805 ),
806 }
807 }
808
809 pub fn into_accepted(
822 self,
823 sender: &UserId,
824 content: &AcceptContent<'_>,
825 ) -> Result<SasState<Accepted>, SasState<Cancelled>> {
826 self.check_event(sender, content.flow_id()).map_err(|c| self.clone().cancel(true, c))?;
827
828 let AcceptMethod::SasV1(content) = content.method() else {
829 return Err(self.cancel(true, CancelCode::UnknownMethod));
830 };
831
832 let accepted_protocols = AcceptedProtocols::try_from(content.clone())
833 .map_err(|c| self.clone().cancel(true, c))?;
834
835 let start_content = self.as_content().into();
836
837 Ok(SasState {
838 inner: self.inner,
839 our_public_key: self.our_public_key,
840 ids: self.ids,
841 verification_flow_id: self.verification_flow_id,
842 creation_time: self.creation_time,
843 last_event_time: Instant::now().into(),
844 started_from_request: self.started_from_request,
845 state: Arc::new(Accepted {
846 start_content,
847 commitment: content.commitment.clone(),
848 request_id: TransactionId::new(),
849 accepted_protocols,
850 }),
851 })
852 }
853}
854
855impl SasState<WeAccepted> {
856 pub fn as_content(&self) -> OwnedAcceptContent {
864 let method = AcceptMethod::SasV1(
865 AcceptV1ContentInit {
866 commitment: self.state.commitment.clone(),
867 hash: self.state.accepted_protocols.hash.clone(),
868 key_agreement_protocol: self
869 .state
870 .accepted_protocols
871 .key_agreement_protocol
872 .clone(),
873 message_authentication_code: self.state.accepted_protocols.message_auth_code.into(),
874 short_authentication_string: self
875 .state
876 .accepted_protocols
877 .short_auth_string
878 .clone(),
879 }
880 .into(),
881 );
882
883 match self.verification_flow_id.as_ref() {
884 FlowId::ToDevice(s) => {
885 ToDeviceKeyVerificationAcceptEventContent::new(s.clone(), method).into()
886 }
887 FlowId::InRoom(r, e) => (
888 r.clone(),
889 KeyVerificationAcceptEventContent::new(method, Reference::new(e.clone())),
890 )
891 .into(),
892 }
893 }
894
895 pub fn into_key_received(
904 self,
905 sender: &UserId,
906 content: &KeyContent<'_>,
907 ) -> Result<SasState<KeyReceived>, SasState<Cancelled>> {
908 let established =
909 self.handle_key_content(sender, content).map_err(|c| self.clone().cancel(true, c))?;
910
911 Ok(SasState {
912 inner: self.inner,
913 our_public_key: self.our_public_key,
914 ids: self.ids,
915 verification_flow_id: self.verification_flow_id,
916 creation_time: self.creation_time,
917 last_event_time: Instant::now().into(),
918 started_from_request: self.started_from_request,
919 state: Arc::new(KeyReceived {
920 sas: Mutex::new(established).into(),
921 we_started: self.state.we_started,
922 request_id: TransactionId::new(),
923 accepted_protocols: self.state.accepted_protocols.clone(),
924 }),
925 })
926 }
927}
928
929impl SasState<Accepted> {
930 pub fn into_key_received(
939 self,
940 sender: &UserId,
941 content: &KeyContent<'_>,
942 ) -> Result<SasState<KeyReceived>, SasState<Cancelled>> {
943 let established =
944 self.handle_key_content(sender, content).map_err(|c| self.clone().cancel(true, c))?;
945
946 let their_public_key = established.their_public_key();
947
948 let commitment =
949 calculate_commitment(their_public_key, &self.state.start_content.as_start_content());
950
951 if self.state.commitment == commitment {
952 Ok(SasState {
953 inner: self.inner,
954 our_public_key: self.our_public_key,
955 ids: self.ids,
956 verification_flow_id: self.verification_flow_id,
957 creation_time: self.creation_time,
958 last_event_time: Instant::now().into(),
959 started_from_request: self.started_from_request,
960 state: Arc::new(KeyReceived {
961 sas: Mutex::new(established).into(),
962 we_started: true,
963 request_id: self.state.request_id.to_owned(),
964 accepted_protocols: self.state.accepted_protocols.clone(),
965 }),
966 })
967 } else {
968 Err(self.cancel(true, CancelCode::MismatchedCommitment))
969 }
970 }
971
972 pub fn into_key_sent(self, request_id: &TransactionId) -> Option<SasState<KeySent>> {
973 (self.state.request_id == request_id).then(|| SasState {
974 inner: self.inner,
975 our_public_key: self.our_public_key,
976 ids: self.ids,
977 verification_flow_id: self.verification_flow_id,
978 creation_time: self.creation_time,
979 last_event_time: Instant::now().into(),
980 started_from_request: self.started_from_request,
981 state: Arc::new(KeySent {
982 we_started: true,
983 start_content: self.state.start_content.clone(),
984 commitment: self.state.commitment.clone(),
985 accepted_protocols: self.state.accepted_protocols.clone(),
986 }),
987 })
988 }
989
990 pub fn as_content(&self) -> (OutgoingContent, RequestInfo) {
994 let content = match &*self.verification_flow_id {
995 FlowId::ToDevice(s) => AnyToDeviceEventContent::KeyVerificationKey(
996 ToDeviceKeyVerificationKeyEventContent::new(
997 s.clone(),
998 Base64::new(self.our_public_key.to_vec()),
999 ),
1000 )
1001 .into(),
1002 FlowId::InRoom(r, e) => (
1003 r.clone(),
1004 AnyMessageLikeEventContent::KeyVerificationKey(
1005 KeyVerificationKeyEventContent::new(
1006 Base64::new(self.our_public_key.to_vec()),
1007 Reference::new(e.clone()),
1008 ),
1009 ),
1010 )
1011 .into(),
1012 };
1013
1014 (
1015 content,
1016 RequestInfo {
1017 flow_id: (*self.verification_flow_id).to_owned(),
1018 request_id: self.state.request_id.to_owned(),
1019 },
1020 )
1021 }
1022}
1023
1024impl SasState<KeySent> {
1025 pub fn into_keys_exchanged(
1026 self,
1027 sender: &UserId,
1028 content: &KeyContent<'_>,
1029 ) -> Result<SasState<KeysExchanged>, SasState<Cancelled>> {
1030 let established =
1031 self.handle_key_content(sender, content).map_err(|c| self.clone().cancel(true, c))?;
1032
1033 let their_public_key = established.their_public_key();
1034 let commitment =
1035 calculate_commitment(their_public_key, &self.state.start_content.as_start_content());
1036
1037 if self.state.commitment == commitment {
1038 Ok(SasState {
1039 inner: self.inner,
1040 our_public_key: self.our_public_key,
1041 ids: self.ids,
1042 verification_flow_id: self.verification_flow_id,
1043 creation_time: self.creation_time,
1044 last_event_time: Instant::now().into(),
1045 started_from_request: self.started_from_request,
1046 state: Arc::new(KeysExchanged {
1047 sas: Mutex::new(established).into(),
1048 we_started: self.state.we_started,
1049 accepted_protocols: self.state.accepted_protocols.clone(),
1050 }),
1051 })
1052 } else {
1053 Err(self.cancel(true, CancelCode::MismatchedCommitment))
1054 }
1055 }
1056}
1057
1058impl SasState<KeyReceived> {
1059 pub fn as_content(&self) -> (OutgoingContent, RequestInfo) {
1064 let content = match &*self.verification_flow_id {
1065 FlowId::ToDevice(s) => AnyToDeviceEventContent::KeyVerificationKey(
1066 ToDeviceKeyVerificationKeyEventContent::new(
1067 s.clone(),
1068 Base64::new(self.our_public_key.to_vec()),
1069 ),
1070 )
1071 .into(),
1072 FlowId::InRoom(r, e) => (
1073 r.clone(),
1074 AnyMessageLikeEventContent::KeyVerificationKey(
1075 KeyVerificationKeyEventContent::new(
1076 Base64::new(self.our_public_key.to_vec()),
1077 Reference::new(e.clone()),
1078 ),
1079 ),
1080 )
1081 .into(),
1082 };
1083
1084 (
1085 content,
1086 RequestInfo {
1087 flow_id: (*self.verification_flow_id).to_owned(),
1088 request_id: self.state.request_id.to_owned(),
1089 },
1090 )
1091 }
1092
1093 pub fn into_keys_exchanged(
1094 self,
1095 request_id: &TransactionId,
1096 ) -> Option<SasState<KeysExchanged>> {
1097 (self.state.request_id == request_id).then(|| SasState {
1098 inner: self.inner,
1099 our_public_key: self.our_public_key,
1100 ids: self.ids,
1101 verification_flow_id: self.verification_flow_id,
1102 creation_time: self.creation_time,
1103 last_event_time: Instant::now().into(),
1104 started_from_request: self.started_from_request,
1105 state: KeysExchanged {
1106 sas: self.state.sas.clone(),
1107 we_started: self.state.we_started,
1108 accepted_protocols: self.state.accepted_protocols.clone(),
1109 }
1110 .into(),
1111 })
1112 }
1113}
1114
1115impl SasState<KeysExchanged> {
1116 pub fn get_emoji(&self) -> [Emoji; 7] {
1121 get_emoji(
1122 &self.state.sas.lock(),
1123 &self.ids,
1124 self.verification_flow_id.as_str(),
1125 self.state.we_started,
1126 )
1127 }
1128
1129 pub fn get_emoji_index(&self) -> [u8; 7] {
1134 get_emoji_index(
1135 &self.state.sas.lock(),
1136 &self.ids,
1137 self.verification_flow_id.as_str(),
1138 self.state.we_started,
1139 )
1140 }
1141
1142 pub fn get_decimal(&self) -> (u16, u16, u16) {
1147 get_decimal(
1148 &self.state.sas.lock(),
1149 &self.ids,
1150 self.verification_flow_id.as_str(),
1151 self.state.we_started,
1152 )
1153 }
1154
1155 pub fn into_mac_received(
1163 self,
1164 sender: &UserId,
1165 content: &MacContent<'_>,
1166 ) -> Result<SasState<MacReceived>, SasState<Cancelled>> {
1167 self.check_event(sender, content.flow_id()).map_err(|c| self.clone().cancel(true, c))?;
1168
1169 let (devices, master_keys) = receive_mac_event(
1170 &self.state.sas.lock(),
1171 &self.ids,
1172 self.verification_flow_id.as_str(),
1173 sender,
1174 self.state.accepted_protocols.message_auth_code,
1175 content,
1176 )
1177 .map_err(|c| self.clone().cancel(true, c))?;
1178
1179 Ok(SasState {
1180 inner: self.inner,
1181 our_public_key: self.our_public_key,
1182 verification_flow_id: self.verification_flow_id,
1183 creation_time: self.creation_time,
1184 last_event_time: Instant::now().into(),
1185 ids: self.ids,
1186 started_from_request: self.started_from_request,
1187 state: Arc::new(MacReceived {
1188 sas: self.state.sas.clone(),
1189 we_started: self.state.we_started,
1190 verified_devices: devices.into(),
1191 verified_master_keys: master_keys.into(),
1192 accepted_protocols: self.state.accepted_protocols.clone(),
1193 }),
1194 })
1195 }
1196
1197 pub fn confirm(self) -> SasState<Confirmed> {
1202 SasState {
1203 inner: self.inner,
1204 our_public_key: self.our_public_key,
1205 started_from_request: self.started_from_request,
1206 verification_flow_id: self.verification_flow_id,
1207 creation_time: self.creation_time,
1208 last_event_time: self.last_event_time,
1209 ids: self.ids,
1210 state: Arc::new(Confirmed {
1211 sas: self.state.sas.clone(),
1212 accepted_protocols: self.state.accepted_protocols.clone(),
1213 }),
1214 }
1215 }
1216}
1217
1218impl SasState<Confirmed> {
1219 pub fn into_done(
1227 self,
1228 sender: &UserId,
1229 content: &MacContent<'_>,
1230 ) -> Result<SasState<Done>, SasState<Cancelled>> {
1231 self.check_event(sender, content.flow_id()).map_err(|c| self.clone().cancel(true, c))?;
1232
1233 let (devices, master_keys) = receive_mac_event(
1234 &self.state.sas.lock(),
1235 &self.ids,
1236 self.verification_flow_id.as_str(),
1237 sender,
1238 self.state.accepted_protocols.message_auth_code,
1239 content,
1240 )
1241 .map_err(|c| self.clone().cancel(true, c))?;
1242
1243 Ok(SasState {
1244 inner: self.inner,
1245 our_public_key: self.our_public_key,
1246 creation_time: self.creation_time,
1247 last_event_time: Instant::now().into(),
1248 verification_flow_id: self.verification_flow_id,
1249 started_from_request: self.started_from_request,
1250 ids: self.ids,
1251
1252 state: Arc::new(Done {
1253 sas: self.state.sas.clone(),
1254 verified_devices: devices.into(),
1255 verified_master_keys: master_keys.into(),
1256 accepted_protocols: self.state.accepted_protocols.clone(),
1257 }),
1258 })
1259 }
1260
1261 pub fn into_waiting_for_done(
1271 self,
1272 sender: &UserId,
1273 content: &MacContent<'_>,
1274 ) -> Result<SasState<WaitingForDone>, SasState<Cancelled>> {
1275 self.check_event(sender, content.flow_id()).map_err(|c| self.clone().cancel(true, c))?;
1276
1277 let (devices, master_keys) = receive_mac_event(
1278 &self.state.sas.lock(),
1279 &self.ids,
1280 self.verification_flow_id.as_str(),
1281 sender,
1282 self.state.accepted_protocols.message_auth_code,
1283 content,
1284 )
1285 .map_err(|c| self.clone().cancel(true, c))?;
1286
1287 Ok(SasState {
1288 inner: self.inner,
1289 our_public_key: self.our_public_key,
1290 creation_time: self.creation_time,
1291 last_event_time: Instant::now().into(),
1292 verification_flow_id: self.verification_flow_id,
1293 started_from_request: self.started_from_request,
1294 ids: self.ids,
1295
1296 state: Arc::new(WaitingForDone {
1297 sas: self.state.sas.clone(),
1298 verified_devices: devices.into(),
1299 verified_master_keys: master_keys.into(),
1300 accepted_protocols: self.state.accepted_protocols.clone(),
1301 }),
1302 })
1303 }
1304
1305 pub fn as_content(&self) -> OutgoingContent {
1309 get_mac_content(
1310 &self.state.sas.lock(),
1311 &self.ids,
1312 &self.verification_flow_id,
1313 self.state.accepted_protocols.message_auth_code,
1314 )
1315 }
1316}
1317
1318impl SasState<MacReceived> {
1319 pub fn confirm(self) -> SasState<Done> {
1324 SasState {
1325 inner: self.inner,
1326 our_public_key: self.our_public_key,
1327 verification_flow_id: self.verification_flow_id,
1328 creation_time: self.creation_time,
1329 started_from_request: self.started_from_request,
1330 last_event_time: self.last_event_time,
1331 ids: self.ids,
1332 state: Arc::new(Done {
1333 sas: self.state.sas.clone(),
1334 verified_devices: self.state.verified_devices.clone(),
1335 verified_master_keys: self.state.verified_master_keys.clone(),
1336 accepted_protocols: self.state.accepted_protocols.clone(),
1337 }),
1338 }
1339 }
1340
1341 pub fn confirm_and_wait_for_done(self) -> SasState<WaitingForDone> {
1348 SasState {
1349 inner: self.inner,
1350 our_public_key: self.our_public_key,
1351 verification_flow_id: self.verification_flow_id,
1352 creation_time: self.creation_time,
1353 started_from_request: self.started_from_request,
1354 last_event_time: self.last_event_time,
1355 ids: self.ids,
1356 state: Arc::new(WaitingForDone {
1357 sas: self.state.sas.clone(),
1358 verified_devices: self.state.verified_devices.clone(),
1359 verified_master_keys: self.state.verified_master_keys.clone(),
1360 accepted_protocols: self.state.accepted_protocols.clone(),
1361 }),
1362 }
1363 }
1364
1365 pub fn get_emoji(&self) -> [Emoji; 7] {
1370 get_emoji(
1371 &self.state.sas.lock(),
1372 &self.ids,
1373 self.verification_flow_id.as_str(),
1374 self.state.we_started,
1375 )
1376 }
1377
1378 pub fn get_emoji_index(&self) -> [u8; 7] {
1383 get_emoji_index(
1384 &self.state.sas.lock(),
1385 &self.ids,
1386 self.verification_flow_id.as_str(),
1387 self.state.we_started,
1388 )
1389 }
1390
1391 pub fn get_decimal(&self) -> (u16, u16, u16) {
1396 get_decimal(
1397 &self.state.sas.lock(),
1398 &self.ids,
1399 self.verification_flow_id.as_str(),
1400 self.state.we_started,
1401 )
1402 }
1403}
1404
1405impl SasState<WaitingForDone> {
1406 pub fn as_content(&self) -> OutgoingContent {
1411 get_mac_content(
1412 &self.state.sas.lock(),
1413 &self.ids,
1414 &self.verification_flow_id,
1415 self.state.accepted_protocols.message_auth_code,
1416 )
1417 }
1418
1419 pub fn done_content(&self) -> OutgoingContent {
1420 match self.verification_flow_id.as_ref() {
1421 FlowId::ToDevice(t) => AnyToDeviceEventContent::KeyVerificationDone(
1422 ToDeviceKeyVerificationDoneEventContent::new(t.to_owned()),
1423 )
1424 .into(),
1425 FlowId::InRoom(r, e) => (
1426 r.clone(),
1427 AnyMessageLikeEventContent::KeyVerificationDone(
1428 KeyVerificationDoneEventContent::new(Reference::new(e.clone())),
1429 ),
1430 )
1431 .into(),
1432 }
1433 }
1434
1435 pub fn into_done(
1443 self,
1444 sender: &UserId,
1445 content: &DoneContent<'_>,
1446 ) -> Result<SasState<Done>, SasState<Cancelled>> {
1447 self.check_event(sender, content.flow_id()).map_err(|c| self.clone().cancel(true, c))?;
1448
1449 Ok(SasState {
1450 inner: self.inner,
1451 our_public_key: self.our_public_key,
1452 creation_time: self.creation_time,
1453 last_event_time: Instant::now().into(),
1454 verification_flow_id: self.verification_flow_id,
1455 started_from_request: self.started_from_request,
1456 ids: self.ids,
1457
1458 state: Arc::new(Done {
1459 sas: self.state.sas.clone(),
1460 verified_devices: self.state.verified_devices.clone(),
1461 verified_master_keys: self.state.verified_master_keys.clone(),
1462 accepted_protocols: self.state.accepted_protocols.clone(),
1463 }),
1464 })
1465 }
1466}
1467
1468impl SasState<Done> {
1469 pub fn as_content(&self) -> OutgoingContent {
1474 get_mac_content(
1475 &self.state.sas.lock(),
1476 &self.ids,
1477 &self.verification_flow_id,
1478 self.state.accepted_protocols.message_auth_code,
1479 )
1480 }
1481
1482 pub fn verified_devices(&self) -> Arc<[DeviceData]> {
1484 self.state.verified_devices.clone()
1485 }
1486
1487 pub fn verified_identities(&self) -> Arc<[UserIdentityData]> {
1489 self.state.verified_master_keys.clone()
1490 }
1491}
1492
1493impl SasState<Cancelled> {
1494 pub fn as_content(&self) -> OutgoingContent {
1495 self.state.as_content(&self.verification_flow_id)
1496 }
1497}
1498
1499#[cfg(test)]
1500mod tests {
1501 use matrix_sdk_test::async_test;
1502 use ruma::{
1503 DeviceId, TransactionId, UserId, device_id,
1504 events::key::verification::{
1505 HashAlgorithm, KeyAgreementProtocol, MessageAuthenticationCode,
1506 ShortAuthenticationString,
1507 accept::{AcceptMethod, ToDeviceKeyVerificationAcceptEventContent},
1508 cancel::CancelCode,
1509 start::{
1510 SasV1Content, SasV1ContentInit, StartMethod,
1511 ToDeviceKeyVerificationStartEventContent,
1512 },
1513 },
1514 serde::Base64,
1515 user_id,
1516 };
1517 use serde_json::json;
1518 use strass::assert_let;
1519
1520 use super::{Accepted, Created, SasState, Started, SupportedMacMethod, WeAccepted};
1521 use crate::{
1522 AcceptedProtocols, Account, DeviceData,
1523 verification::{
1524 FlowId,
1525 event_enums::{AcceptContent, CancelContent, KeyContent, MacContent, StartContent},
1526 },
1527 };
1528
1529 fn alice_id() -> &'static UserId {
1530 user_id!("@alice:example.org")
1531 }
1532
1533 fn alice_device_id() -> &'static DeviceId {
1534 device_id!("JLAFKJWSCS")
1535 }
1536
1537 fn bob_id() -> &'static UserId {
1538 user_id!("@bob:example.org")
1539 }
1540
1541 fn bob_device_id() -> &'static DeviceId {
1542 device_id!("BOBDEVICE")
1543 }
1544
1545 fn get_sas_pair(
1546 mac_method: Option<SupportedMacMethod>,
1547 ) -> (SasState<Created>, SasState<WeAccepted>) {
1548 let alice = Account::with_device_id(alice_id(), alice_device_id());
1549 let alice_device = DeviceData::from_account(&alice);
1550
1551 let bob = Account::with_device_id(bob_id(), bob_device_id());
1552 let bob_device = DeviceData::from_account(&bob);
1553
1554 let flow_id = TransactionId::new().into();
1555 let alice_sas = SasState::<Created>::new(
1556 alice.static_data().clone(),
1557 bob_device,
1558 None,
1559 None,
1560 flow_id,
1561 false,
1562 None,
1563 );
1564
1565 let start_content = alice_sas.as_content();
1566 let flow_id = start_content.flow_id();
1567
1568 let bob_sas = SasState::<Started>::from_start_event(
1569 bob.static_data().clone(),
1570 alice_device,
1571 None,
1572 None,
1573 flow_id,
1574 &start_content.as_start_content(),
1575 false,
1576 );
1577 let bob_sas = bob_sas
1578 .unwrap()
1579 .into_we_accepted_with_mac_method(vec![ShortAuthenticationString::Emoji], mac_method);
1580
1581 (alice_sas, bob_sas)
1582 }
1583
1584 #[test]
1585 fn start_content_accepting() {
1586 let mut start_content: SasV1Content = SasV1ContentInit {
1587 key_agreement_protocols: vec![
1588 KeyAgreementProtocol::Curve25519HkdfSha256,
1589 KeyAgreementProtocol::Curve25519,
1590 ],
1591 hashes: vec![HashAlgorithm::Sha256],
1592 message_authentication_codes: vec![
1593 #[allow(deprecated)]
1594 MessageAuthenticationCode::HkdfHmacSha256,
1595 MessageAuthenticationCode::from("org.matrix.msc3783.hkdf-hmac-sha256"),
1596 MessageAuthenticationCode::HkdfHmacSha256V2,
1597 ],
1598 short_authentication_string: vec![
1599 ShortAuthenticationString::Emoji,
1600 ShortAuthenticationString::Decimal,
1601 ],
1602 }
1603 .into();
1604
1605 let accepted_protocols = AcceptedProtocols::try_from(&start_content).unwrap();
1606
1607 assert_eq!(accepted_protocols.message_auth_code, SupportedMacMethod::HkdfHmacSha256V2);
1608 assert_eq!(
1609 accepted_protocols.key_agreement_protocol,
1610 KeyAgreementProtocol::Curve25519HkdfSha256
1611 );
1612
1613 start_content.message_authentication_codes = vec![
1614 #[allow(deprecated)]
1615 MessageAuthenticationCode::HkdfHmacSha256,
1616 MessageAuthenticationCode::from("org.matrix.msc3783.hkdf-hmac-sha256"),
1617 ];
1618 let accepted_protocols = AcceptedProtocols::try_from(&start_content).unwrap();
1619 assert_eq!(
1620 accepted_protocols.message_auth_code,
1621 SupportedMacMethod::Msc3783HkdfHmacSha256V2
1622 );
1623
1624 start_content.key_agreement_protocols = vec![KeyAgreementProtocol::Curve25519];
1625 AcceptedProtocols::try_from(&start_content)
1626 .expect_err("We don't support the old Curve25519 key agreement protocol");
1627 }
1628
1629 #[test]
1630 fn test_create_sas() {
1631 let (_, _) = get_sas_pair(None);
1632 }
1633
1634 #[test]
1635 fn test_sas_accept() {
1636 let (alice, bob) = get_sas_pair(None);
1637 let content = bob.as_content();
1638 let content = AcceptContent::from(&content);
1639
1640 alice.into_accepted(bob.user_id(), &content).unwrap();
1641 }
1642
1643 #[test]
1644 fn test_sas_key_share() {
1645 let (alice, bob) = get_sas_pair(None);
1646
1647 let content = bob.as_content();
1648 let content = AcceptContent::from(&content);
1649
1650 let alice: SasState<Accepted> = alice.into_accepted(bob.user_id(), &content).unwrap();
1651 let content = alice.as_content();
1652 let transaction_id = content.1.request_id;
1653 let content = KeyContent::try_from(&content.0).unwrap();
1654 let alice = alice.into_key_sent(&transaction_id).unwrap();
1655
1656 let bob = bob.into_key_received(alice.user_id(), &content).unwrap();
1657
1658 let content = bob.as_content();
1659 let transaction_id = content.1.request_id;
1660 let content = KeyContent::try_from(&content.0).unwrap();
1661
1662 let bob = bob.into_keys_exchanged(&transaction_id).unwrap();
1663
1664 let alice = alice.into_keys_exchanged(bob.user_id(), &content).unwrap();
1665
1666 assert_eq!(alice.get_decimal(), bob.get_decimal());
1667 assert_eq!(alice.get_emoji(), bob.get_emoji());
1668 }
1669
1670 fn full_flow_helper(mac_method: SupportedMacMethod) {
1671 let (alice, bob) = get_sas_pair(Some(mac_method));
1672
1673 let content = bob.as_content();
1674 let content = AcceptContent::from(&content);
1675
1676 assert_eq!(
1677 bob.state.accepted_protocols.message_auth_code, mac_method,
1678 "Bob should be using the specified MAC method."
1679 );
1680
1681 let alice: SasState<Accepted> = alice.into_accepted(bob.user_id(), &content).unwrap();
1682
1683 assert_eq!(
1684 alice.state.accepted_protocols.message_auth_code, mac_method,
1685 "Alice should use the our specified MAC method.",
1686 );
1687
1688 let content = alice.as_content();
1689 let request_id = content.1.request_id;
1690 let content = KeyContent::try_from(&content.0).unwrap();
1691
1692 let alice = alice.into_key_sent(&request_id).unwrap();
1693 let bob = bob.into_key_received(alice.user_id(), &content).unwrap();
1694
1695 let (content, request_info) = bob.as_content();
1696 let request_id = request_info.request_id;
1697 let content = KeyContent::try_from(&content).unwrap();
1698 let bob = bob.into_keys_exchanged(&request_id).unwrap();
1699
1700 let alice = alice.into_keys_exchanged(bob.user_id(), &content).unwrap();
1701
1702 assert_eq!(alice.get_decimal(), bob.get_decimal());
1703 assert_eq!(alice.get_emoji(), bob.get_emoji());
1704
1705 let bob_decimals = bob.get_decimal();
1706
1707 let bob = bob.confirm();
1708
1709 let content = bob.as_content();
1710 let content = MacContent::try_from(&content).unwrap();
1711
1712 let alice = alice.into_mac_received(bob.user_id(), &content).unwrap();
1713 assert!(!alice.get_emoji().is_empty());
1714 assert_eq!(alice.get_decimal(), bob_decimals);
1715 let alice = alice.confirm();
1716
1717 let content = alice.as_content();
1718 let content = MacContent::try_from(&content).unwrap();
1719 let bob = bob.into_done(alice.user_id(), &content).unwrap();
1720
1721 assert!(bob.verified_devices().contains(&bob.other_device()));
1722 assert!(alice.verified_devices().contains(&alice.other_device()));
1723 }
1724
1725 #[test]
1726 fn test_full_flow() {
1727 full_flow_helper(SupportedMacMethod::HkdfHmacSha256);
1728 }
1729
1730 #[test]
1731 fn test_full_flow_hkdf_hmac_sha_v2() {
1732 full_flow_helper(SupportedMacMethod::HkdfHmacSha256V2);
1733 }
1734
1735 #[test]
1736 fn test_full_flow_hkdf_msc3783() {
1737 full_flow_helper(SupportedMacMethod::Msc3783HkdfHmacSha256V2);
1738 }
1739
1740 #[test]
1741 fn test_sas_invalid_commitment() {
1742 let (alice, bob) = get_sas_pair(None);
1743
1744 let mut content = bob.as_content();
1745 let method = content.method_mut();
1746
1747 match method {
1748 AcceptMethod::SasV1(c) => {
1749 c.commitment = Base64::empty();
1750 }
1751 _ => panic!("Unknown accept event content"),
1752 }
1753
1754 let content = AcceptContent::from(&content);
1755
1756 let alice: SasState<Accepted> = alice.into_accepted(bob.user_id(), &content).unwrap();
1757
1758 let content = alice.as_content();
1759 let content = KeyContent::try_from(&content.0).unwrap();
1760 let bob = bob.into_key_received(alice.user_id(), &content).unwrap();
1761 let content = bob.as_content();
1762 let content = KeyContent::try_from(&content.0).unwrap();
1763
1764 let alice = alice
1765 .into_key_received(bob.user_id(), &content)
1766 .expect_err("Didn't cancel on invalid commitment");
1767
1768 assert_eq!(alice.state.cancel_code, CancelCode::MismatchedCommitment);
1769
1770 let content = alice.as_content();
1771 let content = CancelContent::try_from(&content).unwrap();
1772 assert_eq!(content.cancel_code(), &CancelCode::MismatchedCommitment);
1773 }
1774
1775 #[test]
1776 fn test_sas_invalid_commitment_after_key_sent() {
1777 let (alice, bob) = get_sas_pair(None);
1778
1779 let mut content = bob.as_content();
1780 assert_let!(AcceptMethod::SasV1(c) = content.method_mut());
1781 c.commitment = Base64::empty();
1782
1783 let content = AcceptContent::from(&content);
1784
1785 let alice: SasState<Accepted> = alice.into_accepted(bob.user_id(), &content).unwrap();
1786
1787 let content = alice.as_content();
1788 let request_id = content.1.request_id;
1789 let content = KeyContent::try_from(&content.0).unwrap();
1790
1791 let alice = alice.into_key_sent(&request_id).unwrap();
1792 let bob = bob.into_key_received(alice.user_id(), &content).unwrap();
1793
1794 let (content, _) = bob.as_content();
1795 let content = KeyContent::try_from(&content).unwrap();
1796
1797 let alice = alice
1798 .into_keys_exchanged(bob.user_id(), &content)
1799 .expect_err("Didn't cancel on invalid commitment");
1800
1801 assert_eq!(alice.state.cancel_code, CancelCode::MismatchedCommitment);
1802
1803 let content = alice.as_content();
1804 let content = CancelContent::try_from(&content).unwrap();
1805 assert_eq!(content.cancel_code(), &CancelCode::MismatchedCommitment);
1806 }
1807
1808 #[test]
1809 fn test_sas_invalid_sender() {
1810 let (alice, bob) = get_sas_pair(None);
1811
1812 let content = bob.as_content();
1813 let content = AcceptContent::from(&content);
1814 let sender = user_id!("@malory:example.org");
1815 alice.into_accepted(sender, &content).expect_err("Didn't cancel on a invalid sender");
1816 }
1817
1818 #[test]
1819 fn test_sas_unknown_sas_method() {
1820 let (alice, bob) = get_sas_pair(None);
1821
1822 let mut content = bob.as_content();
1823 let method = content.method_mut();
1824
1825 match method {
1826 AcceptMethod::SasV1(c) => {
1827 c.short_authentication_string = vec![];
1828 }
1829 _ => panic!("Unknown accept event content"),
1830 }
1831
1832 let content = AcceptContent::from(&content);
1833
1834 alice
1835 .into_accepted(bob.user_id(), &content)
1836 .expect_err("Didn't cancel on an invalid SAS method");
1837 }
1838
1839 #[test]
1840 fn test_sas_unknown_method() {
1841 let (alice, bob) = get_sas_pair(None);
1842
1843 let content = json!({
1844 "method": "m.sas.custom",
1845 "method_data": "something",
1846 "transaction_id": "some_id",
1847 });
1848
1849 let content: ToDeviceKeyVerificationAcceptEventContent =
1850 serde_json::from_value(content).unwrap();
1851 let content = AcceptContent::from(&content);
1852
1853 alice
1854 .into_accepted(bob.user_id(), &content)
1855 .expect_err("Didn't cancel on an unknown SAS method");
1856 }
1857
1858 #[async_test]
1859 async fn test_sas_from_start_unknown_method() {
1860 let alice = Account::with_device_id(alice_id(), alice_device_id());
1861 let alice_device = DeviceData::from_account(&alice);
1862
1863 let bob = Account::with_device_id(bob_id(), bob_device_id());
1864 let bob_device = DeviceData::from_account(&bob);
1865
1866 let flow_id = TransactionId::new().into();
1867 let alice_sas = SasState::<Created>::new(
1868 alice.static_data().clone(),
1869 bob_device,
1870 None,
1871 None,
1872 flow_id,
1873 false,
1874 None,
1875 );
1876
1877 let mut start_content = alice_sas.as_content();
1878 let method = start_content.method_mut();
1879
1880 match method {
1881 StartMethod::SasV1(c) => {
1882 c.message_authentication_codes = vec![];
1883 }
1884 _ => panic!("Unknown SAS start method"),
1885 }
1886
1887 let flow_id = start_content.flow_id();
1888 let content = StartContent::from(&start_content);
1889
1890 SasState::<Started>::from_start_event(
1891 bob.static_data().clone(),
1892 alice_device.clone(),
1893 None,
1894 None,
1895 flow_id,
1896 &content,
1897 false,
1898 )
1899 .expect_err("Didn't cancel on invalid MAC method");
1900
1901 let content = json!({
1902 "method": "m.sas.custom",
1903 "from_device": "DEVICEID",
1904 "method_data": "something",
1905 "transaction_id": "some_id",
1906 });
1907
1908 let content: ToDeviceKeyVerificationStartEventContent =
1909 serde_json::from_value(content).unwrap();
1910 let content = StartContent::from(&content);
1911 let flow_id = content.flow_id().to_owned();
1912
1913 SasState::<Started>::from_start_event(
1914 bob.static_data().clone(),
1915 alice_device,
1916 None,
1917 None,
1918 FlowId::ToDevice(flow_id.into()),
1919 &content,
1920 false,
1921 )
1922 .expect_err("Didn't cancel on unknown sas method");
1923 }
1924}