Skip to main content

matrix_sdk_crypto/verification/sas/
sas_state.rs

1// Copyright 2020 The Matrix.org Foundation C.I.C.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use 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            // TODO: Remove this soon.
87            MessageAuthenticationCode::from("org.matrix.msc3783.hkdf-hmac-sha256"),
88        ],
89        hashes: HASHES.to_vec(),
90    }
91    .into()
92}
93
94// The max time a SAS flow can take from start to done.
95const MAX_AGE: Duration = Duration::from_secs(60 * 5);
96
97// The max time a SAS object will wait for a new event to arrive.
98const MAX_EVENT_TIMEOUT: Duration = Duration::from_secs(60);
99
100/// The list of Message authentication code methods we currently support.
101///
102/// This is a subset of the MAC methods in the `MessageAuthenticationCode` enum.
103#[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    //// Verify that the given MAC matches for the given input and info.
144    ///
145    /// As defined in the [spec]
146    ///
147    /// spec: https://spec.matrix.org/v1.4/client-server-api/#hkdf-calculation//
148    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    /// Calculate the MAC of the input with the given info string.
171    ///
172    /// As defined in the [spec]
173    ///
174    /// spec: https://spec.matrix.org/v1.4/client-server-api/#hkdf-calculation
175    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/// Struct containing the protocols that were agreed to be used for the SAS
190/// flow.
191#[derive(Clone, Debug, PartialEq, Eq)]
192pub struct AcceptedProtocols {
193    /// The key agreement protocol the device is choosing to use.
194    pub key_agreement_protocol: KeyAgreementProtocol,
195    /// The hash method the device is choosing to use.
196    pub hash: HashAlgorithm,
197    /// The message authentication code the device is choosing to use
198    pub message_auth_code: SupportedMacMethod,
199    /// The SAS methods both devices involved in the verification process
200    /// understand.
201    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/// A type level state machine modeling the Sas flow.
306///
307/// This is the generic struct holding common data between the different states
308/// and the specific state.
309#[derive(Clone)]
310pub struct SasState<S: Clone> {
311    /// The SAS struct.
312    inner: Arc<Mutex<Option<Sas>>>,
313
314    /// The public key we generated for this SAS flow.
315    our_public_key: Curve25519PublicKey,
316
317    /// Struct holding the identities that are doing the SAS dance.
318    // `Box` it to reduce the struct size.
319    ids: Box<SasIds>,
320
321    /// The instant when the SAS object was created. If this more than MAX_AGE
322    /// seconds are elapsed, the event will be canceled with a
323    /// `CancelCode::Timeout`
324    creation_time: Arc<Instant>,
325
326    /// The instant the SAS object last received an event.
327    last_event_time: Arc<Instant>,
328
329    /// The unique identifier of this SAS flow.
330    ///
331    /// This will be the transaction id for to-device events and the relates_to
332    /// field for in-room events.
333    pub verification_flow_id: Arc<FlowId>,
334
335    /// The SAS state we're in.
336    pub state: Arc<S>,
337
338    /// Did the SAS verification start from a `m.verification.request`.
339    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/// The initial SAS state.
373#[derive(Clone, Debug)]
374pub struct Created {
375    pub protocol_definitions: SasV1Content,
376}
377
378/// The initial SAS state if the other side started the SAS verification.
379#[derive(Clone, Debug)]
380pub struct Started {
381    commitment: Base64,
382    pub protocol_definitions: SasV1Content,
383    pub accepted_protocols: AcceptedProtocols,
384}
385
386/// The SAS state we're going to be in after the other side accepted our
387/// verification start event.
388#[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/// The SAS state we're going to be in after we accepted our verification start
397/// event.
398#[derive(Clone, Debug)]
399pub struct WeAccepted {
400    we_started: bool,
401    pub accepted_protocols: AcceptedProtocols,
402    commitment: Base64,
403}
404
405/// The SAS state we're going to be in after we received the public key of the
406/// other participant.
407///
408/// From now on we can show the short auth string to the user.
409#[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/// The SAS state we're going to be in after the user has confirmed that the
433/// short auth string matches. We still need to receive a MAC event from the
434/// other side.
435#[derive(Clone, Debug)]
436pub struct Confirmed {
437    sas: Arc<Mutex<EstablishedSas>>,
438    pub accepted_protocols: AcceptedProtocols,
439}
440
441/// The SAS state we're going to be in after we receive a MAC event from the
442/// other side. Our own user still needs to confirm that the short auth string
443/// matches.
444#[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/// The SAS state we're going to be in after we receive a MAC event in a DM. DMs
454/// require a final message `m.key.verification.done` message to conclude the
455/// verification. This state waits for such a message.
456#[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/// The verification state indicating that the verification finished
465/// successfully.
466///
467/// We can now mark the device in our verified devices list as verified and sign
468/// the master keys in the verified devices list.
469#[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    /// Get our own user id.
479    #[cfg(test)]
480    pub fn user_id(&self) -> &UserId {
481        &self.ids.account.user_id
482    }
483
484    /// Get our own device ID.
485    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    /// Did our SAS verification time out.
508    pub fn timed_out(&self) -> bool {
509        self.creation_time.elapsed() > MAX_AGE || self.last_event_time.elapsed() > MAX_EVENT_TIMEOUT
510    }
511
512    /// Is this verification happening inside a DM.
513    #[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    /// Create a new SAS verification flow.
539    ///
540    /// # Arguments
541    ///
542    /// * `account` - Our own account.
543    ///
544    /// * `other_device` - The other device which we are going to verify.
545    ///
546    /// * `other_identity` - The identity of the other user if one exists.
547    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    /// Receive a m.key.verification.accept event, changing the state into an
616    /// Accepted one.
617    ///
618    /// # Arguments
619    ///
620    /// - `event` - The m.key.verification.accept event that was sent to us by
621    ///   the other side.
622    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    /// Create a new SAS verification flow from an in-room
658    /// m.key.verification.start event.
659    ///
660    /// This will put us in the `started` state.
661    ///
662    /// # Arguments
663    ///
664    /// - `account` - Our own account.
665    /// - `other_device` - The other device which we are going to verify.
666    /// - `event` - The m.key.verification.start event that was sent to us by
667    ///   the other side.
668    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        // Decimal is required per spec.
764        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    /// Receive a m.key.verification.accept event, changing the state into an
810    /// Accepted one.
811    ///
812    /// Note: Even though the other side has started the (or rather "a") sas
813    /// verification, it can still accept one, if we have sent one
814    /// simultaneously. In this case we just go on with the verification that
815    /// _we_ started.
816    ///
817    /// # Arguments
818    ///
819    /// - `event` - The m.key.verification.accept event that was sent to us by
820    ///   the other side.
821    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    /// Get the content for the accept event.
857    ///
858    /// The content needs to be sent to the other device.
859    ///
860    /// This should be sent out automatically if the SAS verification flow has
861    /// been started because of a m.key.verification.request ->
862    /// m.key.verification.ready flow.
863    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    /// Receive a m.key.verification.key event, changing the state into a
896    /// `KeyReceived` one
897    ///
898    /// # Arguments
899    ///
900    /// - `event` - The m.key.verification.key event that was sent to us by the
901    ///   other side. The event will be modified so it doesn't contain any key
902    ///   anymore.
903    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    /// Receive a m.key.verification.key event, changing the state into a
931    /// `KeyReceived` one
932    ///
933    /// # Arguments
934    ///
935    /// - `event` - The m.key.verification.key event that was sent to us by the
936    ///   other side. The event will be modified so it doesn't contain any key
937    ///   anymore.
938    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    /// Get the content for the key event.
991    ///
992    /// The content needs to be automatically sent to the other side.
993    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    /// Get the content for the key event.
1060    ///
1061    /// The content needs to be automatically sent to the other side if and only
1062    /// if we_started is false.
1063    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    /// Get the emoji version of the short authentication string.
1117    ///
1118    /// Returns a seven tuples where the first element is the emoji and the
1119    /// second element the English description of the emoji.
1120    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    /// Get the index of the emoji of the short authentication string.
1130    ///
1131    /// Returns seven u8 numbers in the range from 0 to 63 inclusive, those
1132    /// numbers can be converted to a unique emoji defined by the spec.
1133    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    /// Get the decimal version of the short authentication string.
1143    ///
1144    /// Returns a tuple containing three 4 digit integer numbers that represent
1145    /// the short auth string.
1146    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    /// Receive a m.key.verification.mac event, changing the state into a
1156    /// `MacReceived` one
1157    ///
1158    /// # Arguments
1159    ///
1160    /// - `event` - The m.key.verification.mac event that was sent to us by the
1161    ///   other side.
1162    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    /// Confirm that the short auth string matches.
1198    ///
1199    /// This needs to be done by the user, this will put us in the `Confirmed`
1200    /// state.
1201    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    /// Receive a m.key.verification.mac event, changing the state into a `Done`
1220    /// one
1221    ///
1222    /// # Arguments
1223    ///
1224    /// - `event` - The m.key.verification.mac event that was sent to us by the
1225    ///   other side.
1226    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    /// Receive a m.key.verification.mac event, changing the state into a
1262    /// `WaitingForDone` one. This method should be used instead of
1263    /// `into_done()` if the verification started with a
1264    /// `m.key.verification.request`.
1265    ///
1266    /// # Arguments
1267    ///
1268    /// - `event` - The m.key.verification.mac event that was sent to us by the
1269    ///   other side.
1270    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    /// Get the content for the mac event.
1306    ///
1307    /// The content needs to be automatically sent to the other side.
1308    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    /// Confirm that the short auth string matches.
1320    ///
1321    /// This needs to be done by the user, this will put us in the `Done` state
1322    /// since the other side already confirmed and sent us a MAC event.
1323    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    /// Confirm that the short auth string matches but wait for the other side
1342    /// to confirm that it's done.
1343    ///
1344    /// This needs to be done by the user, this will put us in the `WaitForDone`
1345    /// state where we wait for the other side to confirm that the MAC event was
1346    /// successfully received.
1347    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    /// Get the emoji version of the short authentication string.
1366    ///
1367    /// Returns a vector of tuples where the first element is the emoji and the
1368    /// second element the English description of the emoji.
1369    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    /// Get the index of the emoji of the short authentication string.
1379    ///
1380    /// Returns seven u8 numbers in the range from 0 to 63 inclusive, those
1381    /// numbers can be converted to a unique emoji defined by the spec.
1382    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    /// Get the decimal version of the short authentication string.
1392    ///
1393    /// Returns a tuple containing three 4 digit integer numbers that represent
1394    /// the short auth string.
1395    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    /// Get the content for the mac event.
1407    ///
1408    /// The content needs to be automatically sent to the other side if it
1409    /// wasn't already sent.
1410    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    /// Receive a m.key.verification.mac event, changing the state into a `Done`
1436    /// one
1437    ///
1438    /// # Arguments
1439    ///
1440    /// - `event` - The m.key.verification.mac event that was sent to us by the
1441    ///   other side.
1442    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    /// Get the content for the mac event.
1470    ///
1471    /// The content needs to be automatically sent to the other side if it
1472    /// wasn't already sent.
1473    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    /// Get the list of verified devices.
1483    pub fn verified_devices(&self) -> Arc<[DeviceData]> {
1484        self.state.verified_devices.clone()
1485    }
1486
1487    /// Get the list of verified identities.
1488    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}