Skip to main content

matrix_sdk_crypto/identities/
device.rs

1// Copyright 2020 The Matrix.org Foundation C.I.C.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::{
16    collections::{BTreeMap, HashMap},
17    ops::Deref,
18    sync::{
19        Arc,
20        atomic::{AtomicBool, Ordering},
21    },
22};
23
24use matrix_sdk_common::locks::RwLock;
25use ruma::{
26    DeviceId, DeviceKeyAlgorithm, DeviceKeyId, MilliSecondsSinceUnixEpoch, OwnedDeviceId,
27    OwnedDeviceKeyId, UInt, UserId,
28    api::client::keys::upload_signatures::v3::Request as SignatureUploadRequest,
29    events::{AnyToDeviceEventContent, key::verification::VerificationMethod},
30    serde::Raw,
31};
32use serde::{Deserialize, Serialize};
33use serde_json::Value;
34use tracing::{instrument, trace};
35use vodozemac::{Curve25519PublicKey, Ed25519PublicKey, olm::SessionConfig};
36
37use super::{atomic_bool_deserializer, atomic_bool_serializer};
38#[cfg(any(test, feature = "testing", doc))]
39use crate::OlmMachine;
40#[cfg(feature = "experimental-x509-identity-verification")]
41use crate::x509::X509Verifier;
42use crate::{
43    Account, Sas, VerificationRequest,
44    error::{MismatchedIdentityKeysError, OlmError, OlmResult, SignatureError},
45    identities::{OwnUserIdentityData, UserIdentityData},
46    olm::{InboundGroupSession, OutboundGroupSession, Session, ShareInfo, VerifyJson},
47    session_manager::{CollectStrategy, withheld_code_for_device_for_share_strategy},
48    store::{
49        CryptoStoreWrapper, Result as StoreResult,
50        caches::SequenceNumber,
51        types::{Changes, DeviceChanges},
52    },
53    types::{
54        DeviceKey, DeviceKeys, EventEncryptionAlgorithm, Signatures, SignedKey,
55        events::{
56            EventType, forwarded_room_key::ForwardedRoomKeyContent,
57            room::encrypted::ToDeviceEncryptedEventContent,
58        },
59        requests::{OutgoingVerificationRequest, ToDeviceRequest},
60    },
61    verification::VerificationMachine,
62};
63
64pub enum MaybeEncryptedRoomKey {
65    Encrypted {
66        // `Box` the session to reduce the size of `Encrypted`.
67        used_session: Box<Session>,
68        // `Box` the session to reduce the size of `Encrypted`.
69        share_info: Box<ShareInfo>,
70        message: Raw<AnyToDeviceEventContent>,
71    },
72    /// We could not encrypt a message to this device because there is no active
73    /// Olm session.
74    MissingSession,
75}
76
77/// A read-only version of a `Device`.
78#[derive(Clone, Serialize, Deserialize)]
79pub struct DeviceData {
80    #[serde(alias = "inner")]
81    pub(crate) device_keys: Arc<DeviceKeys>,
82    #[serde(
83        serialize_with = "atomic_bool_serializer",
84        deserialize_with = "atomic_bool_deserializer"
85    )]
86    deleted: Arc<AtomicBool>,
87    trust_state: Arc<RwLock<LocalTrust>>,
88    /// Flag remembering if we successfully sent an `m.no_olm` withheld code to
89    /// this device.
90    #[serde(
91        default,
92        serialize_with = "atomic_bool_serializer",
93        deserialize_with = "atomic_bool_deserializer"
94    )]
95    withheld_code_sent: Arc<AtomicBool>,
96    /// First time this device was seen in milliseconds since epoch. Default to
97    /// epoch for migration purpose.
98    #[serde(default = "default_timestamp")]
99    first_time_seen_ts: MilliSecondsSinceUnixEpoch,
100    /// The number of times the device has tried to unwedge Olm sessions with
101    /// us.
102    #[serde(default)]
103    pub(crate) olm_wedging_index: SequenceNumber,
104}
105
106fn default_timestamp() -> MilliSecondsSinceUnixEpoch {
107    MilliSecondsSinceUnixEpoch(UInt::default())
108}
109
110#[cfg(not(tarpaulin_include))]
111impl std::fmt::Debug for DeviceData {
112    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
113        f.debug_struct("DeviceData")
114            .field("user_id", &self.user_id())
115            .field("device_id", &self.device_id())
116            .field("display_name", &self.display_name())
117            .field("keys", self.keys())
118            .field("deleted", &self.deleted.load(Ordering::SeqCst))
119            .field("trust_state", &self.trust_state)
120            .field("withheld_code_sent", &self.withheld_code_sent)
121            .finish()
122    }
123}
124
125/// A device represents a E2EE capable client of an user.
126#[derive(Clone)]
127pub struct Device {
128    pub(crate) inner: DeviceData,
129    pub(crate) verification_machine: VerificationMachine,
130    pub(crate) own_identity: Option<OwnUserIdentityData>,
131    pub(crate) device_owner_identity: Option<UserIdentityData>,
132    #[cfg(feature = "experimental-x509-identity-verification")]
133    pub(crate) x509_verifier: Option<X509Verifier>,
134}
135
136#[cfg(not(tarpaulin_include))]
137impl std::fmt::Debug for Device {
138    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
139        f.debug_struct("Device").field("device", &self.inner).finish()
140    }
141}
142
143impl Deref for Device {
144    type Target = DeviceData;
145
146    fn deref(&self) -> &Self::Target {
147        &self.inner
148    }
149}
150
151impl Device {
152    /// Start a interactive verification with this `Device`
153    ///
154    /// Returns a `Sas` object and a to-device request that needs to be sent
155    /// out.
156    ///
157    /// This method has been deprecated in the spec and the
158    /// [`request_verification()`] method should be used instead.
159    ///
160    /// [`request_verification()`]: #method.request_verification
161    pub async fn start_verification(&self) -> StoreResult<(Sas, ToDeviceRequest)> {
162        let (sas, request) = self.verification_machine.start_sas(self.inner.clone()).await?;
163
164        if let OutgoingVerificationRequest::ToDevice(r) = request {
165            Ok((sas, r))
166        } else {
167            panic!("Invalid verification request type");
168        }
169    }
170
171    /// Is this our own device?
172    pub fn is_our_own_device(&self) -> bool {
173        let own_ed25519_key = self.verification_machine.store.account.identity_keys.ed25519;
174        let own_curve25519_key = self.verification_machine.store.account.identity_keys.curve25519;
175
176        self.user_id() == self.verification_machine.own_user_id()
177            && self.device_id() == self.verification_machine.own_device_id()
178            && self.ed25519_key().is_some_and(|k| k == own_ed25519_key)
179            && self.curve25519_key().is_some_and(|k| k == own_curve25519_key)
180    }
181
182    /// Does the given `InboundGroupSession` belong to this device?
183    ///
184    /// An `InboundGroupSession` is exchanged between devices as an Olm
185    /// encrypted `m.room_key` event. This method determines if this `Device`
186    /// can be confirmed as the creator and owner of the `m.room_key`.
187    pub fn is_owner_of_session(
188        &self,
189        session: &InboundGroupSession,
190    ) -> Result<bool, MismatchedIdentityKeysError> {
191        if session.has_been_imported() {
192            // An imported room key means that we did not receive the room key
193            // as a `m.room_key` event when the room key was initially
194            // exchanged.
195            //
196            // This could mean a couple of things:
197            //      1. We received the room key as a `m.forwarded_room_key`.
198            //      2. We imported the room key through a file export.
199            //      3. We imported the room key through a backup.
200            //
201            // To be certain that a `Device` is the owner of a room key we need
202            // to have a proof that the `Curve25519` key of this `Device` was
203            // used to initially exchange the room key. This proof is provided
204            // by the Olm decryption step, see below for further clarification.
205            //
206            // Each of the above room key methods that receive room keys do not
207            // contain this proof and we received only a claim that the room key
208            // is tied to a `Curve25519` key.
209            //
210            // Since there's no way to verify that the claim is true, we say
211            // that we don't know that the room key belongs to this device.
212            Ok(false)
213        } else if let Some(key) =
214            session.signing_keys().get(&DeviceKeyAlgorithm::Ed25519).and_then(|k| k.ed25519())
215        {
216            // Room keys are received as an `m.room.encrypted` to-device message
217            // using the `m.olm` algorithm. Upon decryption of the
218            // `m.room.encrypted` to-device message, the decrypted content will
219            // contain also an `Ed25519` public key[1].
220            //
221            // The inclusion of this key means that the `Curve25519` key of the
222            // `Device` and Olm `Session`, established using the DH
223            // authentication of the double ratchet, "binds" the `Ed25519` key
224            // of the `Device`. In other words, it prevents an attack in which
225            // Mallory publishes Bob's public `Curve25519` key as her own, and
226            // subsequently forwards an Olm message she received from Bob to
227            // Alice, claiming that she, Mallory, originated the Olm message
228            // (leading Alice to believe that Mallory also sent the messages in
229            // the subsequent Megolm session).
230            //
231            // On the other hand, the `Ed25519` key binds the `Curve25519` key
232            // using a signature which is uploaded to the server as
233            // `device_keys` and downloaded by us using a `/keys/query` request.
234            //
235            // A `Device` is considered to be the owner of a room key iff:
236            //     1. The `Curve25519` key that was used to establish the Olm `Session` that
237            //        was used to decrypt the to-device message is binding the `Ed25519` key
238            //        of this `Device` via the content of the to-device message, and:
239            //     2. The `Ed25519` key of this device has signed a `device_keys` object
240            //        that contains the `Curve25519` key from step 1.
241            //
242            // We don't need to check the signature of the `Device` here, since
243            // we don't accept a `Device` unless it has a valid `Ed25519`
244            // signature.
245            //
246            // We do check that the `Curve25519` that was used to decrypt the
247            // event carrying the `m.room_key` and the `Ed25519` key that was
248            // part of the decrypted content matches the keys found in this
249            // `Device`.
250            //
251            // ```text
252            //                                              ┌───────────────────────┐
253            //                                              │ EncryptedToDeviceEvent│
254            //                                              └───────────────────────┘
255            //                                                         │
256            //    ┌──────────────────────────────────┐                 │
257            //    │              Device              │                 ▼
258            //    ├──────────────────────────────────┤        ┌──────────────────┐
259            //    │            Device Keys           │        │      Session     │
260            //    ├────────────────┬─────────────────┤        ├──────────────────┤
261            //    │   Ed25519 Key  │  Curve25519 Key │◄──────►│  Curve25519 Key  │
262            //    └────────────────┴─────────────────┘        └──────────────────┘
263            //            ▲                                            │
264            //            │                                            │
265            //            │                                            │ Decrypt
266            //            │                                            │
267            //            │                                            ▼
268            //            │                                 ┌───────────────────────┐
269            //            │                                 │  DecryptedOlmV1Event  │
270            //            │                                 ├───────────────────────┤
271            //            │                                 │         keys          │
272            //            │                                 ├───────────────────────┤
273            //            └────────────────────────────────►│       Ed25519 Key     │
274            //                                              └───────────────────────┘
275            // ```
276            //
277            // [1]: https://spec.matrix.org/v1.5/client-server-api/#molmv1curve25519-aes-sha2
278            let ed25519_comparison = self.ed25519_key().map(|k| k == key);
279            let curve25519_comparison = self.curve25519_key().map(|k| k == session.sender_key());
280
281            match (ed25519_comparison, curve25519_comparison) {
282                // If we have any of the keys but they don't turn out to match,
283                // refuse to decrypt instead.
284                (_, Some(false)) | (Some(false), _) => Err(MismatchedIdentityKeysError {
285                    key_ed25519: key.into(),
286                    device_ed25519: self.ed25519_key().map(Into::into),
287                    key_curve25519: session.sender_key().into(),
288                    device_curve25519: self.curve25519_key().map(Into::into),
289                }),
290                // If both keys match, we have ourselves an owner.
291                (Some(true), Some(true)) => Ok(true),
292                // In the remaining cases, the device is missing at least one of
293                // the required identity keys, so we default to a negative
294                // answer.
295                _ => Ok(false),
296            }
297        } else {
298            Ok(false)
299        }
300    }
301
302    /// Is this device cross signed by its owner?
303    pub fn is_cross_signed_by_owner(&self) -> bool {
304        self.device_owner_identity
305            .as_ref()
306            .is_some_and(|owner_identity| self.inner.is_cross_signed_by_owner(owner_identity))
307    }
308
309    /// Is the device owner verified by us?
310    pub fn is_device_owner_verified(&self) -> bool {
311        self.device_owner_identity.as_ref().is_some_and(|id| match id {
312            UserIdentityData::Own(own_identity) => own_identity.is_verified(),
313            UserIdentityData::Other(other_identity) => other_identity.is_verified(
314                self.own_identity.as_ref(),
315                #[cfg(feature = "experimental-x509-identity-verification")]
316                self.x509_verifier.as_ref(),
317            ),
318        })
319    }
320
321    /// Request an interactive verification with this `Device`.
322    ///
323    /// Returns a `VerificationRequest` object and a to-device request that
324    /// needs to be sent out.
325    pub fn request_verification(&self) -> (VerificationRequest, OutgoingVerificationRequest) {
326        self.request_verification_helper(None)
327    }
328
329    /// Request an interactive verification with this `Device`.
330    ///
331    /// Returns a `VerificationRequest` object and a to-device request that
332    /// needs to be sent out.
333    ///
334    /// # Arguments
335    ///
336    /// - `methods` - The verification methods that we want to support.
337    pub fn request_verification_with_methods(
338        &self,
339        methods: Vec<VerificationMethod>,
340    ) -> (VerificationRequest, OutgoingVerificationRequest) {
341        self.request_verification_helper(Some(methods))
342    }
343
344    fn request_verification_helper(
345        &self,
346        methods: Option<Vec<VerificationMethod>>,
347    ) -> (VerificationRequest, OutgoingVerificationRequest) {
348        self.verification_machine.request_to_device_verification(
349            self.user_id(),
350            vec![self.device_id().to_owned()],
351            methods,
352        )
353    }
354
355    /// Get the most recently created session that belongs to this device.
356    pub(crate) async fn get_most_recent_session(&self) -> OlmResult<Option<Session>> {
357        self.inner.get_most_recent_session(self.verification_machine.store.inner()).await
358    }
359
360    /// Is this device considered to be verified.
361    ///
362    /// This method returns true if either [`is_locally_trusted()`] returns true
363    /// or if [`is_cross_signing_trusted()`] returns true.
364    ///
365    /// [`is_locally_trusted()`]: #method.is_locally_trusted
366    /// [`is_cross_signing_trusted()`]: #method.is_cross_signing_trusted
367    pub fn is_verified(&self) -> bool {
368        self.inner.is_verified(
369            &self.own_identity,
370            &self.device_owner_identity,
371            #[cfg(feature = "experimental-x509-identity-verification")]
372            self.x509_verifier.as_ref(),
373        )
374    }
375
376    /// Is this device considered to be verified using cross signing.
377    pub fn is_cross_signing_trusted(&self) -> bool {
378        self.inner.is_cross_signing_trusted(
379            &self.own_identity,
380            &self.device_owner_identity,
381            #[cfg(feature = "experimental-x509-identity-verification")]
382            self.x509_verifier.as_ref(),
383        )
384    }
385
386    /// Manually verify this device.
387    ///
388    /// This method will attempt to sign the device using our private cross
389    /// signing key.
390    ///
391    /// This method will always fail if the device belongs to someone else, we
392    /// can only sign our own devices.
393    ///
394    /// It can also fail if we don't have the private part of our self-signing
395    /// key.
396    ///
397    /// Returns a request that needs to be sent out for the device to be marked
398    /// as verified.
399    pub async fn verify(&self) -> Result<SignatureUploadRequest, SignatureError> {
400        if self.user_id() == self.verification_machine.own_user_id() {
401            Ok(self
402                .verification_machine
403                .store
404                .private_identity
405                .lock()
406                .await
407                .sign_device(&self.inner)
408                .await?)
409        } else {
410            Err(SignatureError::UserIdMismatch)
411        }
412    }
413
414    /// Set the local trust state of the device to the given state.
415    ///
416    /// This won't affect any cross signing trust state, this only sets a flag
417    /// marking to have the given trust state.
418    ///
419    /// # Arguments
420    ///
421    /// - `trust_state` - The new trust state that should be set for the device.
422    pub async fn set_local_trust(&self, trust_state: LocalTrust) -> StoreResult<()> {
423        self.inner.set_trust_state(trust_state);
424
425        let changes = Changes {
426            devices: DeviceChanges { changed: vec![self.inner.clone()], ..Default::default() },
427            ..Default::default()
428        };
429
430        self.verification_machine.store.save_changes(changes).await
431    }
432
433    /// Encrypt the given content for this `Device`.
434    ///
435    /// # Arguments
436    ///
437    /// - `event_type` - The type of the event that should be encrypted.
438    /// - `content` - The content of the event that should be encrypted.
439    ///
440    /// # Returns
441    ///
442    /// On success, a tuple `(session, content, message_id)`, where `session` is
443    /// the Olm [`Session`] that was used to encrypt the content, `content` is
444    /// the content for the `m.room.encrypted` to-device event, and `message_id`
445    /// is the newly-minted message ID stored within the content.
446    ///
447    /// If an Olm session has not already been established with this device,
448    /// returns `Err(OlmError::MissingSession)`.
449    pub(crate) async fn encrypt(
450        &self,
451        event_type: &str,
452        content: impl Serialize,
453    ) -> OlmResult<(Session, Raw<ToDeviceEncryptedEventContent>, String)> {
454        self.inner.encrypt(self.verification_machine.store.inner(), event_type, content).await
455    }
456
457    /// Encrypt the given inbound group session as a forwarded room key for this
458    /// device.
459    pub async fn encrypt_room_key_for_forwarding(
460        &self,
461        session: InboundGroupSession,
462        message_index: Option<u32>,
463    ) -> OlmResult<(Session, Raw<ToDeviceEncryptedEventContent>)> {
464        let content: ForwardedRoomKeyContent = {
465            let export = if let Some(index) = message_index {
466                session.export_at_index(index).await
467            } else {
468                session.export().await
469            };
470
471            export.try_into()?
472        };
473
474        let event_type = content.event_type().to_owned();
475
476        self.encrypt(&event_type, content)
477            .await
478            .map(|(session, message, _message_id)| (session, message))
479    }
480
481    /// Encrypt an event for this device.
482    ///
483    /// Beware that the 1-to-1 session must be established prior to this call by
484    /// using the [`OlmMachine::get_missing_sessions`] method.
485    ///
486    /// Notable limitation: The caller is responsible for sending the encrypted
487    /// event to the target device, this encryption method supports out-of-order
488    /// messages to a certain extent (2000 messages), if multiple messages are
489    /// encrypted using this method they should be sent in the same order as
490    /// they are encrypted.
491    ///
492    /// _Note_: To instead encrypt an event meant for a room use the
493    /// [`OlmMachine::encrypt_room_event()`] method instead.
494    ///
495    /// # Arguments
496    ///
497    /// - `event_type` - The type of the event to be sent.
498    /// - `content` - The content of the event to be sent. This should be a type
499    ///   that implements the `Serialize` trait.
500    /// - `share_strategy` - The share strategy to use to determine whether we
501    ///   should encrypt to the device.
502    ///
503    /// # Returns
504    ///
505    /// The encrypted raw content to be shared with your preferred transport
506    /// layer (usually to-device), [`OlmError::MissingSession`] if there is no
507    /// established session with the device.
508    pub async fn encrypt_event_raw(
509        &self,
510        event_type: &str,
511        content: &Value,
512        share_strategy: CollectStrategy,
513    ) -> OlmResult<Raw<ToDeviceEncryptedEventContent>> {
514        if let Some(withheld_code) = withheld_code_for_device_for_share_strategy(
515            &self.inner,
516            share_strategy,
517            &self.own_identity,
518            &self.device_owner_identity,
519            #[cfg(feature = "experimental-x509-identity-verification")]
520            self.x509_verifier.as_ref(),
521        )
522        .await?
523        {
524            return Err(OlmError::Withheld(withheld_code));
525        }
526
527        let (used_session, raw_encrypted, _message_id) = self.encrypt(event_type, content).await?;
528
529        // Persist the used session
530        self.verification_machine
531            .store
532            .save_changes(Changes { sessions: vec![used_session], ..Default::default() })
533            .await?;
534
535        Ok(raw_encrypted)
536    }
537
538    /// True if this device is an [MSC3814](https://github.com/matrix-org/matrix-spec-proposals/pull/3814) dehydrated device.
539    pub fn is_dehydrated(&self) -> bool {
540        self.inner.is_dehydrated()
541    }
542}
543
544/// A read only view over all devices belonging to a user.
545#[derive(Debug)]
546pub struct UserDevices {
547    pub(crate) inner: HashMap<OwnedDeviceId, DeviceData>,
548    pub(crate) verification_machine: VerificationMachine,
549    pub(crate) own_identity: Option<OwnUserIdentityData>,
550    pub(crate) device_owner_identity: Option<UserIdentityData>,
551    #[cfg(feature = "experimental-x509-identity-verification")]
552    pub(crate) x509_verifier: Option<X509Verifier>,
553}
554
555impl UserDevices {
556    /// Get the specific device with the given device ID.
557    pub fn get(&self, device_id: &DeviceId) -> Option<Device> {
558        self.inner.get(device_id).map(|d| Device {
559            inner: d.clone(),
560            verification_machine: self.verification_machine.clone(),
561            own_identity: self.own_identity.clone(),
562            device_owner_identity: self.device_owner_identity.clone(),
563            #[cfg(feature = "experimental-x509-identity-verification")]
564            x509_verifier: self.x509_verifier.clone(),
565        })
566    }
567
568    fn own_user_id(&self) -> &UserId {
569        self.verification_machine.own_user_id()
570    }
571
572    fn own_device_id(&self) -> &DeviceId {
573        self.verification_machine.own_device_id()
574    }
575
576    /// Returns true if there is at least one devices of this user that is
577    /// considered to be verified, false otherwise.
578    ///
579    /// This won't consider your own device as verified, as your own device is
580    /// always implicitly verified.
581    pub fn is_any_verified(&self) -> bool {
582        self.inner
583            .values()
584            .filter(|d| {
585                !(d.user_id() == self.own_user_id() && d.device_id() == self.own_device_id())
586            })
587            .any(|d| {
588                d.is_verified(
589                    &self.own_identity,
590                    &self.device_owner_identity,
591                    #[cfg(feature = "experimental-x509-identity-verification")]
592                    self.x509_verifier.as_ref(),
593                )
594            })
595    }
596
597    /// Iterator over all the device ids of the user devices.
598    pub fn keys(&self) -> impl Iterator<Item = &DeviceId> {
599        self.inner.keys().map(Deref::deref)
600    }
601
602    /// Iterator over all the devices of the user devices.
603    pub fn devices(&self) -> impl Iterator<Item = Device> + '_ {
604        self.inner.values().map(move |d| Device {
605            inner: d.clone(),
606            verification_machine: self.verification_machine.clone(),
607            own_identity: self.own_identity.clone(),
608            device_owner_identity: self.device_owner_identity.clone(),
609            #[cfg(feature = "experimental-x509-identity-verification")]
610            x509_verifier: self.x509_verifier.clone(),
611        })
612    }
613}
614
615/// The local trust state of a device.
616#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
617#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
618pub enum LocalTrust {
619    /// The device has been verified and is trusted.
620    Verified = 0,
621    /// The device been blacklisted from communicating.
622    BlackListed = 1,
623    /// The trust state of the device is being ignored.
624    Ignored = 2,
625    /// The trust state is unset.
626    Unset = 3,
627}
628
629impl From<i64> for LocalTrust {
630    fn from(state: i64) -> Self {
631        match state {
632            0 => LocalTrust::Verified,
633            1 => LocalTrust::BlackListed,
634            2 => LocalTrust::Ignored,
635            3 => LocalTrust::Unset,
636            _ => LocalTrust::Unset,
637        }
638    }
639}
640
641impl DeviceData {
642    /// Create a new Device, this constructor skips signature verification of
643    /// the keys, `TryFrom` should be used for completely new devices we
644    /// receive.
645    pub fn new(device_keys: DeviceKeys, trust_state: LocalTrust) -> Self {
646        Self {
647            device_keys: device_keys.into(),
648            trust_state: Arc::new(RwLock::new(trust_state)),
649            deleted: Arc::new(AtomicBool::new(false)),
650            withheld_code_sent: Arc::new(AtomicBool::new(false)),
651            first_time_seen_ts: MilliSecondsSinceUnixEpoch::now(),
652            olm_wedging_index: Default::default(),
653        }
654    }
655
656    /// The user id of the device owner.
657    pub fn user_id(&self) -> &UserId {
658        &self.device_keys.user_id
659    }
660
661    /// The unique ID of the device.
662    pub fn device_id(&self) -> &DeviceId {
663        &self.device_keys.device_id
664    }
665
666    /// Get the human readable name of the device.
667    pub fn display_name(&self) -> Option<&str> {
668        self.device_keys.unsigned.device_display_name.as_deref()
669    }
670
671    /// Get the key of the given key algorithm belonging to this device.
672    pub fn get_key(&self, algorithm: DeviceKeyAlgorithm) -> Option<&DeviceKey> {
673        self.device_keys.get_key(algorithm)
674    }
675
676    /// Get the Curve25519 key of the given device.
677    pub fn curve25519_key(&self) -> Option<Curve25519PublicKey> {
678        self.device_keys.curve25519_key()
679    }
680
681    /// Get the Ed25519 key of the given device.
682    pub fn ed25519_key(&self) -> Option<Ed25519PublicKey> {
683        self.device_keys.ed25519_key()
684    }
685
686    /// Get a map containing all the device keys.
687    pub fn keys(&self) -> &BTreeMap<OwnedDeviceKeyId, DeviceKey> {
688        &self.device_keys.keys
689    }
690
691    /// Get a map containing all the device signatures.
692    pub fn signatures(&self) -> &Signatures {
693        &self.device_keys.signatures
694    }
695
696    /// Get the trust state of the device.
697    pub fn local_trust_state(&self) -> LocalTrust {
698        *self.trust_state.read()
699    }
700
701    /// Is the device locally marked as trusted.
702    pub fn is_locally_trusted(&self) -> bool {
703        self.local_trust_state() == LocalTrust::Verified
704    }
705
706    /// Is the device locally marked as blacklisted.
707    ///
708    /// Blacklisted devices won't receive any group sessions.
709    pub fn is_blacklisted(&self) -> bool {
710        self.local_trust_state() == LocalTrust::BlackListed
711    }
712
713    /// Set the trust state of the device to the given state.
714    ///
715    /// Note: This should only done in the crypto store where the trust state
716    /// can be stored.
717    pub(crate) fn set_trust_state(&self, state: LocalTrust) {
718        *self.trust_state.write() = state;
719    }
720
721    pub(crate) fn mark_withheld_code_as_sent(&self) {
722        self.withheld_code_sent.store(true, Ordering::Relaxed)
723    }
724
725    /// Returns true if the `m.no_olm` withheld code was already sent to this
726    /// device.
727    pub fn was_withheld_code_sent(&self) -> bool {
728        self.withheld_code_sent.load(Ordering::Relaxed)
729    }
730
731    /// Get the list of algorithms this device supports.
732    pub fn algorithms(&self) -> &[EventEncryptionAlgorithm] {
733        &self.device_keys.algorithms
734    }
735
736    /// Does this device support any of our known Olm encryption algorithms.
737    pub fn supports_olm(&self) -> bool {
738        #[cfg(feature = "experimental-algorithms")]
739        {
740            self.algorithms().contains(&EventEncryptionAlgorithm::OlmV1Curve25519AesSha2)
741                || self.algorithms().contains(&EventEncryptionAlgorithm::OlmV2Curve25519AesSha2)
742        }
743
744        #[cfg(not(feature = "experimental-algorithms"))]
745        {
746            self.algorithms().contains(&EventEncryptionAlgorithm::OlmV1Curve25519AesSha2)
747        }
748    }
749
750    /// Find and return the most recently created Olm [`Session`] we are sharing
751    /// with this device.
752    pub(crate) async fn get_most_recent_session(
753        &self,
754        store: &CryptoStoreWrapper,
755    ) -> OlmResult<Option<Session>> {
756        if let Some(sender_key) = self.curve25519_key() {
757            if let Some(sessions) = store.get_sessions(&sender_key.to_base64()).await? {
758                let mut sessions = sessions.lock().await;
759                sessions.sort_by_key(|s| s.creation_time);
760
761                Ok(sessions.last().cloned())
762            } else {
763                Ok(None)
764            }
765        } else {
766            Ok(None)
767        }
768    }
769
770    /// Does this device support the olm.v2.curve25519-aes-sha2 encryption
771    /// algorithm.
772    #[cfg(feature = "experimental-algorithms")]
773    pub fn supports_olm_v2(&self) -> bool {
774        self.algorithms().contains(&EventEncryptionAlgorithm::OlmV2Curve25519AesSha2)
775    }
776
777    /// Get the optimal `SessionConfig` for this device.
778    pub fn olm_session_config(&self) -> SessionConfig {
779        #[cfg(feature = "experimental-algorithms")]
780        if self.supports_olm_v2() {
781            SessionConfig::version_2()
782        } else {
783            SessionConfig::version_1()
784        }
785
786        #[cfg(not(feature = "experimental-algorithms"))]
787        SessionConfig::version_1()
788    }
789
790    /// Is the device deleted.
791    pub fn is_deleted(&self) -> bool {
792        self.deleted.load(Ordering::Relaxed)
793    }
794
795    pub(crate) fn is_verified(
796        &self,
797        own_identity: &Option<OwnUserIdentityData>,
798        device_owner: &Option<UserIdentityData>,
799        #[cfg(feature = "experimental-x509-identity-verification")] x509_verifier: Option<
800            &X509Verifier,
801        >,
802    ) -> bool {
803        self.is_locally_trusted()
804            || self.is_cross_signing_trusted(
805                own_identity,
806                device_owner,
807                #[cfg(feature = "experimental-x509-identity-verification")]
808                x509_verifier,
809            )
810    }
811
812    pub(crate) fn is_cross_signing_trusted(
813        &self,
814        own_identity: &Option<OwnUserIdentityData>,
815        device_owner: &Option<UserIdentityData>,
816        #[cfg(feature = "experimental-x509-identity-verification")] x509_verifier: Option<
817            &X509Verifier,
818        >,
819    ) -> bool {
820        device_owner.as_ref().is_some_and(|device_identity| match device_identity {
821            UserIdentityData::Own(_) => own_identity.as_ref().is_some_and(|own_identity| {
822                own_identity.is_verified() && own_identity.is_device_signed(self)
823            }),
824
825            // If it's a device from someone else, first check that our user has
826            // verified the other user (either by cross-signing their identity,
827            // or via a valid X.509 signature on their master key) and then
828            // check if the other user has signed this device.
829            UserIdentityData::Other(device_identity) => {
830                device_identity.is_verified(
831                    own_identity.as_ref(),
832                    #[cfg(feature = "experimental-x509-identity-verification")]
833                    x509_verifier,
834                ) && device_identity.is_device_signed(self)
835            }
836        })
837    }
838
839    pub(crate) fn is_cross_signed_by_owner(
840        &self,
841        device_owner_identity: &UserIdentityData,
842    ) -> bool {
843        match device_owner_identity {
844            // If it's one of our own devices, just check that we signed the
845            // device.
846            UserIdentityData::Own(identity) => identity.is_device_signed(self),
847            // If it's a device from someone else, check if the other user has
848            // signed this device.
849            UserIdentityData::Other(device_identity) => device_identity.is_device_signed(self),
850        }
851    }
852
853    /// Encrypt the given content for this device.
854    ///
855    /// # Arguments
856    ///
857    /// - `store` - The crypto store. Used to find an established Olm session
858    ///   for this device.
859    /// - `event_type` - The type of the event that should be encrypted.
860    /// - `content` - The content of the event that should be encrypted.
861    ///
862    /// # Returns
863    ///
864    /// On success, a tuple `(session, content, message_id)`, where `session` is
865    /// the Olm [`Session`] that was used to encrypt the content, `content` is
866    /// the content for the `m.room.encrypted` to-device event, and `message_id`
867    /// is the newly-minted message ID stored within the content.
868    ///
869    /// If an Olm session has not already been established with this device,
870    /// returns `Err(OlmError::MissingSession)`.
871    #[instrument(
872        skip_all,
873        fields(
874            recipient = ?self.user_id(),
875            recipient_device = ?self.device_id(),
876            recipient_key = ?self.curve25519_key(),
877            event_type,
878            message_id,
879        ))
880    ]
881    pub(crate) async fn encrypt(
882        &self,
883        store: &CryptoStoreWrapper,
884        event_type: &str,
885        content: impl Serialize,
886    ) -> OlmResult<(Session, Raw<ToDeviceEncryptedEventContent>, String)> {
887        #[cfg(not(target_family = "wasm"))]
888        let message_id = ulid::Ulid::generate().to_string();
889        #[cfg(target_family = "wasm")]
890        let message_id = ruma::TransactionId::new().to_string();
891
892        tracing::Span::current().record("message_id", &message_id);
893
894        let session = self.get_most_recent_session(store).await?;
895
896        if let Some(mut session) = session {
897            let message =
898                session.encrypt(self, event_type, content, Some(message_id.clone())).await?;
899
900            Ok((session, message, message_id))
901        } else {
902            trace!("Trying to encrypt an event for a device, but no Olm session is found.");
903            Err(OlmError::MissingSession)
904        }
905    }
906
907    pub(crate) async fn maybe_encrypt_room_key(
908        &self,
909        store: &CryptoStoreWrapper,
910        session: OutboundGroupSession,
911    ) -> OlmResult<MaybeEncryptedRoomKey> {
912        let content = session.as_content().await;
913        let message_index = session.message_index().await;
914        let event_type = content.event_type().to_owned();
915
916        match self.encrypt(store, &event_type, content).await {
917            Ok((session, encrypted, _)) => Ok(MaybeEncryptedRoomKey::Encrypted {
918                share_info: Box::new(ShareInfo::new_shared(
919                    session.sender_key().to_owned(),
920                    message_index,
921                    self.olm_wedging_index,
922                )),
923                used_session: Box::new(session),
924                message: encrypted.cast(),
925            }),
926
927            Err(OlmError::MissingSession) => Ok(MaybeEncryptedRoomKey::MissingSession),
928            Err(e) => Err(e),
929        }
930    }
931
932    /// Update a device with a new device keys struct.
933    ///
934    /// Returns `true` if any changes were made to the data.
935    pub(crate) fn update_device(
936        &mut self,
937        device_keys: &DeviceKeys,
938    ) -> Result<bool, SignatureError> {
939        device_keys.check_self_signature()?;
940
941        if self.user_id() != device_keys.user_id || self.device_id() != device_keys.device_id {
942            Err(SignatureError::UserIdMismatch)
943        } else if self.ed25519_key() != device_keys.ed25519_key() {
944            Err(SignatureError::SigningKeyChanged(
945                self.ed25519_key().map(Box::new),
946                device_keys.ed25519_key().map(Box::new),
947            ))
948        } else if self.device_keys.as_ref() != device_keys {
949            trace!(
950                user_id = ?self.user_id(),
951                device_id = ?self.device_id(),
952                keys = ?self.keys(),
953                "Updated a device",
954            );
955
956            self.device_keys = device_keys.clone().into();
957
958            Ok(true)
959        } else {
960            // no changes needed
961            Ok(false)
962        }
963    }
964
965    /// Return the device keys
966    pub fn as_device_keys(&self) -> &DeviceKeys {
967        &self.device_keys
968    }
969
970    /// Check if the given JSON is signed by this device key.
971    ///
972    /// This method should only be used if an object's signature needs to be
973    /// checked multiple times, and you'd like to avoid performing the
974    /// canonicalization step each time.
975    ///
976    /// **Note**: Use this method with caution, the `canonical_json` needs to be
977    /// correctly canonicalized and make sure that the object you are checking
978    /// the signature for is allowed to be signed by a device.
979    pub(crate) fn has_signed_raw(
980        &self,
981        signatures: &Signatures,
982        canonical_json: &str,
983    ) -> Result<(), SignatureError> {
984        let key = self.ed25519_key().ok_or(SignatureError::MissingSigningKey)?;
985        let user_id = self.user_id();
986        let key_id = &DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, self.device_id());
987
988        key.verify_canonicalized_json(user_id, key_id, signatures, canonical_json)
989    }
990
991    pub(crate) fn verify_one_time_key(
992        &self,
993        one_time_key: &SignedKey,
994    ) -> Result<(), SignatureError> {
995        self.device_keys.has_signed(one_time_key)
996    }
997
998    /// Mark the device as deleted.
999    pub(crate) fn mark_as_deleted(&self) {
1000        self.deleted.store(true, Ordering::Relaxed);
1001    }
1002
1003    #[cfg(any(test, feature = "testing"))]
1004    #[allow(dead_code)]
1005    /// Generate the Device from a reference of an OlmMachine.
1006    pub async fn from_machine_test_helper(
1007        machine: &OlmMachine,
1008    ) -> Result<DeviceData, crate::CryptoStoreError> {
1009        Ok(DeviceData::from_account(&*machine.store().cache().await?.account().await?))
1010    }
1011
1012    /// Create [`DeviceData`] from an [`Account`].
1013    ///
1014    /// We will have our own device data in the store once we receive a
1015    /// `/keys/query` response, but this is useful to create it before we
1016    /// receive such a response.
1017    ///
1018    /// It also makes it easier to check that the server doesn't lie about our
1019    /// own device.
1020    ///
1021    /// _Don't_ use this after we received a `/keys/query` response, other
1022    /// users/devices might add signatures to our own device, which can't be
1023    /// replicated locally.
1024    pub fn from_account(account: &Account) -> DeviceData {
1025        let device_keys = account.device_keys();
1026        let mut device = DeviceData::try_from(&device_keys)
1027            .expect("Creating a device from our own account should always succeed");
1028        device.first_time_seen_ts = account.creation_local_time();
1029
1030        device
1031    }
1032
1033    /// Get the local timestamp of when this device was first persisted, in
1034    /// milliseconds since epoch (client local time).
1035    pub fn first_time_seen_ts(&self) -> MilliSecondsSinceUnixEpoch {
1036        self.first_time_seen_ts
1037    }
1038
1039    /// True if this device is an [MSC3814](https://github.com/matrix-org/matrix-spec-proposals/pull/3814) dehydrated device.
1040    pub fn is_dehydrated(&self) -> bool {
1041        self.device_keys.dehydrated.unwrap_or(false)
1042    }
1043}
1044
1045impl TryFrom<&DeviceKeys> for DeviceData {
1046    type Error = SignatureError;
1047
1048    fn try_from(device_keys: &DeviceKeys) -> Result<Self, Self::Error> {
1049        device_keys.check_self_signature()?;
1050        Ok(Self {
1051            device_keys: device_keys.clone().into(),
1052            deleted: Arc::new(AtomicBool::new(false)),
1053            trust_state: Arc::new(RwLock::new(LocalTrust::Unset)),
1054            withheld_code_sent: Arc::new(AtomicBool::new(false)),
1055            first_time_seen_ts: MilliSecondsSinceUnixEpoch::now(),
1056            olm_wedging_index: Default::default(),
1057        })
1058    }
1059}
1060
1061impl PartialEq for DeviceData {
1062    fn eq(&self, other: &Self) -> bool {
1063        self.user_id() == other.user_id() && self.device_id() == other.device_id()
1064    }
1065}
1066
1067/// Testing Facilities for Device Management
1068#[cfg(any(test, feature = "testing"))]
1069#[allow(dead_code)]
1070pub(crate) mod testing {
1071    use serde_json::json;
1072
1073    use crate::{identities::DeviceData, types::DeviceKeys};
1074
1075    /// Generate default DeviceKeys for tests
1076    pub fn device_keys() -> DeviceKeys {
1077        let device_keys = json!({
1078          "algorithms": vec![
1079              "m.olm.v1.curve25519-aes-sha2",
1080              "m.megolm.v1.aes-sha2"
1081          ],
1082          "device_id": "BNYQQWUMXO",
1083          "user_id": "@example:localhost",
1084          "keys": {
1085              "curve25519:BNYQQWUMXO": "xfgbLIC5WAl1OIkpOzoxpCe8FsRDT6nch7NQsOb15nc",
1086              "ed25519:BNYQQWUMXO": "2/5LWJMow5zhJqakV88SIc7q/1pa8fmkfgAzx72w9G4"
1087          },
1088          "signatures": {
1089              "@example:localhost": {
1090                  "ed25519:BNYQQWUMXO": "kTwMrbsLJJM/uFGOj/oqlCaRuw7i9p/6eGrTlXjo8UJMCFAetoyWzoMcF35vSe4S6FTx8RJmqX6rM7ep53MHDQ"
1091              }
1092          },
1093          "unsigned": {
1094              "device_display_name": "Alice's mobile phone"
1095          }
1096        });
1097
1098        serde_json::from_value(device_keys).unwrap()
1099    }
1100
1101    /// Generate default [`DeviceData`] for tests
1102    pub fn get_device() -> DeviceData {
1103        let device_keys = device_keys();
1104        DeviceData::try_from(&device_keys).unwrap()
1105    }
1106}
1107
1108#[cfg(test)]
1109pub(crate) mod tests {
1110    use ruma::{MilliSecondsSinceUnixEpoch, user_id};
1111    use serde_json::json;
1112    use vodozemac::{Curve25519PublicKey, Ed25519PublicKey};
1113
1114    use super::testing::{device_keys, get_device};
1115    use crate::{DeviceData, identities::LocalTrust};
1116
1117    #[test]
1118    fn create_a_device() {
1119        let now = MilliSecondsSinceUnixEpoch::now();
1120        let user_id = user_id!("@example:localhost");
1121        let device_id = "BNYQQWUMXO";
1122
1123        let device = get_device();
1124
1125        assert_eq!(user_id, device.user_id());
1126        assert_eq!(device_id, device.device_id());
1127        assert_eq!(device.algorithms().len(), 2);
1128        assert_eq!(LocalTrust::Unset, device.local_trust_state());
1129        assert_eq!("Alice's mobile phone", device.display_name().unwrap());
1130        assert_eq!(
1131            device.curve25519_key().unwrap(),
1132            Curve25519PublicKey::from_base64("xfgbLIC5WAl1OIkpOzoxpCe8FsRDT6nch7NQsOb15nc")
1133                .unwrap(),
1134        );
1135        assert_eq!(
1136            device.ed25519_key().unwrap(),
1137            Ed25519PublicKey::from_base64("2/5LWJMow5zhJqakV88SIc7q/1pa8fmkfgAzx72w9G4").unwrap(),
1138        );
1139
1140        let then = MilliSecondsSinceUnixEpoch::now();
1141
1142        assert!(device.first_time_seen_ts() >= now);
1143        assert!(device.first_time_seen_ts() <= then);
1144    }
1145
1146    #[test]
1147    fn update_a_device() {
1148        let mut device = get_device();
1149
1150        assert_eq!("Alice's mobile phone", device.display_name().unwrap());
1151
1152        let display_name = "Alice's work computer".to_owned();
1153
1154        let mut device_keys = device_keys();
1155        device_keys.unsigned.device_display_name = Some(display_name.clone());
1156        assert!(device.update_device(&device_keys).unwrap());
1157        assert_eq!(&display_name, device.display_name().as_ref().unwrap());
1158
1159        // A second call to `update_device` with the same data should return
1160        // `false`.
1161        assert!(!device.update_device(&device_keys).unwrap());
1162    }
1163
1164    #[test]
1165    #[allow(clippy::redundant_clone)]
1166    fn delete_a_device() {
1167        let device = get_device();
1168        assert!(!device.is_deleted());
1169
1170        let device_clone = device.clone();
1171
1172        device.mark_as_deleted();
1173        assert!(device.is_deleted());
1174        assert!(device_clone.is_deleted());
1175    }
1176
1177    #[test]
1178    fn deserialize_device() {
1179        let user_id = user_id!("@example:localhost");
1180        let device_id = "BNYQQWUMXO";
1181
1182        let device = json!({
1183            "inner": {
1184                "user_id": user_id,
1185                "device_id": device_id,
1186                "algorithms": ["m.olm.v1.curve25519-aes-sha2","m.megolm.v1.aes-sha2"],
1187                "keys": {
1188                    "curve25519:BNYQQWUMXO": "xfgbLIC5WAl1OIkpOzoxpCe8FsRDT6nch7NQsOb15nc",
1189                    "ed25519:BNYQQWUMXO": "2/5LWJMow5zhJqakV88SIc7q/1pa8fmkfgAzx72w9G4"
1190                },
1191                "signatures": {
1192                    "@example:localhost": {
1193                        "ed25519:BNYQQWUMXO": "kTwMrbsLJJM/uFGOj/oqlCaRuw7i9p/6eGrTlXjo8UJMCFAetoyWzoMcF35vSe4S6FTx8RJmqX6rM7ep53MHDQ"
1194                    }
1195                },
1196                "unsigned": {
1197                    "device_display_name": "Alice's mobile phone"
1198                }
1199            },
1200            "deleted": false,
1201            "trust_state": "Verified",
1202            "withheld_code_sent": false,
1203            "first_time_seen_ts": 1696931068314u64
1204        });
1205
1206        let device: DeviceData =
1207            serde_json::from_value(device).expect("We should be able to deserialize our device");
1208
1209        assert_eq!(user_id, device.user_id());
1210        assert_eq!(device_id, device.device_id());
1211        assert_eq!(device.algorithms().len(), 2);
1212        assert_eq!(LocalTrust::Verified, device.local_trust_state());
1213        assert_eq!("Alice's mobile phone", device.display_name().unwrap());
1214        assert_eq!(
1215            device.curve25519_key().unwrap(),
1216            Curve25519PublicKey::from_base64("xfgbLIC5WAl1OIkpOzoxpCe8FsRDT6nch7NQsOb15nc")
1217                .unwrap(),
1218        );
1219        assert_eq!(
1220            device.ed25519_key().unwrap(),
1221            Ed25519PublicKey::from_base64("2/5LWJMow5zhJqakV88SIc7q/1pa8fmkfgAzx72w9G4").unwrap(),
1222        );
1223    }
1224
1225    /// A device signed by its owner's self-signing key becomes trusted when the
1226    /// owner's identity is verified via X.509, even though we never
1227    /// cross-signed the identity ourselves.
1228    #[cfg(feature = "experimental-x509-identity-verification")]
1229    #[matrix_sdk_test::async_test]
1230    async fn test_x509_verified_owner_confers_device_trust() {
1231        use std::sync::Arc;
1232
1233        use ruma::device_id;
1234
1235        use crate::{
1236            machine::test_helpers::create_signed_device_of_unverified_user,
1237            olm::{Account, PrivateCrossSigningIdentity},
1238            x509::{
1239                RustRawX509Signer, RustRawX509Verifier, X509Signer, X509Verifier,
1240                tests::{ca_cert, cert_and_key_with_email_signed_by},
1241            },
1242        };
1243
1244        // Given Alice's identity is signed with an X.509 certificate chaining
1245        // to a CA...
1246        let (ca_certificate, ca_signing_key) = ca_cert();
1247        let (certificate, signing_key) =
1248            cert_and_key_with_email_signed_by("alice@hs.co", &ca_certificate, &ca_signing_key);
1249        let x509_signer = X509Signer::new(Arc::new(
1250            RustRawX509Signer::new_from_pem_data(&certificate.pem(), &signing_key.serialize_pem())
1251                .unwrap(),
1252        ));
1253        let account = Account::with_device_id(user_id!("@alice:hs.co"), device_id!("ALICEDEV"));
1254        let alice_private_identity =
1255            PrivateCrossSigningIdentity::for_account(&account, Some(&x509_signer)).await.unwrap();
1256
1257        // ...and her device is signed by her self-signing key, but we have not
1258        // cross-signed her identity ourselves.
1259        let mut device =
1260            create_signed_device_of_unverified_user(account.device_keys(), &alice_private_identity)
1261                .await;
1262        assert!(device.is_cross_signed_by_owner());
1263
1264        // Without a verifier for the CA, the device is not trusted.
1265        assert!(!device.is_cross_signing_trusted());
1266
1267        // With a verifier trusting the CA, the device is trusted.
1268        device.x509_verifier = Some(X509Verifier::new(Arc::new(
1269            RustRawX509Verifier::new_from_pem_data(&ca_certificate.pem()).unwrap(),
1270        )));
1271        assert!(device.is_cross_signing_trusted());
1272        assert!(device.is_verified());
1273
1274        // A verifier trusting a *different* CA does not trust the device.
1275        let (wrong_ca_certificate, _) = ca_cert();
1276        device.x509_verifier = Some(X509Verifier::new(Arc::new(
1277            RustRawX509Verifier::new_from_pem_data(&wrong_ca_certificate.pem()).unwrap(),
1278        )));
1279        assert!(!device.is_cross_signing_trusted());
1280    }
1281}