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    SasV1(Sas),
188    /// The `m.qr_code.*.v1` verification variant.
189    #[cfg(feature = "qrcode")]
190    QrV1(QrVerification),
191}
192
193impl Verification {
194    /// Try to deconstruct this verification enum into a SAS verification.
195    pub fn sas_v1(self) -> Option<Sas> {
196        as_variant!(self, Verification::SasV1)
197    }
198
199    /// Try to deconstruct this verification enum into a QR code verification.
200    #[cfg(feature = "qrcode")]
201    pub fn qr_v1(self) -> Option<QrVerification> {
202        as_variant!(self, Verification::QrV1)
203    }
204
205    /// Has this verification finished.
206    pub fn is_done(&self) -> bool {
207        match self {
208            Verification::SasV1(s) => s.is_done(),
209            #[cfg(feature = "qrcode")]
210            Verification::QrV1(qr) => qr.is_done(),
211        }
212    }
213
214    /// Get the ID that uniquely identifies this verification flow.
215    pub fn flow_id(&self) -> &str {
216        match self {
217            Verification::SasV1(s) => s.flow_id().as_str(),
218            #[cfg(feature = "qrcode")]
219            Verification::QrV1(qr) => qr.flow_id().as_str(),
220        }
221    }
222
223    /// Has the verification been cancelled.
224    pub fn is_cancelled(&self) -> bool {
225        match self {
226            Verification::SasV1(s) => s.is_cancelled(),
227            #[cfg(feature = "qrcode")]
228            Verification::QrV1(qr) => qr.is_cancelled(),
229        }
230    }
231
232    /// Get our own user id that is participating in this verification.
233    pub fn user_id(&self) -> &UserId {
234        match self {
235            Verification::SasV1(v) => v.user_id(),
236            #[cfg(feature = "qrcode")]
237            Verification::QrV1(v) => v.user_id(),
238        }
239    }
240
241    /// Get the other user id that is participating in this verification.
242    pub fn other_user(&self) -> &UserId {
243        match self {
244            Verification::SasV1(s) => s.other_user_id(),
245            #[cfg(feature = "qrcode")]
246            Verification::QrV1(qr) => qr.other_user_id(),
247        }
248    }
249
250    /// Is this a verification verifying a device that belongs to us.
251    pub fn is_self_verification(&self) -> bool {
252        match self {
253            Verification::SasV1(v) => v.is_self_verification(),
254            #[cfg(feature = "qrcode")]
255            Verification::QrV1(v) => v.is_self_verification(),
256        }
257    }
258
259    fn cancel(&self) -> Option<OutgoingVerificationRequest> {
260        match self {
261            Verification::SasV1(v) => v.cancel(),
262            #[cfg(feature = "qrcode")]
263            Verification::QrV1(v) => v.cancel(),
264        }
265    }
266}
267
268impl From<Sas> for Verification {
269    fn from(sas: Sas) -> Self {
270        Self::SasV1(sas)
271    }
272}
273
274#[cfg(feature = "qrcode")]
275impl From<QrVerification> for Verification {
276    fn from(qr: QrVerification) -> Self {
277        Self::QrV1(qr)
278    }
279}
280
281/// The verification state indicating that the verification finished
282/// successfully.
283///
284/// We can now mark the device in our verified devices list as verified and sign
285/// the master keys in the verified devices list.
286#[cfg(feature = "qrcode")]
287#[derive(Clone, Debug)]
288pub struct Done {
289    verified_devices: Arc<[DeviceData]>,
290    verified_master_keys: Arc<[UserIdentityData]>,
291}
292
293#[cfg(feature = "qrcode")]
294impl Done {
295    pub fn as_content(&self, flow_id: &FlowId) -> OutgoingContent {
296        match flow_id {
297            FlowId::ToDevice(t) => AnyToDeviceEventContent::KeyVerificationDone(
298                ToDeviceKeyVerificationDoneEventContent::new(t.to_owned()),
299            )
300            .into(),
301            FlowId::InRoom(r, e) => (
302                r.to_owned(),
303                AnyMessageLikeEventContent::KeyVerificationDone(
304                    KeyVerificationDoneEventContent::new(Reference::new(e.to_owned())),
305                ),
306            )
307                .into(),
308        }
309    }
310}
311
312/// Information about the cancellation of a verification request or verification
313/// flow.
314#[derive(Clone, Debug)]
315pub struct CancelInfo {
316    cancelled_by_us: bool,
317    cancel_code: CancelCode,
318    reason: &'static str,
319}
320
321impl CancelInfo {
322    /// Get the human readable reason of the cancellation.
323    pub fn reason(&self) -> &'static str {
324        self.reason
325    }
326
327    /// Get the `CancelCode` that cancelled this verification.
328    pub fn cancel_code(&self) -> &CancelCode {
329        &self.cancel_code
330    }
331
332    /// Was the verification cancelled by us?
333    pub fn cancelled_by_us(&self) -> bool {
334        self.cancelled_by_us
335    }
336}
337
338impl From<Cancelled> for CancelInfo {
339    fn from(c: Cancelled) -> Self {
340        Self { cancelled_by_us: c.cancelled_by_us, cancel_code: c.cancel_code, reason: c.reason }
341    }
342}
343
344#[derive(Clone, Debug)]
345pub struct Cancelled {
346    cancelled_by_us: bool,
347    cancel_code: CancelCode,
348    reason: &'static str,
349}
350
351impl Cancelled {
352    fn new(cancelled_by_us: bool, code: CancelCode) -> Self {
353        let reason = match code {
354            CancelCode::Accepted => {
355                "A m.key.verification.request was accepted by a different device."
356            }
357            CancelCode::InvalidMessage => "The received message was invalid.",
358            CancelCode::KeyMismatch => "The expected key did not match the verified one",
359            CancelCode::MismatchedCommitment => "The hash commitment did not match.",
360            CancelCode::MismatchedSas => "The SAS did not match.",
361            CancelCode::Timeout => "The verification process timed out.",
362            CancelCode::UnexpectedMessage => "The device received an unexpected message.",
363            CancelCode::UnknownMethod => {
364                "The device does not know how to handle the requested method."
365            }
366            CancelCode::UnknownTransaction => {
367                "The device does not know about the given transaction ID."
368            }
369            CancelCode::User => "The user cancelled the verification.",
370            CancelCode::UserMismatch => "The expected user did not match the verified user",
371            _ => "Unknown cancel reason",
372        };
373
374        Self { cancelled_by_us, cancel_code: code, reason }
375    }
376
377    pub fn as_content(&self, flow_id: &FlowId) -> OutgoingContent {
378        match flow_id {
379            FlowId::ToDevice(s) => AnyToDeviceEventContent::KeyVerificationCancel(
380                ToDeviceKeyVerificationCancelEventContent::new(
381                    s.clone(),
382                    self.reason.to_owned(),
383                    self.cancel_code.clone(),
384                ),
385            )
386            .into(),
387
388            FlowId::InRoom(r, e) => (
389                r.clone(),
390                AnyMessageLikeEventContent::KeyVerificationCancel(
391                    KeyVerificationCancelEventContent::new(
392                        self.reason.to_owned(),
393                        self.cancel_code.clone(),
394                        Reference::new(e.clone()),
395                    ),
396                ),
397            )
398                .into(),
399        }
400    }
401}
402
403/// A key verification can be requested and started by a to-device
404/// request or a room event. `FlowId` helps to represent both
405/// usecases.
406#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd)]
407pub enum FlowId {
408    /// The flow ID comes from a to-device request.
409    ToDevice(OwnedTransactionId),
410
411    /// The flow ID comes from a room event.
412    InRoom(OwnedRoomId, OwnedEventId),
413}
414
415impl FlowId {
416    /// Get the room ID if the flow ID comes from a room event.
417    pub fn room_id(&self) -> Option<&RoomId> {
418        as_variant!(self, Self::InRoom(room_id, _) => room_id)
419    }
420
421    /// Get the ID a string.
422    pub fn as_str(&self) -> &str {
423        match self {
424            Self::InRoom(_, event_id) => event_id.as_str(),
425            Self::ToDevice(transaction_id) => transaction_id.as_str(),
426        }
427    }
428}
429
430impl From<OwnedTransactionId> for FlowId {
431    fn from(transaction_id: OwnedTransactionId) -> Self {
432        Self::ToDevice(transaction_id)
433    }
434}
435
436impl From<(OwnedRoomId, OwnedEventId)> for FlowId {
437    fn from(ids: (OwnedRoomId, OwnedEventId)) -> Self {
438        Self::InRoom(ids.0, ids.1)
439    }
440}
441
442impl From<(&RoomId, &EventId)> for FlowId {
443    fn from(ids: (&RoomId, &EventId)) -> Self {
444        Self::InRoom(ids.0.to_owned(), ids.1.to_owned())
445    }
446}
447
448/// A result of a verification flow.
449#[derive(Clone, Debug)]
450pub enum VerificationResult {
451    /// The verification succeeded, nothing needs to be done.
452    Ok,
453    /// The verification was canceled.
454    Cancel(CancelCode),
455    /// The verification is done and has signatures that need to be uploaded.
456    SignatureUpload(SignatureUploadRequest),
457}
458
459#[derive(Clone, Debug)]
460pub struct IdentitiesBeingVerified {
461    private_identity: PrivateCrossSigningIdentity,
462    store: VerificationStore,
463    device_being_verified: DeviceData,
464    own_identity: Option<OwnUserIdentityData>,
465    identity_being_verified: Option<UserIdentityData>,
466}
467
468impl IdentitiesBeingVerified {
469    #[cfg(feature = "qrcode")]
470    async fn can_sign_devices(&self) -> bool {
471        self.private_identity.can_sign_devices().await
472    }
473
474    fn user_id(&self) -> &UserId {
475        self.private_identity.user_id()
476    }
477
478    fn is_self_verification(&self) -> bool {
479        self.user_id() == self.other_user_id()
480    }
481
482    fn other_user_id(&self) -> &UserId {
483        self.device_being_verified.user_id()
484    }
485
486    fn other_device_id(&self) -> &DeviceId {
487        self.device_being_verified.device_id()
488    }
489
490    fn other_device(&self) -> &DeviceData {
491        &self.device_being_verified
492    }
493
494    pub async fn mark_as_done(
495        &self,
496        verified_devices: Option<&[DeviceData]>,
497        verified_identities: Option<&[UserIdentityData]>,
498    ) -> Result<VerificationResult, CryptoStoreError> {
499        let device = self.mark_device_as_verified(verified_devices).await?;
500        let (identity, should_request_secrets) =
501            self.mark_identity_as_verified(verified_identities).await?;
502
503        if device.is_none() && identity.is_none() {
504            // Something went wrong if nothing was verified. We use key
505            // mismatch here, since it's the closest to nothing was verified
506            return Ok(VerificationResult::Cancel(CancelCode::KeyMismatch));
507        }
508
509        let mut changes = Changes::default();
510
511        let signature_request = if let Some(device) = device {
512            // We only sign devices of our own user here.
513            let signature_request = if device.user_id() == self.user_id() {
514                match self.private_identity.sign_device(&device).await {
515                    Ok(r) => Some(r),
516                    Err(SignatureError::MissingSigningKey) => {
517                        warn!(
518                            "Can't sign the device keys for {} {}, \
519                                  no private device signing key found",
520                            device.user_id(),
521                            device.device_id(),
522                        );
523
524                        None
525                    }
526                    Err(e) => {
527                        error!(
528                            user_id = ?device.user_id(),
529                            device_id = ?device.device_id(),
530                            "Error signing device keys: {e:?}",
531                        );
532                        None
533                    }
534                }
535            } else {
536                None
537            };
538
539            changes.devices.changed.push(device);
540            signature_request
541        } else {
542            None
543        };
544
545        let identity_signature_request = if let Some(i) = identity {
546            // We only sign other users here.
547            let request = if let Some(i) = i.other() {
548                // Signing can fail if the user signing key is missing.
549                match self.private_identity.sign_user(i).await {
550                    Ok(r) => Some(r),
551                    Err(SignatureError::MissingSigningKey) => {
552                        warn!(
553                            user_id = ?i.user_id(),
554                            "Can't sign the public cross signing keys, \
555                             no private user signing key found",
556                        );
557                        None
558                    }
559                    Err(e) => {
560                        error!(
561                            user_id = ?i.user_id(),
562                            "Error signing the public cross signing keys: {e:?}",
563                        );
564                        None
565                    }
566                }
567            } else {
568                None
569            };
570
571            changes.identities.changed.push(i);
572            request
573        } else {
574            None
575        };
576
577        // If there are two signature upload requests, merge them. Otherwise
578        // use the one we have or None.
579        //
580        // Realistically at most one request will be used but let's make
581        // this future proof.
582        let merged_request = if let Some(mut r) = signature_request {
583            if let Some(user_request) = identity_signature_request {
584                r.signed_keys.extend(user_request.signed_keys);
585            }
586
587            Some(r)
588        } else {
589            identity_signature_request
590        };
591
592        if should_request_secrets {
593            let secret_requests = self.request_missing_secrets().await?;
594            changes.key_requests = secret_requests;
595        }
596
597        // TODO store the signature upload request as well.
598        self.store.save_changes(changes).await?;
599
600        Ok(merged_request
601            .map(VerificationResult::SignatureUpload)
602            .unwrap_or(VerificationResult::Ok))
603    }
604
605    async fn request_missing_secrets(&self) -> Result<Vec<GossipRequest>, CryptoStoreError> {
606        let mut secrets = self.private_identity.get_missing_secrets().await;
607
608        if self.store.inner.load_backup_keys().await?.decryption_key.is_none() {
609            secrets.push(ruma::events::secret::request::SecretName::RecoveryKey);
610        }
611
612        Ok(GossipMachine::request_missing_secrets(self.user_id(), secrets))
613    }
614
615    async fn mark_identity_as_verified(
616        &self,
617        verified_identities: Option<&[UserIdentityData]>,
618    ) -> Result<(Option<UserIdentityData>, bool), CryptoStoreError> {
619        // If there wasn't an identity available during the verification flow
620        // return early as there's nothing to do.
621        if self.identity_being_verified.is_none() {
622            return Ok((None, false));
623        }
624
625        let identity = self.store.get_user_identity(self.other_user_id()).await?;
626
627        Ok(if let Some(identity) = identity {
628            if self
629                .identity_being_verified
630                .as_ref()
631                .is_some_and(|i| i.master_key() == identity.master_key())
632            {
633                if verified_identities.is_some_and(|i| {
634                    i.iter().any(|verified| verified.user_id() == identity.user_id())
635                }) {
636                    info!(
637                        user_id = ?self.other_user_id(),
638                        "The interactive verification process verified the identity of \
639                        the remote user: marking as verified."
640                    );
641
642                    let should_request_secrets = if let UserIdentityData::Own(i) = &identity {
643                        i.mark_as_verified();
644                        true
645                    } else {
646                        false
647                    };
648
649                    (Some(identity), should_request_secrets)
650                } else {
651                    // Note, this is normal. For example, if we're an existing device in a device
652                    // verification, we don't need to verify our identity: instead the verification
653                    // process should verify the new device.
654                    debug!(
655                        user_id = ?self.other_user_id(),
656                        "The interactive verification process didn't verify \
657                         the user identity of the user that participated in \
658                         the interactive verification",
659                    );
660
661                    (None, false)
662                }
663            } else {
664                warn!(
665                    user_id = ?self.other_user_id(),
666                    "The master keys of the user have changed while an interactive \
667                      verification was going on, not marking the identity as verified.",
668                );
669
670                (None, false)
671            }
672        } else {
673            info!(
674                user_id = ?self.other_user_id(),
675                "The identity of the user was deleted while an interactive \
676                 verification was going on.",
677            );
678            (None, false)
679        })
680    }
681
682    async fn mark_device_as_verified(
683        &self,
684        verified_devices: Option<&[DeviceData]>,
685    ) -> Result<Option<DeviceData>, CryptoStoreError> {
686        let device = self.store.get_device(self.other_user_id(), self.other_device_id()).await?;
687
688        let Some(device) = device else {
689            let device = &self.device_being_verified;
690            info!(
691                user_id = ?device.user_id(),
692                device_id = ?device.device_id(),
693                "The device was deleted while an interactive verification was going on.",
694            );
695            return Ok(None);
696        };
697
698        if device.keys() != self.device_being_verified.keys() {
699            warn!(
700                user_id = ?device.user_id(),
701                device_id = ?device.device_id(),
702                "The device keys have changed while an interactive verification \
703                 was going on, not marking the device as verified.",
704            );
705            return Ok(None);
706        }
707
708        if verified_devices.is_some_and(|v| v.contains(&device)) {
709            info!(
710                user_id = ?device.user_id(),
711                device_id = ?device.device_id(),
712                "The interactive verification process verified the remote device: marking as verified.",
713            );
714
715            device.set_trust_state(LocalTrust::Verified);
716
717            Ok(Some(device))
718        } else {
719            // Note, this is normal. For example, if we're a new device in a QR code device
720            // verification, we'll verify the master key but not (directly) the
721            // remote device. Likewise, in a QR code identity verification, we'll verify the
722            // master key of the remote user but not (directly) their device.
723            debug!(
724                user_id = ?device.user_id(),
725                device_id = ?device.device_id(),
726                "The interactive verification process didn't verify the remote device",
727            );
728
729            Ok(None)
730        }
731    }
732}
733
734#[cfg(test)]
735pub(crate) mod tests {
736    use std::sync::Arc;
737
738    use ruma::{
739        device_id,
740        events::{AnyToDeviceEventContent, ToDeviceEvent},
741        user_id, DeviceId, UserId,
742    };
743    use tokio::sync::Mutex;
744
745    use super::{event_enums::OutgoingContent, VerificationStore};
746    use crate::{
747        olm::PrivateCrossSigningIdentity,
748        store::{Changes, CryptoStore, CryptoStoreWrapper, IdentityChanges, MemoryStore},
749        types::{
750            events::ToDeviceEvents,
751            requests::{AnyOutgoingRequest, OutgoingRequest, OutgoingVerificationRequest},
752        },
753        Account, DeviceData, OtherUserIdentityData, OwnUserIdentityData,
754    };
755
756    pub(crate) fn request_to_event(
757        sender: &UserId,
758        request: &OutgoingVerificationRequest,
759    ) -> ToDeviceEvents {
760        let content =
761            request.to_owned().try_into().expect("Can't fetch content out of the request");
762        wrap_any_to_device_content(sender, content)
763    }
764
765    pub(crate) fn outgoing_request_to_event(
766        sender: &UserId,
767        request: &OutgoingRequest,
768    ) -> ToDeviceEvents {
769        match request.request() {
770            AnyOutgoingRequest::ToDeviceRequest(r) => request_to_event(sender, &r.clone().into()),
771            _ => panic!("Unsupported outgoing request"),
772        }
773    }
774
775    pub(crate) fn wrap_any_to_device_content(
776        sender: &UserId,
777        content: OutgoingContent,
778    ) -> ToDeviceEvents {
779        let content = if let OutgoingContent::ToDevice(c) = content { c } else { unreachable!() };
780        let sender = sender.to_owned();
781
782        match content {
783            AnyToDeviceEventContent::KeyVerificationRequest(c) => {
784                ToDeviceEvents::KeyVerificationRequest(ToDeviceEvent { sender, content: c })
785            }
786            AnyToDeviceEventContent::KeyVerificationReady(c) => {
787                ToDeviceEvents::KeyVerificationReady(ToDeviceEvent { sender, content: c })
788            }
789            AnyToDeviceEventContent::KeyVerificationKey(c) => {
790                ToDeviceEvents::KeyVerificationKey(ToDeviceEvent { sender, content: c })
791            }
792            AnyToDeviceEventContent::KeyVerificationStart(c) => {
793                ToDeviceEvents::KeyVerificationStart(ToDeviceEvent { sender, content: c })
794            }
795            AnyToDeviceEventContent::KeyVerificationAccept(c) => {
796                ToDeviceEvents::KeyVerificationAccept(ToDeviceEvent { sender, content: c })
797            }
798            AnyToDeviceEventContent::KeyVerificationMac(c) => {
799                ToDeviceEvents::KeyVerificationMac(ToDeviceEvent { sender, content: c })
800            }
801            AnyToDeviceEventContent::KeyVerificationDone(c) => {
802                ToDeviceEvents::KeyVerificationDone(ToDeviceEvent { sender, content: c })
803            }
804
805            _ => unreachable!(),
806        }
807    }
808
809    pub fn alice_id() -> &'static UserId {
810        user_id!("@alice:example.org")
811    }
812
813    pub fn alice_device_id() -> &'static DeviceId {
814        device_id!("JLAFKJWSCS")
815    }
816
817    pub fn bob_id() -> &'static UserId {
818        user_id!("@bob:example.org")
819    }
820
821    pub fn bob_device_id() -> &'static DeviceId {
822        device_id!("BOBDEVICE")
823    }
824
825    pub(crate) async fn setup_stores() -> (Account, VerificationStore, Account, VerificationStore) {
826        let alice = Account::with_device_id(alice_id(), alice_device_id());
827        let alice_store = MemoryStore::new();
828        let (alice_private_identity, _, _) =
829            PrivateCrossSigningIdentity::with_account(&alice).await;
830        let alice_private_identity = Mutex::new(alice_private_identity);
831
832        let bob = Account::with_device_id(bob_id(), bob_device_id());
833        let bob_store = MemoryStore::new();
834        let (bob_private_identity, _, _) = PrivateCrossSigningIdentity::with_account(&bob).await;
835        let bob_private_identity = Mutex::new(bob_private_identity);
836
837        let alice_public_identity =
838            OtherUserIdentityData::from_private(&*alice_private_identity.lock().await).await;
839        let alice_identity_data =
840            OwnUserIdentityData::from_private(&*alice_private_identity.lock().await).await;
841        let bob_public_identity =
842            OtherUserIdentityData::from_private(&*bob_private_identity.lock().await).await;
843        let bob_identity_data =
844            OwnUserIdentityData::from_private(&*bob_private_identity.lock().await).await;
845
846        let alice_device = DeviceData::from_account(&alice);
847        let bob_device = DeviceData::from_account(&bob);
848
849        let alice_changes = Changes {
850            identities: IdentityChanges {
851                new: vec![alice_identity_data.into(), bob_public_identity.into()],
852                changed: vec![],
853                unchanged: vec![],
854            },
855            ..Default::default()
856        };
857        alice_store.save_changes(alice_changes).await.unwrap();
858        alice_store.save_devices(vec![bob_device]);
859
860        let bob_changes = Changes {
861            identities: IdentityChanges {
862                new: vec![bob_identity_data.into(), alice_public_identity.into()],
863                changed: vec![],
864                unchanged: vec![],
865            },
866            ..Default::default()
867        };
868        bob_store.save_changes(bob_changes).await.unwrap();
869        bob_store.save_devices(vec![alice_device]);
870
871        let alice_store = VerificationStore {
872            inner: Arc::new(CryptoStoreWrapper::new(
873                alice.user_id(),
874                alice.device_id(),
875                alice_store,
876            )),
877            account: alice.static_data.clone(),
878            private_identity: alice_private_identity.into(),
879        };
880
881        let bob_store = VerificationStore {
882            account: bob.static_data.clone(),
883            inner: Arc::new(CryptoStoreWrapper::new(bob.user_id(), bob.device_id(), bob_store)),
884            private_identity: bob_private_identity.into(),
885        };
886
887        (alice, alice_store, bob, bob_store)
888    }
889}