matrix_sdk_crypto/verification/
mod.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
15mod cache;
16mod event_enums;
17mod machine;
18#[cfg(feature = "qrcode")]
19mod qrcode;
20mod requests;
21mod sas;
22
23use std::{collections::HashMap, ops::Deref, sync::Arc};
24
25use as_variant::as_variant;
26use event_enums::OutgoingContent;
27pub use machine::VerificationMachine;
28#[cfg(feature = "qrcode")]
29pub use qrcode::{QrVerification, QrVerificationState, ScanError};
30pub use requests::{VerificationRequest, VerificationRequestState};
31#[cfg(feature = "qrcode")]
32use ruma::events::key::verification::done::{
33    KeyVerificationDoneEventContent, ToDeviceKeyVerificationDoneEventContent,
34};
35use ruma::{
36    api::client::keys::upload_signatures::v3::Request as SignatureUploadRequest,
37    events::{
38        key::verification::cancel::{
39            CancelCode, KeyVerificationCancelEventContent,
40            ToDeviceKeyVerificationCancelEventContent,
41        },
42        relation::Reference,
43        AnyMessageLikeEventContent, AnyToDeviceEventContent,
44    },
45    DeviceId, EventId, OwnedDeviceId, OwnedEventId, OwnedRoomId, OwnedTransactionId, RoomId,
46    UserId,
47};
48pub use sas::{AcceptSettings, AcceptedProtocols, EmojiShortAuthString, Sas, SasState};
49use tokio::sync::Mutex;
50use tracing::{debug, error, info, warn};
51
52use crate::{
53    error::SignatureError,
54    gossiping::{GossipMachine, GossipRequest},
55    olm::{PrivateCrossSigningIdentity, StaticAccountData},
56    store::{Changes, CryptoStoreWrapper},
57    types::{requests::OutgoingVerificationRequest, Signatures},
58    CryptoStoreError, DeviceData, LocalTrust, OwnUserIdentityData, UserIdentityData,
59};
60
61#[derive(Clone, Debug)]
62pub(crate) struct VerificationStore {
63    pub account: StaticAccountData,
64    pub private_identity: Arc<Mutex<PrivateCrossSigningIdentity>>,
65    inner: Arc<CryptoStoreWrapper>,
66}
67
68/// An emoji that is used for interactive verification using a short auth
69/// string.
70///
71/// This will contain a single emoji and description from the list of emojis
72/// from the [spec].
73///
74/// [spec]: https://spec.matrix.org/unstable/client-server-api/#sas-method-emoji
75#[derive(Debug, Clone, PartialEq, Eq, PartialOrd)]
76pub struct Emoji {
77    /// The emoji symbol that represents a part of the short auth string, for
78    /// example: 🐶
79    pub symbol: &'static str,
80    /// The description of the emoji, for example 'Dog'.
81    pub description: &'static str,
82}
83
84/// Format the list of emojis as a two line string.
85///
86/// The first line will contain the emojis spread out so the second line can
87/// contain the descriptions centered bellow the emoji.
88pub fn format_emojis(emojis: [Emoji; 7]) -> String {
89    let (emojis, descriptions): (Vec<_>, Vec<_>) =
90        emojis.iter().map(|e| (e.symbol, e.description)).unzip();
91
92    let center_emoji = |emoji: &str| -> String {
93        const EMOJI_WIDTH: usize = 2;
94        // These are emojis that need VARIATION-SELECTOR-16 (U+FE0F) so that they are
95        // rendered with coloured glyphs. For these, we need to add an extra
96        // space after them so that they are rendered properly in terminals.
97        const VARIATION_SELECTOR_EMOJIS: [&str; 7] = ["☁️", "❤️", "☂️", "✏️", "✂️", "☎️", "✈️"];
98
99        // Hack to make terminals behave properly when one of the above is printed.
100        let emoji = if VARIATION_SELECTOR_EMOJIS.contains(&emoji) {
101            format!("{emoji} ")
102        } else {
103            emoji.to_owned()
104        };
105
106        // This is a trick to account for the fact that emojis are wider than other
107        // monospace characters.
108        let placeholder = ".".repeat(EMOJI_WIDTH);
109
110        format!("{placeholder:^12}").replace(&placeholder, &emoji)
111    };
112
113    let emoji_string = emojis.iter().map(|e| center_emoji(e)).collect::<Vec<_>>().join("");
114
115    let description = descriptions.iter().map(|d| format!("{d:^12}")).collect::<Vec<_>>().join("");
116
117    format!("{emoji_string}\n{description}")
118}
119
120impl VerificationStore {
121    pub async fn get_device(
122        &self,
123        user_id: &UserId,
124        device_id: &DeviceId,
125    ) -> Result<Option<DeviceData>, CryptoStoreError> {
126        Ok(self.inner.get_device(user_id, device_id).await?.filter(|d| {
127            !(d.user_id() == self.account.user_id && d.device_id() == self.account.device_id)
128        }))
129    }
130
131    pub async fn get_user_identity(
132        &self,
133        user_id: &UserId,
134    ) -> Result<Option<UserIdentityData>, CryptoStoreError> {
135        self.inner.get_user_identity(user_id).await
136    }
137
138    pub async fn get_identities(
139        &self,
140        device_being_verified: DeviceData,
141    ) -> Result<IdentitiesBeingVerified, CryptoStoreError> {
142        let identity_being_verified =
143            self.get_user_identity(device_being_verified.user_id()).await?;
144
145        Ok(IdentitiesBeingVerified {
146            private_identity: self.private_identity.lock().await.clone(),
147            store: self.clone(),
148            device_being_verified,
149            own_identity: self
150                .get_user_identity(&self.account.user_id)
151                .await?
152                .and_then(|i| i.into_own()),
153            identity_being_verified,
154        })
155    }
156
157    pub async fn save_changes(&self, changes: Changes) -> Result<(), CryptoStoreError> {
158        self.inner.save_changes(changes).await
159    }
160
161    pub async fn get_user_devices(
162        &self,
163        user_id: &UserId,
164    ) -> Result<HashMap<OwnedDeviceId, DeviceData>, CryptoStoreError> {
165        self.inner.get_user_devices(user_id).await
166    }
167
168    /// Get the signatures that have signed our own device.
169    pub async fn device_signatures(&self) -> Result<Option<Signatures>, CryptoStoreError> {
170        Ok(self
171            .inner
172            .get_device(&self.account.user_id, &self.account.device_id)
173            .await?
174            .map(|d| d.signatures().to_owned()))
175    }
176
177    pub fn inner(&self) -> &CryptoStoreWrapper {
178        self.inner.deref()
179    }
180}
181
182/// An enum over the different verification types the SDK supports.
183#[derive(Clone, Debug)]
184#[non_exhaustive]
185pub enum Verification {
186    /// The `m.sas.v1` verification variant.
187    // `Box` the `Sas` to reduce the enum size.
188    SasV1(Box<Sas>),
189    /// The `m.qr_code.*.v1` verification variant.
190    // `Box` the `QrVerification` to reduce the enum size.
191    #[cfg(feature = "qrcode")]
192    QrV1(Box<QrVerification>),
193}
194
195impl Verification {
196    /// Try to deconstruct this verification enum into a SAS verification.
197    pub fn sas_v1(self) -> Option<Box<Sas>> {
198        as_variant!(self, Verification::SasV1)
199    }
200
201    /// Try to deconstruct this verification enum into a QR code verification.
202    #[cfg(feature = "qrcode")]
203    pub fn qr_v1(self) -> Option<Box<QrVerification>> {
204        as_variant!(self, Verification::QrV1)
205    }
206
207    /// Has this verification finished.
208    pub fn is_done(&self) -> bool {
209        match self {
210            Verification::SasV1(s) => s.is_done(),
211            #[cfg(feature = "qrcode")]
212            Verification::QrV1(qr) => qr.is_done(),
213        }
214    }
215
216    /// Get the ID that uniquely identifies this verification flow.
217    pub fn flow_id(&self) -> &str {
218        match self {
219            Verification::SasV1(s) => s.flow_id().as_str(),
220            #[cfg(feature = "qrcode")]
221            Verification::QrV1(qr) => qr.flow_id().as_str(),
222        }
223    }
224
225    /// Has the verification been cancelled.
226    pub fn is_cancelled(&self) -> bool {
227        match self {
228            Verification::SasV1(s) => s.is_cancelled(),
229            #[cfg(feature = "qrcode")]
230            Verification::QrV1(qr) => qr.is_cancelled(),
231        }
232    }
233
234    /// Get our own user id that is participating in this verification.
235    pub fn user_id(&self) -> &UserId {
236        match self {
237            Verification::SasV1(v) => v.user_id(),
238            #[cfg(feature = "qrcode")]
239            Verification::QrV1(v) => v.user_id(),
240        }
241    }
242
243    /// Get the other user id that is participating in this verification.
244    pub fn other_user(&self) -> &UserId {
245        match self {
246            Verification::SasV1(s) => s.other_user_id(),
247            #[cfg(feature = "qrcode")]
248            Verification::QrV1(qr) => qr.other_user_id(),
249        }
250    }
251
252    /// Is this a verification verifying a device that belongs to us.
253    pub fn is_self_verification(&self) -> bool {
254        match self {
255            Verification::SasV1(v) => v.is_self_verification(),
256            #[cfg(feature = "qrcode")]
257            Verification::QrV1(v) => v.is_self_verification(),
258        }
259    }
260
261    fn cancel(&self) -> Option<OutgoingVerificationRequest> {
262        match self {
263            Verification::SasV1(v) => v.cancel(),
264            #[cfg(feature = "qrcode")]
265            Verification::QrV1(v) => v.cancel(),
266        }
267    }
268}
269
270impl From<Sas> for Verification {
271    fn from(sas: Sas) -> Self {
272        Self::SasV1(Box::new(sas))
273    }
274}
275
276#[cfg(feature = "qrcode")]
277impl From<QrVerification> for Verification {
278    fn from(qr: QrVerification) -> Self {
279        Self::QrV1(Box::new(qr))
280    }
281}
282
283/// The verification state indicating that the verification finished
284/// successfully.
285///
286/// We can now mark the device in our verified devices list as verified and sign
287/// the master keys in the verified devices list.
288#[cfg(feature = "qrcode")]
289#[derive(Clone, Debug)]
290pub struct Done {
291    verified_devices: Arc<[DeviceData]>,
292    verified_master_keys: Arc<[UserIdentityData]>,
293}
294
295#[cfg(feature = "qrcode")]
296impl Done {
297    pub fn as_content(&self, flow_id: &FlowId) -> OutgoingContent {
298        match flow_id {
299            FlowId::ToDevice(t) => AnyToDeviceEventContent::KeyVerificationDone(
300                ToDeviceKeyVerificationDoneEventContent::new(t.to_owned()),
301            )
302            .into(),
303            FlowId::InRoom(r, e) => (
304                r.to_owned(),
305                AnyMessageLikeEventContent::KeyVerificationDone(
306                    KeyVerificationDoneEventContent::new(Reference::new(e.to_owned())),
307                ),
308            )
309                .into(),
310        }
311    }
312}
313
314/// Information about the cancellation of a verification request or verification
315/// flow.
316#[derive(Clone, Debug)]
317pub struct CancelInfo {
318    cancelled_by_us: bool,
319    cancel_code: CancelCode,
320    reason: &'static str,
321}
322
323impl CancelInfo {
324    /// Get the human readable reason of the cancellation.
325    pub fn reason(&self) -> &'static str {
326        self.reason
327    }
328
329    /// Get the `CancelCode` that cancelled this verification.
330    pub fn cancel_code(&self) -> &CancelCode {
331        &self.cancel_code
332    }
333
334    /// Was the verification cancelled by us?
335    pub fn cancelled_by_us(&self) -> bool {
336        self.cancelled_by_us
337    }
338}
339
340impl From<Cancelled> for CancelInfo {
341    fn from(c: Cancelled) -> Self {
342        Self { cancelled_by_us: c.cancelled_by_us, cancel_code: c.cancel_code, reason: c.reason }
343    }
344}
345
346#[derive(Clone, Debug)]
347pub struct Cancelled {
348    cancelled_by_us: bool,
349    cancel_code: CancelCode,
350    reason: &'static str,
351}
352
353impl Cancelled {
354    fn new(cancelled_by_us: bool, code: CancelCode) -> Self {
355        let reason = match code {
356            CancelCode::Accepted => {
357                "A m.key.verification.request was accepted by a different device."
358            }
359            CancelCode::InvalidMessage => "The received message was invalid.",
360            CancelCode::KeyMismatch => "The expected key did not match the verified one",
361            CancelCode::MismatchedCommitment => "The hash commitment did not match.",
362            CancelCode::MismatchedSas => "The SAS did not match.",
363            CancelCode::Timeout => "The verification process timed out.",
364            CancelCode::UnexpectedMessage => "The device received an unexpected message.",
365            CancelCode::UnknownMethod => {
366                "The device does not know how to handle the requested method."
367            }
368            CancelCode::UnknownTransaction => {
369                "The device does not know about the given transaction ID."
370            }
371            CancelCode::User => "The user cancelled the verification.",
372            CancelCode::UserMismatch => "The expected user did not match the verified user",
373            _ => "Unknown cancel reason",
374        };
375
376        Self { cancelled_by_us, cancel_code: code, reason }
377    }
378
379    pub fn as_content(&self, flow_id: &FlowId) -> OutgoingContent {
380        match flow_id {
381            FlowId::ToDevice(s) => AnyToDeviceEventContent::KeyVerificationCancel(
382                ToDeviceKeyVerificationCancelEventContent::new(
383                    s.clone(),
384                    self.reason.to_owned(),
385                    self.cancel_code.clone(),
386                ),
387            )
388            .into(),
389
390            FlowId::InRoom(r, e) => (
391                r.clone(),
392                AnyMessageLikeEventContent::KeyVerificationCancel(
393                    KeyVerificationCancelEventContent::new(
394                        self.reason.to_owned(),
395                        self.cancel_code.clone(),
396                        Reference::new(e.clone()),
397                    ),
398                ),
399            )
400                .into(),
401        }
402    }
403}
404
405/// A key verification can be requested and started by a to-device
406/// request or a room event. `FlowId` helps to represent both
407/// usecases.
408#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd)]
409pub enum FlowId {
410    /// The flow ID comes from a to-device request.
411    ToDevice(OwnedTransactionId),
412
413    /// The flow ID comes from a room event.
414    InRoom(OwnedRoomId, OwnedEventId),
415}
416
417impl FlowId {
418    /// Get the room ID if the flow ID comes from a room event.
419    pub fn room_id(&self) -> Option<&RoomId> {
420        as_variant!(self, Self::InRoom(room_id, _) => room_id)
421    }
422
423    /// Get the ID a string.
424    pub fn as_str(&self) -> &str {
425        match self {
426            Self::InRoom(_, event_id) => event_id.as_str(),
427            Self::ToDevice(transaction_id) => transaction_id.as_str(),
428        }
429    }
430}
431
432impl From<OwnedTransactionId> for FlowId {
433    fn from(transaction_id: OwnedTransactionId) -> Self {
434        Self::ToDevice(transaction_id)
435    }
436}
437
438impl From<(OwnedRoomId, OwnedEventId)> for FlowId {
439    fn from(ids: (OwnedRoomId, OwnedEventId)) -> Self {
440        Self::InRoom(ids.0, ids.1)
441    }
442}
443
444impl From<(&RoomId, &EventId)> for FlowId {
445    fn from(ids: (&RoomId, &EventId)) -> Self {
446        Self::InRoom(ids.0.to_owned(), ids.1.to_owned())
447    }
448}
449
450/// A result of a verification flow.
451#[derive(Clone, Debug)]
452pub enum VerificationResult {
453    /// The verification succeeded, nothing needs to be done.
454    Ok,
455    /// The verification was canceled.
456    Cancel(CancelCode),
457    /// The verification is done and has signatures that need to be uploaded.
458    SignatureUpload(SignatureUploadRequest),
459}
460
461#[derive(Clone, Debug)]
462pub struct IdentitiesBeingVerified {
463    private_identity: PrivateCrossSigningIdentity,
464    store: VerificationStore,
465    device_being_verified: DeviceData,
466    own_identity: Option<OwnUserIdentityData>,
467    identity_being_verified: Option<UserIdentityData>,
468}
469
470impl IdentitiesBeingVerified {
471    #[cfg(feature = "qrcode")]
472    async fn can_sign_devices(&self) -> bool {
473        self.private_identity.can_sign_devices().await
474    }
475
476    fn user_id(&self) -> &UserId {
477        self.private_identity.user_id()
478    }
479
480    fn is_self_verification(&self) -> bool {
481        self.user_id() == self.other_user_id()
482    }
483
484    fn other_user_id(&self) -> &UserId {
485        self.device_being_verified.user_id()
486    }
487
488    fn other_device_id(&self) -> &DeviceId {
489        self.device_being_verified.device_id()
490    }
491
492    fn other_device(&self) -> &DeviceData {
493        &self.device_being_verified
494    }
495
496    pub async fn mark_as_done(
497        &self,
498        verified_devices: Option<&[DeviceData]>,
499        verified_identities: Option<&[UserIdentityData]>,
500    ) -> Result<VerificationResult, CryptoStoreError> {
501        let device = self.mark_device_as_verified(verified_devices).await?;
502        let (identity, should_request_secrets) =
503            self.mark_identity_as_verified(verified_identities).await?;
504
505        if device.is_none() && identity.is_none() {
506            // Something went wrong if nothing was verified. We use key
507            // mismatch here, since it's the closest to nothing was verified
508            return Ok(VerificationResult::Cancel(CancelCode::KeyMismatch));
509        }
510
511        let mut changes = Changes::default();
512
513        let signature_request = if let Some(device) = device {
514            // We only sign devices of our own user here.
515            let signature_request = if device.user_id() == self.user_id() {
516                match self.private_identity.sign_device(&device).await {
517                    Ok(r) => Some(r),
518                    Err(SignatureError::MissingSigningKey) => {
519                        warn!(
520                            "Can't sign the device keys for {} {}, \
521                                  no private device signing key found",
522                            device.user_id(),
523                            device.device_id(),
524                        );
525
526                        None
527                    }
528                    Err(e) => {
529                        error!(
530                            user_id = ?device.user_id(),
531                            device_id = ?device.device_id(),
532                            "Error signing device keys: {e:?}",
533                        );
534                        None
535                    }
536                }
537            } else {
538                None
539            };
540
541            changes.devices.changed.push(device);
542            signature_request
543        } else {
544            None
545        };
546
547        let identity_signature_request = if let Some(i) = identity {
548            // We only sign other users here.
549            let request = if let Some(i) = i.other() {
550                // Signing can fail if the user signing key is missing.
551                match self.private_identity.sign_user(i).await {
552                    Ok(r) => Some(r),
553                    Err(SignatureError::MissingSigningKey) => {
554                        warn!(
555                            user_id = ?i.user_id(),
556                            "Can't sign the public cross signing keys, \
557                             no private user signing key found",
558                        );
559                        None
560                    }
561                    Err(e) => {
562                        error!(
563                            user_id = ?i.user_id(),
564                            "Error signing the public cross signing keys: {e:?}",
565                        );
566                        None
567                    }
568                }
569            } else {
570                None
571            };
572
573            changes.identities.changed.push(i);
574            request
575        } else {
576            None
577        };
578
579        // If there are two signature upload requests, merge them. Otherwise
580        // use the one we have or None.
581        //
582        // Realistically at most one request will be used but let's make
583        // this future proof.
584        let merged_request = if let Some(mut r) = signature_request {
585            if let Some(user_request) = identity_signature_request {
586                r.signed_keys.extend(user_request.signed_keys);
587            }
588
589            Some(r)
590        } else {
591            identity_signature_request
592        };
593
594        if should_request_secrets {
595            let secret_requests = self.request_missing_secrets().await?;
596            changes.key_requests = secret_requests;
597        }
598
599        // TODO store the signature upload request as well.
600        self.store.save_changes(changes).await?;
601
602        Ok(merged_request
603            .map(VerificationResult::SignatureUpload)
604            .unwrap_or(VerificationResult::Ok))
605    }
606
607    async fn request_missing_secrets(&self) -> Result<Vec<GossipRequest>, CryptoStoreError> {
608        let mut secrets = self.private_identity.get_missing_secrets().await;
609
610        if self.store.inner.load_backup_keys().await?.decryption_key.is_none() {
611            secrets.push(ruma::events::secret::request::SecretName::RecoveryKey);
612        }
613
614        Ok(GossipMachine::request_missing_secrets(self.user_id(), secrets))
615    }
616
617    async fn mark_identity_as_verified(
618        &self,
619        verified_identities: Option<&[UserIdentityData]>,
620    ) -> Result<(Option<UserIdentityData>, bool), CryptoStoreError> {
621        // If there wasn't an identity available during the verification flow
622        // return early as there's nothing to do.
623        if self.identity_being_verified.is_none() {
624            return Ok((None, false));
625        }
626
627        let identity = self.store.get_user_identity(self.other_user_id()).await?;
628
629        Ok(if let Some(identity) = identity {
630            if self
631                .identity_being_verified
632                .as_ref()
633                .is_some_and(|i| i.master_key() == identity.master_key())
634            {
635                if verified_identities.is_some_and(|i| {
636                    i.iter().any(|verified| verified.user_id() == identity.user_id())
637                }) {
638                    info!(
639                        user_id = ?self.other_user_id(),
640                        "The interactive verification process verified the identity of \
641                        the remote user: marking as verified."
642                    );
643
644                    let should_request_secrets = if let UserIdentityData::Own(i) = &identity {
645                        i.mark_as_verified();
646                        true
647                    } else {
648                        false
649                    };
650
651                    (Some(identity), should_request_secrets)
652                } else {
653                    // Note, this is normal. For example, if we're an existing device in a device
654                    // verification, we don't need to verify our identity: instead the verification
655                    // process should verify the new device.
656                    debug!(
657                        user_id = ?self.other_user_id(),
658                        "The interactive verification process didn't verify \
659                         the user identity of the user that participated in \
660                         the interactive verification",
661                    );
662
663                    (None, false)
664                }
665            } else {
666                warn!(
667                    user_id = ?self.other_user_id(),
668                    "The master keys of the user have changed while an interactive \
669                      verification was going on, not marking the identity as verified.",
670                );
671
672                (None, false)
673            }
674        } else {
675            info!(
676                user_id = ?self.other_user_id(),
677                "The identity of the user was deleted while an interactive \
678                 verification was going on.",
679            );
680            (None, false)
681        })
682    }
683
684    async fn mark_device_as_verified(
685        &self,
686        verified_devices: Option<&[DeviceData]>,
687    ) -> Result<Option<DeviceData>, CryptoStoreError> {
688        let device = self.store.get_device(self.other_user_id(), self.other_device_id()).await?;
689
690        let Some(device) = device else {
691            let device = &self.device_being_verified;
692            info!(
693                user_id = ?device.user_id(),
694                device_id = ?device.device_id(),
695                "The device was deleted while an interactive verification was going on.",
696            );
697            return Ok(None);
698        };
699
700        if device.keys() != self.device_being_verified.keys() {
701            warn!(
702                user_id = ?device.user_id(),
703                device_id = ?device.device_id(),
704                "The device keys have changed while an interactive verification \
705                 was going on, not marking the device as verified.",
706            );
707            return Ok(None);
708        }
709
710        if verified_devices.is_some_and(|v| v.contains(&device)) {
711            info!(
712                user_id = ?device.user_id(),
713                device_id = ?device.device_id(),
714                "The interactive verification process verified the remote device: marking as verified.",
715            );
716
717            device.set_trust_state(LocalTrust::Verified);
718
719            Ok(Some(device))
720        } else {
721            // Note, this is normal. For example, if we're a new device in a QR code device
722            // verification, we'll verify the master key but not (directly) the
723            // remote device. Likewise, in a QR code identity verification, we'll verify the
724            // master key of the remote user but not (directly) their device.
725            debug!(
726                user_id = ?device.user_id(),
727                device_id = ?device.device_id(),
728                "The interactive verification process didn't verify the remote device",
729            );
730
731            Ok(None)
732        }
733    }
734}
735
736#[cfg(test)]
737pub(crate) mod tests {
738    use std::sync::Arc;
739
740    use ruma::{
741        device_id,
742        events::{AnyToDeviceEventContent, ToDeviceEvent},
743        user_id, DeviceId, UserId,
744    };
745    use tokio::sync::Mutex;
746
747    use super::{event_enums::OutgoingContent, VerificationStore};
748    use crate::{
749        olm::PrivateCrossSigningIdentity,
750        store::{Changes, CryptoStore, CryptoStoreWrapper, IdentityChanges, MemoryStore},
751        types::{
752            events::ToDeviceEvents,
753            requests::{AnyOutgoingRequest, OutgoingRequest, OutgoingVerificationRequest},
754        },
755        Account, DeviceData, OtherUserIdentityData, OwnUserIdentityData,
756    };
757
758    pub(crate) fn request_to_event(
759        sender: &UserId,
760        request: &OutgoingVerificationRequest,
761    ) -> ToDeviceEvents {
762        let content =
763            request.to_owned().try_into().expect("Can't fetch content out of the request");
764        wrap_any_to_device_content(sender, content)
765    }
766
767    pub(crate) fn outgoing_request_to_event(
768        sender: &UserId,
769        request: &OutgoingRequest,
770    ) -> ToDeviceEvents {
771        match request.request() {
772            AnyOutgoingRequest::ToDeviceRequest(r) => request_to_event(sender, &r.clone().into()),
773            _ => panic!("Unsupported outgoing request"),
774        }
775    }
776
777    pub(crate) fn wrap_any_to_device_content(
778        sender: &UserId,
779        content: OutgoingContent,
780    ) -> ToDeviceEvents {
781        let content = if let OutgoingContent::ToDevice(c) = content { c } else { unreachable!() };
782        let sender = sender.to_owned();
783
784        match *content {
785            AnyToDeviceEventContent::KeyVerificationRequest(c) => {
786                ToDeviceEvents::KeyVerificationRequest(ToDeviceEvent { sender, content: c })
787            }
788            AnyToDeviceEventContent::KeyVerificationReady(c) => {
789                ToDeviceEvents::KeyVerificationReady(ToDeviceEvent { sender, content: c })
790            }
791            AnyToDeviceEventContent::KeyVerificationKey(c) => {
792                ToDeviceEvents::KeyVerificationKey(ToDeviceEvent { sender, content: c })
793            }
794            AnyToDeviceEventContent::KeyVerificationStart(c) => {
795                ToDeviceEvents::KeyVerificationStart(ToDeviceEvent { sender, content: c })
796            }
797            AnyToDeviceEventContent::KeyVerificationAccept(c) => {
798                ToDeviceEvents::KeyVerificationAccept(ToDeviceEvent { sender, content: c })
799            }
800            AnyToDeviceEventContent::KeyVerificationMac(c) => {
801                ToDeviceEvents::KeyVerificationMac(ToDeviceEvent { sender, content: c })
802            }
803            AnyToDeviceEventContent::KeyVerificationDone(c) => {
804                ToDeviceEvents::KeyVerificationDone(ToDeviceEvent { sender, content: c })
805            }
806
807            _ => unreachable!(),
808        }
809    }
810
811    pub fn alice_id() -> &'static UserId {
812        user_id!("@alice:example.org")
813    }
814
815    pub fn alice_device_id() -> &'static DeviceId {
816        device_id!("JLAFKJWSCS")
817    }
818
819    pub fn bob_id() -> &'static UserId {
820        user_id!("@bob:example.org")
821    }
822
823    pub fn bob_device_id() -> &'static DeviceId {
824        device_id!("BOBDEVICE")
825    }
826
827    pub(crate) async fn setup_stores() -> (Account, VerificationStore, Account, VerificationStore) {
828        let alice = Account::with_device_id(alice_id(), alice_device_id());
829        let alice_store = MemoryStore::new();
830        let (alice_private_identity, _, _) =
831            PrivateCrossSigningIdentity::with_account(&alice).await;
832        let alice_private_identity = Mutex::new(alice_private_identity);
833
834        let bob = Account::with_device_id(bob_id(), bob_device_id());
835        let bob_store = MemoryStore::new();
836        let (bob_private_identity, _, _) = PrivateCrossSigningIdentity::with_account(&bob).await;
837        let bob_private_identity = Mutex::new(bob_private_identity);
838
839        let alice_public_identity =
840            OtherUserIdentityData::from_private(&*alice_private_identity.lock().await).await;
841        let alice_identity_data =
842            OwnUserIdentityData::from_private(&*alice_private_identity.lock().await).await;
843        let bob_public_identity =
844            OtherUserIdentityData::from_private(&*bob_private_identity.lock().await).await;
845        let bob_identity_data =
846            OwnUserIdentityData::from_private(&*bob_private_identity.lock().await).await;
847
848        let alice_device = DeviceData::from_account(&alice);
849        let bob_device = DeviceData::from_account(&bob);
850
851        let alice_changes = Changes {
852            identities: IdentityChanges {
853                new: vec![alice_identity_data.into(), bob_public_identity.into()],
854                changed: vec![],
855                unchanged: vec![],
856            },
857            ..Default::default()
858        };
859        alice_store.save_changes(alice_changes).await.unwrap();
860        alice_store.save_devices(vec![bob_device]);
861
862        let bob_changes = Changes {
863            identities: IdentityChanges {
864                new: vec![bob_identity_data.into(), alice_public_identity.into()],
865                changed: vec![],
866                unchanged: vec![],
867            },
868            ..Default::default()
869        };
870        bob_store.save_changes(bob_changes).await.unwrap();
871        bob_store.save_devices(vec![alice_device]);
872
873        let alice_store = VerificationStore {
874            inner: Arc::new(CryptoStoreWrapper::new(
875                alice.user_id(),
876                alice.device_id(),
877                alice_store,
878            )),
879            account: alice.static_data.clone(),
880            private_identity: alice_private_identity.into(),
881        };
882
883        let bob_store = VerificationStore {
884            account: bob.static_data.clone(),
885            inner: Arc::new(CryptoStoreWrapper::new(bob.user_id(), bob.device_id(), bob_store)),
886            private_identity: bob_private_identity.into(),
887        };
888
889        (alice, alice_store, bob, bob_store)
890    }
891}