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::KeyMismatch))
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::KeyMismatch))
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 start::{
1509 SasV1Content, SasV1ContentInit, StartMethod,
1510 ToDeviceKeyVerificationStartEventContent,
1511 },
1512 },
1513 serde::Base64,
1514 user_id,
1515 };
1516 use serde_json::json;
1517
1518 use super::{Accepted, Created, SasState, Started, SupportedMacMethod, WeAccepted};
1519 use crate::{
1520 AcceptedProtocols, Account, DeviceData,
1521 verification::{
1522 FlowId,
1523 event_enums::{AcceptContent, KeyContent, MacContent, StartContent},
1524 },
1525 };
1526
1527 fn alice_id() -> &'static UserId {
1528 user_id!("@alice:example.org")
1529 }
1530
1531 fn alice_device_id() -> &'static DeviceId {
1532 device_id!("JLAFKJWSCS")
1533 }
1534
1535 fn bob_id() -> &'static UserId {
1536 user_id!("@bob:example.org")
1537 }
1538
1539 fn bob_device_id() -> &'static DeviceId {
1540 device_id!("BOBDEVICE")
1541 }
1542
1543 fn get_sas_pair(
1544 mac_method: Option<SupportedMacMethod>,
1545 ) -> (SasState<Created>, SasState<WeAccepted>) {
1546 let alice = Account::with_device_id(alice_id(), alice_device_id());
1547 let alice_device = DeviceData::from_account(&alice);
1548
1549 let bob = Account::with_device_id(bob_id(), bob_device_id());
1550 let bob_device = DeviceData::from_account(&bob);
1551
1552 let flow_id = TransactionId::new().into();
1553 let alice_sas = SasState::<Created>::new(
1554 alice.static_data().clone(),
1555 bob_device,
1556 None,
1557 None,
1558 flow_id,
1559 false,
1560 None,
1561 );
1562
1563 let start_content = alice_sas.as_content();
1564 let flow_id = start_content.flow_id();
1565
1566 let bob_sas = SasState::<Started>::from_start_event(
1567 bob.static_data().clone(),
1568 alice_device,
1569 None,
1570 None,
1571 flow_id,
1572 &start_content.as_start_content(),
1573 false,
1574 );
1575 let bob_sas = bob_sas
1576 .unwrap()
1577 .into_we_accepted_with_mac_method(vec![ShortAuthenticationString::Emoji], mac_method);
1578
1579 (alice_sas, bob_sas)
1580 }
1581
1582 #[test]
1583 fn start_content_accepting() {
1584 let mut start_content: SasV1Content = SasV1ContentInit {
1585 key_agreement_protocols: vec![
1586 KeyAgreementProtocol::Curve25519HkdfSha256,
1587 KeyAgreementProtocol::Curve25519,
1588 ],
1589 hashes: vec![HashAlgorithm::Sha256],
1590 message_authentication_codes: vec![
1591 #[allow(deprecated)]
1592 MessageAuthenticationCode::HkdfHmacSha256,
1593 MessageAuthenticationCode::from("org.matrix.msc3783.hkdf-hmac-sha256"),
1594 MessageAuthenticationCode::HkdfHmacSha256V2,
1595 ],
1596 short_authentication_string: vec![
1597 ShortAuthenticationString::Emoji,
1598 ShortAuthenticationString::Decimal,
1599 ],
1600 }
1601 .into();
1602
1603 let accepted_protocols = AcceptedProtocols::try_from(&start_content).unwrap();
1604
1605 assert_eq!(accepted_protocols.message_auth_code, SupportedMacMethod::HkdfHmacSha256V2);
1606 assert_eq!(
1607 accepted_protocols.key_agreement_protocol,
1608 KeyAgreementProtocol::Curve25519HkdfSha256
1609 );
1610
1611 start_content.message_authentication_codes = vec![
1612 #[allow(deprecated)]
1613 MessageAuthenticationCode::HkdfHmacSha256,
1614 MessageAuthenticationCode::from("org.matrix.msc3783.hkdf-hmac-sha256"),
1615 ];
1616 let accepted_protocols = AcceptedProtocols::try_from(&start_content).unwrap();
1617 assert_eq!(
1618 accepted_protocols.message_auth_code,
1619 SupportedMacMethod::Msc3783HkdfHmacSha256V2
1620 );
1621
1622 start_content.key_agreement_protocols = vec![KeyAgreementProtocol::Curve25519];
1623 AcceptedProtocols::try_from(&start_content)
1624 .expect_err("We don't support the old Curve25519 key agreement protocol");
1625 }
1626
1627 #[test]
1628 fn test_create_sas() {
1629 let (_, _) = get_sas_pair(None);
1630 }
1631
1632 #[test]
1633 fn test_sas_accept() {
1634 let (alice, bob) = get_sas_pair(None);
1635 let content = bob.as_content();
1636 let content = AcceptContent::from(&content);
1637
1638 alice.into_accepted(bob.user_id(), &content).unwrap();
1639 }
1640
1641 #[test]
1642 fn test_sas_key_share() {
1643 let (alice, bob) = get_sas_pair(None);
1644
1645 let content = bob.as_content();
1646 let content = AcceptContent::from(&content);
1647
1648 let alice: SasState<Accepted> = alice.into_accepted(bob.user_id(), &content).unwrap();
1649 let content = alice.as_content();
1650 let transaction_id = content.1.request_id;
1651 let content = KeyContent::try_from(&content.0).unwrap();
1652 let alice = alice.into_key_sent(&transaction_id).unwrap();
1653
1654 let bob = bob.into_key_received(alice.user_id(), &content).unwrap();
1655
1656 let content = bob.as_content();
1657 let transaction_id = content.1.request_id;
1658 let content = KeyContent::try_from(&content.0).unwrap();
1659
1660 let bob = bob.into_keys_exchanged(&transaction_id).unwrap();
1661
1662 let alice = alice.into_keys_exchanged(bob.user_id(), &content).unwrap();
1663
1664 assert_eq!(alice.get_decimal(), bob.get_decimal());
1665 assert_eq!(alice.get_emoji(), bob.get_emoji());
1666 }
1667
1668 fn full_flow_helper(mac_method: SupportedMacMethod) {
1669 let (alice, bob) = get_sas_pair(Some(mac_method));
1670
1671 let content = bob.as_content();
1672 let content = AcceptContent::from(&content);
1673
1674 assert_eq!(
1675 bob.state.accepted_protocols.message_auth_code, mac_method,
1676 "Bob should be using the specified MAC method."
1677 );
1678
1679 let alice: SasState<Accepted> = alice.into_accepted(bob.user_id(), &content).unwrap();
1680
1681 assert_eq!(
1682 alice.state.accepted_protocols.message_auth_code, mac_method,
1683 "Alice should use the our specified MAC method.",
1684 );
1685
1686 let content = alice.as_content();
1687 let request_id = content.1.request_id;
1688 let content = KeyContent::try_from(&content.0).unwrap();
1689
1690 let alice = alice.into_key_sent(&request_id).unwrap();
1691 let bob = bob.into_key_received(alice.user_id(), &content).unwrap();
1692
1693 let (content, request_info) = bob.as_content();
1694 let request_id = request_info.request_id;
1695 let content = KeyContent::try_from(&content).unwrap();
1696 let bob = bob.into_keys_exchanged(&request_id).unwrap();
1697
1698 let alice = alice.into_keys_exchanged(bob.user_id(), &content).unwrap();
1699
1700 assert_eq!(alice.get_decimal(), bob.get_decimal());
1701 assert_eq!(alice.get_emoji(), bob.get_emoji());
1702
1703 let bob_decimals = bob.get_decimal();
1704
1705 let bob = bob.confirm();
1706
1707 let content = bob.as_content();
1708 let content = MacContent::try_from(&content).unwrap();
1709
1710 let alice = alice.into_mac_received(bob.user_id(), &content).unwrap();
1711 assert!(!alice.get_emoji().is_empty());
1712 assert_eq!(alice.get_decimal(), bob_decimals);
1713 let alice = alice.confirm();
1714
1715 let content = alice.as_content();
1716 let content = MacContent::try_from(&content).unwrap();
1717 let bob = bob.into_done(alice.user_id(), &content).unwrap();
1718
1719 assert!(bob.verified_devices().contains(&bob.other_device()));
1720 assert!(alice.verified_devices().contains(&alice.other_device()));
1721 }
1722
1723 #[test]
1724 fn test_full_flow() {
1725 full_flow_helper(SupportedMacMethod::HkdfHmacSha256);
1726 }
1727
1728 #[test]
1729 fn test_full_flow_hkdf_hmac_sha_v2() {
1730 full_flow_helper(SupportedMacMethod::HkdfHmacSha256V2);
1731 }
1732
1733 #[test]
1734 fn test_full_flow_hkdf_msc3783() {
1735 full_flow_helper(SupportedMacMethod::Msc3783HkdfHmacSha256V2);
1736 }
1737
1738 #[test]
1739 fn test_sas_invalid_commitment() {
1740 let (alice, bob) = get_sas_pair(None);
1741
1742 let mut content = bob.as_content();
1743 let method = content.method_mut();
1744
1745 match method {
1746 AcceptMethod::SasV1(c) => {
1747 c.commitment = Base64::empty();
1748 }
1749 _ => panic!("Unknown accept event content"),
1750 }
1751
1752 let content = AcceptContent::from(&content);
1753
1754 let alice: SasState<Accepted> = alice.into_accepted(bob.user_id(), &content).unwrap();
1755
1756 let content = alice.as_content();
1757 let content = KeyContent::try_from(&content.0).unwrap();
1758 let bob = bob.into_key_received(alice.user_id(), &content).unwrap();
1759 let content = bob.as_content();
1760 let content = KeyContent::try_from(&content.0).unwrap();
1761
1762 alice
1763 .into_key_received(bob.user_id(), &content)
1764 .expect_err("Didn't cancel on invalid commitment");
1765 }
1766
1767 #[test]
1768 fn test_sas_invalid_sender() {
1769 let (alice, bob) = get_sas_pair(None);
1770
1771 let content = bob.as_content();
1772 let content = AcceptContent::from(&content);
1773 let sender = user_id!("@malory:example.org");
1774 alice.into_accepted(sender, &content).expect_err("Didn't cancel on a invalid sender");
1775 }
1776
1777 #[test]
1778 fn test_sas_unknown_sas_method() {
1779 let (alice, bob) = get_sas_pair(None);
1780
1781 let mut content = bob.as_content();
1782 let method = content.method_mut();
1783
1784 match method {
1785 AcceptMethod::SasV1(c) => {
1786 c.short_authentication_string = vec![];
1787 }
1788 _ => panic!("Unknown accept event content"),
1789 }
1790
1791 let content = AcceptContent::from(&content);
1792
1793 alice
1794 .into_accepted(bob.user_id(), &content)
1795 .expect_err("Didn't cancel on an invalid SAS method");
1796 }
1797
1798 #[test]
1799 fn test_sas_unknown_method() {
1800 let (alice, bob) = get_sas_pair(None);
1801
1802 let content = json!({
1803 "method": "m.sas.custom",
1804 "method_data": "something",
1805 "transaction_id": "some_id",
1806 });
1807
1808 let content: ToDeviceKeyVerificationAcceptEventContent =
1809 serde_json::from_value(content).unwrap();
1810 let content = AcceptContent::from(&content);
1811
1812 alice
1813 .into_accepted(bob.user_id(), &content)
1814 .expect_err("Didn't cancel on an unknown SAS method");
1815 }
1816
1817 #[async_test]
1818 async fn test_sas_from_start_unknown_method() {
1819 let alice = Account::with_device_id(alice_id(), alice_device_id());
1820 let alice_device = DeviceData::from_account(&alice);
1821
1822 let bob = Account::with_device_id(bob_id(), bob_device_id());
1823 let bob_device = DeviceData::from_account(&bob);
1824
1825 let flow_id = TransactionId::new().into();
1826 let alice_sas = SasState::<Created>::new(
1827 alice.static_data().clone(),
1828 bob_device,
1829 None,
1830 None,
1831 flow_id,
1832 false,
1833 None,
1834 );
1835
1836 let mut start_content = alice_sas.as_content();
1837 let method = start_content.method_mut();
1838
1839 match method {
1840 StartMethod::SasV1(c) => {
1841 c.message_authentication_codes = vec![];
1842 }
1843 _ => panic!("Unknown SAS start method"),
1844 }
1845
1846 let flow_id = start_content.flow_id();
1847 let content = StartContent::from(&start_content);
1848
1849 SasState::<Started>::from_start_event(
1850 bob.static_data().clone(),
1851 alice_device.clone(),
1852 None,
1853 None,
1854 flow_id,
1855 &content,
1856 false,
1857 )
1858 .expect_err("Didn't cancel on invalid MAC method");
1859
1860 let content = json!({
1861 "method": "m.sas.custom",
1862 "from_device": "DEVICEID",
1863 "method_data": "something",
1864 "transaction_id": "some_id",
1865 });
1866
1867 let content: ToDeviceKeyVerificationStartEventContent =
1868 serde_json::from_value(content).unwrap();
1869 let content = StartContent::from(&content);
1870 let flow_id = content.flow_id().to_owned();
1871
1872 SasState::<Started>::from_start_event(
1873 bob.static_data().clone(),
1874 alice_device,
1875 None,
1876 None,
1877 FlowId::ToDevice(flow_id.into()),
1878 &content,
1879 false,
1880 )
1881 .expect_err("Didn't cancel on unknown sas method");
1882 }
1883}