Skip to main content

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