1use 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 used_session: Box<Session>,
68 share_info: Box<ShareInfo>,
70 message: Raw<AnyToDeviceEventContent>,
71 },
72 MissingSession,
75}
76
77#[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 #[serde(
91 default,
92 serialize_with = "atomic_bool_serializer",
93 deserialize_with = "atomic_bool_deserializer"
94 )]
95 withheld_code_sent: Arc<AtomicBool>,
96 #[serde(default = "default_timestamp")]
99 first_time_seen_ts: MilliSecondsSinceUnixEpoch,
100 #[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#[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 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 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 pub fn is_owner_of_session(
188 &self,
189 session: &InboundGroupSession,
190 ) -> Result<bool, MismatchedIdentityKeysError> {
191 if session.has_been_imported() {
192 Ok(false)
212 } else if let Some(key) =
213 session.signing_keys().get(&DeviceKeyAlgorithm::Ed25519).and_then(|k| k.ed25519())
214 {
215 let ed25519_comparison = self.ed25519_key().map(|k| k == key);
274 let curve25519_comparison = self.curve25519_key().map(|k| k == session.sender_key());
275
276 match (ed25519_comparison, curve25519_comparison) {
277 (_, Some(false)) | (Some(false), _) => Err(MismatchedIdentityKeysError {
280 key_ed25519: key.into(),
281 device_ed25519: self.ed25519_key().map(Into::into),
282 key_curve25519: session.sender_key().into(),
283 device_curve25519: self.curve25519_key().map(Into::into),
284 }),
285 (Some(true), Some(true)) => Ok(true),
287 _ => Ok(false),
290 }
291 } else {
292 Ok(false)
293 }
294 }
295
296 pub fn is_cross_signed_by_owner(&self) -> bool {
298 self.device_owner_identity
299 .as_ref()
300 .is_some_and(|owner_identity| self.inner.is_cross_signed_by_owner(owner_identity))
301 }
302
303 pub fn is_device_owner_verified(&self) -> bool {
305 self.device_owner_identity.as_ref().is_some_and(|id| match id {
306 UserIdentityData::Own(own_identity) => own_identity.is_verified(),
307 UserIdentityData::Other(other_identity) => other_identity.is_verified(
308 self.own_identity.as_ref(),
309 #[cfg(feature = "experimental-x509-identity-verification")]
310 self.x509_verifier.as_ref(),
311 ),
312 })
313 }
314
315 pub fn request_verification(&self) -> (VerificationRequest, OutgoingVerificationRequest) {
320 self.request_verification_helper(None)
321 }
322
323 pub fn request_verification_with_methods(
332 &self,
333 methods: Vec<VerificationMethod>,
334 ) -> (VerificationRequest, OutgoingVerificationRequest) {
335 self.request_verification_helper(Some(methods))
336 }
337
338 fn request_verification_helper(
339 &self,
340 methods: Option<Vec<VerificationMethod>>,
341 ) -> (VerificationRequest, OutgoingVerificationRequest) {
342 self.verification_machine.request_to_device_verification(
343 self.user_id(),
344 vec![self.device_id().to_owned()],
345 methods,
346 )
347 }
348
349 pub(crate) async fn get_most_recent_session(&self) -> OlmResult<Option<Session>> {
351 self.inner.get_most_recent_session(self.verification_machine.store.inner()).await
352 }
353
354 pub fn is_verified(&self) -> bool {
362 self.inner.is_verified(
363 &self.own_identity,
364 &self.device_owner_identity,
365 #[cfg(feature = "experimental-x509-identity-verification")]
366 self.x509_verifier.as_ref(),
367 )
368 }
369
370 pub fn is_cross_signing_trusted(&self) -> bool {
372 self.inner.is_cross_signing_trusted(
373 &self.own_identity,
374 &self.device_owner_identity,
375 #[cfg(feature = "experimental-x509-identity-verification")]
376 self.x509_verifier.as_ref(),
377 )
378 }
379
380 pub async fn verify(&self) -> Result<SignatureUploadRequest, SignatureError> {
394 if self.user_id() == self.verification_machine.own_user_id() {
395 Ok(self
396 .verification_machine
397 .store
398 .private_identity
399 .lock()
400 .await
401 .sign_device(&self.inner)
402 .await?)
403 } else {
404 Err(SignatureError::UserIdMismatch)
405 }
406 }
407
408 pub async fn set_local_trust(&self, trust_state: LocalTrust) -> StoreResult<()> {
417 self.inner.set_trust_state(trust_state);
418
419 let changes = Changes {
420 devices: DeviceChanges { changed: vec![self.inner.clone()], ..Default::default() },
421 ..Default::default()
422 };
423
424 self.verification_machine.store.save_changes(changes).await
425 }
426
427 pub(crate) async fn encrypt(
444 &self,
445 event_type: &str,
446 content: impl Serialize,
447 ) -> OlmResult<(Session, Raw<ToDeviceEncryptedEventContent>, String)> {
448 self.inner.encrypt(self.verification_machine.store.inner(), event_type, content).await
449 }
450
451 pub async fn encrypt_room_key_for_forwarding(
454 &self,
455 session: InboundGroupSession,
456 message_index: Option<u32>,
457 ) -> OlmResult<(Session, Raw<ToDeviceEncryptedEventContent>)> {
458 let content: ForwardedRoomKeyContent = {
459 let export = if let Some(index) = message_index {
460 session.export_at_index(index).await
461 } else {
462 session.export().await
463 };
464
465 export.try_into()?
466 };
467
468 let event_type = content.event_type().to_owned();
469
470 self.encrypt(&event_type, content)
471 .await
472 .map(|(session, message, _message_id)| (session, message))
473 }
474
475 pub async fn encrypt_event_raw(
502 &self,
503 event_type: &str,
504 content: &Value,
505 share_strategy: CollectStrategy,
506 ) -> OlmResult<Raw<ToDeviceEncryptedEventContent>> {
507 if let Some(withheld_code) = withheld_code_for_device_for_share_strategy(
508 &self.inner,
509 share_strategy,
510 &self.own_identity,
511 &self.device_owner_identity,
512 #[cfg(feature = "experimental-x509-identity-verification")]
513 self.x509_verifier.as_ref(),
514 )
515 .await?
516 {
517 return Err(OlmError::Withheld(withheld_code));
518 }
519
520 let (used_session, raw_encrypted, _message_id) = self.encrypt(event_type, content).await?;
521
522 self.verification_machine
524 .store
525 .save_changes(Changes { sessions: vec![used_session], ..Default::default() })
526 .await?;
527
528 Ok(raw_encrypted)
529 }
530
531 pub fn is_dehydrated(&self) -> bool {
533 self.inner.is_dehydrated()
534 }
535}
536
537#[derive(Debug)]
539pub struct UserDevices {
540 pub(crate) inner: HashMap<OwnedDeviceId, DeviceData>,
541 pub(crate) verification_machine: VerificationMachine,
542 pub(crate) own_identity: Option<OwnUserIdentityData>,
543 pub(crate) device_owner_identity: Option<UserIdentityData>,
544 #[cfg(feature = "experimental-x509-identity-verification")]
545 pub(crate) x509_verifier: Option<X509Verifier>,
546}
547
548impl UserDevices {
549 pub fn get(&self, device_id: &DeviceId) -> Option<Device> {
551 self.inner.get(device_id).map(|d| Device {
552 inner: d.clone(),
553 verification_machine: self.verification_machine.clone(),
554 own_identity: self.own_identity.clone(),
555 device_owner_identity: self.device_owner_identity.clone(),
556 #[cfg(feature = "experimental-x509-identity-verification")]
557 x509_verifier: self.x509_verifier.clone(),
558 })
559 }
560
561 fn own_user_id(&self) -> &UserId {
562 self.verification_machine.own_user_id()
563 }
564
565 fn own_device_id(&self) -> &DeviceId {
566 self.verification_machine.own_device_id()
567 }
568
569 pub fn is_any_verified(&self) -> bool {
575 self.inner
576 .values()
577 .filter(|d| {
578 !(d.user_id() == self.own_user_id() && d.device_id() == self.own_device_id())
579 })
580 .any(|d| {
581 d.is_verified(
582 &self.own_identity,
583 &self.device_owner_identity,
584 #[cfg(feature = "experimental-x509-identity-verification")]
585 self.x509_verifier.as_ref(),
586 )
587 })
588 }
589
590 pub fn keys(&self) -> impl Iterator<Item = &DeviceId> {
592 self.inner.keys().map(Deref::deref)
593 }
594
595 pub fn devices(&self) -> impl Iterator<Item = Device> + '_ {
597 self.inner.values().map(move |d| Device {
598 inner: d.clone(),
599 verification_machine: self.verification_machine.clone(),
600 own_identity: self.own_identity.clone(),
601 device_owner_identity: self.device_owner_identity.clone(),
602 #[cfg(feature = "experimental-x509-identity-verification")]
603 x509_verifier: self.x509_verifier.clone(),
604 })
605 }
606}
607
608#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
610#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
611pub enum LocalTrust {
612 Verified = 0,
614 BlackListed = 1,
616 Ignored = 2,
618 Unset = 3,
620}
621
622impl From<i64> for LocalTrust {
623 fn from(state: i64) -> Self {
624 match state {
625 0 => LocalTrust::Verified,
626 1 => LocalTrust::BlackListed,
627 2 => LocalTrust::Ignored,
628 3 => LocalTrust::Unset,
629 _ => LocalTrust::Unset,
630 }
631 }
632}
633
634impl DeviceData {
635 pub fn new(device_keys: DeviceKeys, trust_state: LocalTrust) -> Self {
639 Self {
640 device_keys: device_keys.into(),
641 trust_state: Arc::new(RwLock::new(trust_state)),
642 deleted: Arc::new(AtomicBool::new(false)),
643 withheld_code_sent: Arc::new(AtomicBool::new(false)),
644 first_time_seen_ts: MilliSecondsSinceUnixEpoch::now(),
645 olm_wedging_index: Default::default(),
646 }
647 }
648
649 pub fn user_id(&self) -> &UserId {
651 &self.device_keys.user_id
652 }
653
654 pub fn device_id(&self) -> &DeviceId {
656 &self.device_keys.device_id
657 }
658
659 pub fn display_name(&self) -> Option<&str> {
661 self.device_keys.unsigned.device_display_name.as_deref()
662 }
663
664 pub fn get_key(&self, algorithm: DeviceKeyAlgorithm) -> Option<&DeviceKey> {
666 self.device_keys.get_key(algorithm)
667 }
668
669 pub fn curve25519_key(&self) -> Option<Curve25519PublicKey> {
671 self.device_keys.curve25519_key()
672 }
673
674 pub fn ed25519_key(&self) -> Option<Ed25519PublicKey> {
676 self.device_keys.ed25519_key()
677 }
678
679 pub fn keys(&self) -> &BTreeMap<OwnedDeviceKeyId, DeviceKey> {
681 &self.device_keys.keys
682 }
683
684 pub fn signatures(&self) -> &Signatures {
686 &self.device_keys.signatures
687 }
688
689 pub fn local_trust_state(&self) -> LocalTrust {
691 *self.trust_state.read()
692 }
693
694 pub fn is_locally_trusted(&self) -> bool {
696 self.local_trust_state() == LocalTrust::Verified
697 }
698
699 pub fn is_blacklisted(&self) -> bool {
703 self.local_trust_state() == LocalTrust::BlackListed
704 }
705
706 pub(crate) fn set_trust_state(&self, state: LocalTrust) {
711 *self.trust_state.write() = state;
712 }
713
714 pub(crate) fn mark_withheld_code_as_sent(&self) {
715 self.withheld_code_sent.store(true, Ordering::Relaxed)
716 }
717
718 pub fn was_withheld_code_sent(&self) -> bool {
721 self.withheld_code_sent.load(Ordering::Relaxed)
722 }
723
724 pub fn algorithms(&self) -> &[EventEncryptionAlgorithm] {
726 &self.device_keys.algorithms
727 }
728
729 pub fn supports_olm(&self) -> bool {
731 #[cfg(feature = "experimental-algorithms")]
732 {
733 self.algorithms().contains(&EventEncryptionAlgorithm::OlmV1Curve25519AesSha2)
734 || self.algorithms().contains(&EventEncryptionAlgorithm::OlmV2Curve25519AesSha2)
735 }
736
737 #[cfg(not(feature = "experimental-algorithms"))]
738 {
739 self.algorithms().contains(&EventEncryptionAlgorithm::OlmV1Curve25519AesSha2)
740 }
741 }
742
743 pub(crate) async fn get_most_recent_session(
746 &self,
747 store: &CryptoStoreWrapper,
748 ) -> OlmResult<Option<Session>> {
749 if let Some(sender_key) = self.curve25519_key() {
750 if let Some(sessions) = store.get_sessions(&sender_key.to_base64()).await? {
751 let mut sessions = sessions.lock().await;
752 sessions.sort_by_key(|s| s.creation_time);
753
754 Ok(sessions.last().cloned())
755 } else {
756 Ok(None)
757 }
758 } else {
759 Ok(None)
760 }
761 }
762
763 #[cfg(feature = "experimental-algorithms")]
766 pub fn supports_olm_v2(&self) -> bool {
767 self.algorithms().contains(&EventEncryptionAlgorithm::OlmV2Curve25519AesSha2)
768 }
769
770 pub fn olm_session_config(&self) -> SessionConfig {
772 #[cfg(feature = "experimental-algorithms")]
773 if self.supports_olm_v2() {
774 SessionConfig::version_2()
775 } else {
776 SessionConfig::version_1()
777 }
778
779 #[cfg(not(feature = "experimental-algorithms"))]
780 SessionConfig::version_1()
781 }
782
783 pub fn is_deleted(&self) -> bool {
785 self.deleted.load(Ordering::Relaxed)
786 }
787
788 pub(crate) fn is_verified(
789 &self,
790 own_identity: &Option<OwnUserIdentityData>,
791 device_owner: &Option<UserIdentityData>,
792 #[cfg(feature = "experimental-x509-identity-verification")] x509_verifier: Option<
793 &X509Verifier,
794 >,
795 ) -> bool {
796 self.is_locally_trusted()
797 || self.is_cross_signing_trusted(
798 own_identity,
799 device_owner,
800 #[cfg(feature = "experimental-x509-identity-verification")]
801 x509_verifier,
802 )
803 }
804
805 pub(crate) fn is_cross_signing_trusted(
806 &self,
807 own_identity: &Option<OwnUserIdentityData>,
808 device_owner: &Option<UserIdentityData>,
809 #[cfg(feature = "experimental-x509-identity-verification")] x509_verifier: Option<
810 &X509Verifier,
811 >,
812 ) -> bool {
813 device_owner.as_ref().is_some_and(|device_identity| match device_identity {
814 UserIdentityData::Own(_) => own_identity.as_ref().is_some_and(|own_identity| {
815 own_identity.is_verified() && own_identity.is_device_signed(self)
816 }),
817
818 UserIdentityData::Other(device_identity) => {
823 device_identity.is_verified(
824 own_identity.as_ref(),
825 #[cfg(feature = "experimental-x509-identity-verification")]
826 x509_verifier,
827 ) && device_identity.is_device_signed(self)
828 }
829 })
830 }
831
832 pub(crate) fn is_cross_signed_by_owner(
833 &self,
834 device_owner_identity: &UserIdentityData,
835 ) -> bool {
836 match device_owner_identity {
837 UserIdentityData::Own(identity) => identity.is_device_signed(self),
840 UserIdentityData::Other(device_identity) => device_identity.is_device_signed(self),
843 }
844 }
845
846 #[instrument(
865 skip_all,
866 fields(
867 recipient = ?self.user_id(),
868 recipient_device = ?self.device_id(),
869 recipient_key = ?self.curve25519_key(),
870 event_type,
871 message_id,
872 ))
873 ]
874 pub(crate) async fn encrypt(
875 &self,
876 store: &CryptoStoreWrapper,
877 event_type: &str,
878 content: impl Serialize,
879 ) -> OlmResult<(Session, Raw<ToDeviceEncryptedEventContent>, String)> {
880 #[cfg(not(target_family = "wasm"))]
881 let message_id = ulid::Ulid::generate().to_string();
882 #[cfg(target_family = "wasm")]
883 let message_id = ruma::TransactionId::new().to_string();
884
885 tracing::Span::current().record("message_id", &message_id);
886
887 let session = self.get_most_recent_session(store).await?;
888
889 if let Some(mut session) = session {
890 let message =
891 session.encrypt(self, event_type, content, Some(message_id.clone())).await?;
892
893 Ok((session, message, message_id))
894 } else {
895 trace!("Trying to encrypt an event for a device, but no Olm session is found.");
896 Err(OlmError::MissingSession)
897 }
898 }
899
900 pub(crate) async fn maybe_encrypt_room_key(
901 &self,
902 store: &CryptoStoreWrapper,
903 session: OutboundGroupSession,
904 ) -> OlmResult<MaybeEncryptedRoomKey> {
905 let content = session.as_content().await;
906 let message_index = session.message_index().await;
907 let event_type = content.event_type().to_owned();
908
909 match self.encrypt(store, &event_type, content).await {
910 Ok((session, encrypted, _)) => Ok(MaybeEncryptedRoomKey::Encrypted {
911 share_info: Box::new(ShareInfo::new_shared(
912 session.sender_key().to_owned(),
913 message_index,
914 self.olm_wedging_index,
915 )),
916 used_session: Box::new(session),
917 message: encrypted.cast(),
918 }),
919
920 Err(OlmError::MissingSession) => Ok(MaybeEncryptedRoomKey::MissingSession),
921 Err(e) => Err(e),
922 }
923 }
924
925 pub(crate) fn update_device(
929 &mut self,
930 device_keys: &DeviceKeys,
931 ) -> Result<bool, SignatureError> {
932 device_keys.check_self_signature()?;
933
934 if self.user_id() != device_keys.user_id || self.device_id() != device_keys.device_id {
935 Err(SignatureError::UserIdMismatch)
936 } else if self.ed25519_key() != device_keys.ed25519_key() {
937 Err(SignatureError::SigningKeyChanged(
938 self.ed25519_key().map(Box::new),
939 device_keys.ed25519_key().map(Box::new),
940 ))
941 } else if self.device_keys.as_ref() != device_keys {
942 trace!(
943 user_id = ?self.user_id(),
944 device_id = ?self.device_id(),
945 keys = ?self.keys(),
946 "Updated a device",
947 );
948
949 self.device_keys = device_keys.clone().into();
950
951 Ok(true)
952 } else {
953 Ok(false)
955 }
956 }
957
958 pub fn as_device_keys(&self) -> &DeviceKeys {
960 &self.device_keys
961 }
962
963 pub(crate) fn has_signed_raw(
973 &self,
974 signatures: &Signatures,
975 canonical_json: &str,
976 ) -> Result<(), SignatureError> {
977 let key = self.ed25519_key().ok_or(SignatureError::MissingSigningKey)?;
978 let user_id = self.user_id();
979 let key_id = &DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, self.device_id());
980
981 key.verify_canonicalized_json(user_id, key_id, signatures, canonical_json)
982 }
983
984 pub(crate) fn verify_one_time_key(
985 &self,
986 one_time_key: &SignedKey,
987 ) -> Result<(), SignatureError> {
988 self.device_keys.has_signed(one_time_key)
989 }
990
991 pub(crate) fn mark_as_deleted(&self) {
993 self.deleted.store(true, Ordering::Relaxed);
994 }
995
996 #[cfg(any(test, feature = "testing"))]
997 #[allow(dead_code)]
998 pub async fn from_machine_test_helper(
1000 machine: &OlmMachine,
1001 ) -> Result<DeviceData, crate::CryptoStoreError> {
1002 Ok(DeviceData::from_account(&*machine.store().cache().await?.account().await?))
1003 }
1004
1005 pub fn from_account(account: &Account) -> DeviceData {
1018 let device_keys = account.device_keys();
1019 let mut device = DeviceData::try_from(&device_keys)
1020 .expect("Creating a device from our own account should always succeed");
1021 device.first_time_seen_ts = account.creation_local_time();
1022
1023 device
1024 }
1025
1026 pub fn first_time_seen_ts(&self) -> MilliSecondsSinceUnixEpoch {
1029 self.first_time_seen_ts
1030 }
1031
1032 pub fn is_dehydrated(&self) -> bool {
1034 self.device_keys.dehydrated.unwrap_or(false)
1035 }
1036}
1037
1038impl TryFrom<&DeviceKeys> for DeviceData {
1039 type Error = SignatureError;
1040
1041 fn try_from(device_keys: &DeviceKeys) -> Result<Self, Self::Error> {
1042 device_keys.check_self_signature()?;
1043 Ok(Self {
1044 device_keys: device_keys.clone().into(),
1045 deleted: Arc::new(AtomicBool::new(false)),
1046 trust_state: Arc::new(RwLock::new(LocalTrust::Unset)),
1047 withheld_code_sent: Arc::new(AtomicBool::new(false)),
1048 first_time_seen_ts: MilliSecondsSinceUnixEpoch::now(),
1049 olm_wedging_index: Default::default(),
1050 })
1051 }
1052}
1053
1054impl PartialEq for DeviceData {
1055 fn eq(&self, other: &Self) -> bool {
1056 self.user_id() == other.user_id() && self.device_id() == other.device_id()
1057 }
1058}
1059
1060#[cfg(any(test, feature = "testing"))]
1062#[allow(dead_code)]
1063pub(crate) mod testing {
1064 use serde_json::json;
1065
1066 use crate::{identities::DeviceData, types::DeviceKeys};
1067
1068 pub fn device_keys() -> DeviceKeys {
1070 let device_keys = json!({
1071 "algorithms": vec![
1072 "m.olm.v1.curve25519-aes-sha2",
1073 "m.megolm.v1.aes-sha2"
1074 ],
1075 "device_id": "BNYQQWUMXO",
1076 "user_id": "@example:localhost",
1077 "keys": {
1078 "curve25519:BNYQQWUMXO": "xfgbLIC5WAl1OIkpOzoxpCe8FsRDT6nch7NQsOb15nc",
1079 "ed25519:BNYQQWUMXO": "2/5LWJMow5zhJqakV88SIc7q/1pa8fmkfgAzx72w9G4"
1080 },
1081 "signatures": {
1082 "@example:localhost": {
1083 "ed25519:BNYQQWUMXO": "kTwMrbsLJJM/uFGOj/oqlCaRuw7i9p/6eGrTlXjo8UJMCFAetoyWzoMcF35vSe4S6FTx8RJmqX6rM7ep53MHDQ"
1084 }
1085 },
1086 "unsigned": {
1087 "device_display_name": "Alice's mobile phone"
1088 }
1089 });
1090
1091 serde_json::from_value(device_keys).unwrap()
1092 }
1093
1094 pub fn get_device() -> DeviceData {
1096 let device_keys = device_keys();
1097 DeviceData::try_from(&device_keys).unwrap()
1098 }
1099}
1100
1101#[cfg(test)]
1102pub(crate) mod tests {
1103 use ruma::{MilliSecondsSinceUnixEpoch, user_id};
1104 use serde_json::json;
1105 use vodozemac::{Curve25519PublicKey, Ed25519PublicKey};
1106
1107 use super::testing::{device_keys, get_device};
1108 use crate::{DeviceData, identities::LocalTrust};
1109
1110 #[test]
1111 fn create_a_device() {
1112 let now = MilliSecondsSinceUnixEpoch::now();
1113 let user_id = user_id!("@example:localhost");
1114 let device_id = "BNYQQWUMXO";
1115
1116 let device = get_device();
1117
1118 assert_eq!(user_id, device.user_id());
1119 assert_eq!(device_id, device.device_id());
1120 assert_eq!(device.algorithms().len(), 2);
1121 assert_eq!(LocalTrust::Unset, device.local_trust_state());
1122 assert_eq!("Alice's mobile phone", device.display_name().unwrap());
1123 assert_eq!(
1124 device.curve25519_key().unwrap(),
1125 Curve25519PublicKey::from_base64("xfgbLIC5WAl1OIkpOzoxpCe8FsRDT6nch7NQsOb15nc")
1126 .unwrap(),
1127 );
1128 assert_eq!(
1129 device.ed25519_key().unwrap(),
1130 Ed25519PublicKey::from_base64("2/5LWJMow5zhJqakV88SIc7q/1pa8fmkfgAzx72w9G4").unwrap(),
1131 );
1132
1133 let then = MilliSecondsSinceUnixEpoch::now();
1134
1135 assert!(device.first_time_seen_ts() >= now);
1136 assert!(device.first_time_seen_ts() <= then);
1137 }
1138
1139 #[test]
1140 fn update_a_device() {
1141 let mut device = get_device();
1142
1143 assert_eq!("Alice's mobile phone", device.display_name().unwrap());
1144
1145 let display_name = "Alice's work computer".to_owned();
1146
1147 let mut device_keys = device_keys();
1148 device_keys.unsigned.device_display_name = Some(display_name.clone());
1149 assert!(device.update_device(&device_keys).unwrap());
1150 assert_eq!(&display_name, device.display_name().as_ref().unwrap());
1151
1152 assert!(!device.update_device(&device_keys).unwrap());
1154 }
1155
1156 #[test]
1157 #[allow(clippy::redundant_clone)]
1158 fn delete_a_device() {
1159 let device = get_device();
1160 assert!(!device.is_deleted());
1161
1162 let device_clone = device.clone();
1163
1164 device.mark_as_deleted();
1165 assert!(device.is_deleted());
1166 assert!(device_clone.is_deleted());
1167 }
1168
1169 #[test]
1170 fn deserialize_device() {
1171 let user_id = user_id!("@example:localhost");
1172 let device_id = "BNYQQWUMXO";
1173
1174 let device = json!({
1175 "inner": {
1176 "user_id": user_id,
1177 "device_id": device_id,
1178 "algorithms": ["m.olm.v1.curve25519-aes-sha2","m.megolm.v1.aes-sha2"],
1179 "keys": {
1180 "curve25519:BNYQQWUMXO": "xfgbLIC5WAl1OIkpOzoxpCe8FsRDT6nch7NQsOb15nc",
1181 "ed25519:BNYQQWUMXO": "2/5LWJMow5zhJqakV88SIc7q/1pa8fmkfgAzx72w9G4"
1182 },
1183 "signatures": {
1184 "@example:localhost": {
1185 "ed25519:BNYQQWUMXO": "kTwMrbsLJJM/uFGOj/oqlCaRuw7i9p/6eGrTlXjo8UJMCFAetoyWzoMcF35vSe4S6FTx8RJmqX6rM7ep53MHDQ"
1186 }
1187 },
1188 "unsigned": {
1189 "device_display_name": "Alice's mobile phone"
1190 }
1191 },
1192 "deleted": false,
1193 "trust_state": "Verified",
1194 "withheld_code_sent": false,
1195 "first_time_seen_ts": 1696931068314u64
1196 });
1197
1198 let device: DeviceData =
1199 serde_json::from_value(device).expect("We should be able to deserialize our device");
1200
1201 assert_eq!(user_id, device.user_id());
1202 assert_eq!(device_id, device.device_id());
1203 assert_eq!(device.algorithms().len(), 2);
1204 assert_eq!(LocalTrust::Verified, device.local_trust_state());
1205 assert_eq!("Alice's mobile phone", device.display_name().unwrap());
1206 assert_eq!(
1207 device.curve25519_key().unwrap(),
1208 Curve25519PublicKey::from_base64("xfgbLIC5WAl1OIkpOzoxpCe8FsRDT6nch7NQsOb15nc")
1209 .unwrap(),
1210 );
1211 assert_eq!(
1212 device.ed25519_key().unwrap(),
1213 Ed25519PublicKey::from_base64("2/5LWJMow5zhJqakV88SIc7q/1pa8fmkfgAzx72w9G4").unwrap(),
1214 );
1215 }
1216
1217 #[cfg(feature = "experimental-x509-identity-verification")]
1221 #[matrix_sdk_test::async_test]
1222 async fn test_x509_verified_owner_confers_device_trust() {
1223 use std::sync::Arc;
1224
1225 use ruma::device_id;
1226
1227 use crate::{
1228 machine::test_helpers::create_signed_device_of_unverified_user,
1229 olm::{Account, PrivateCrossSigningIdentity},
1230 x509::{
1231 RustRawX509Signer, RustRawX509Verifier, X509Signer, X509Verifier,
1232 tests::{ca_cert, cert_and_key_with_email_signed_by},
1233 },
1234 };
1235
1236 let (ca_certificate, ca_signing_key) = ca_cert();
1239 let (certificate, signing_key) =
1240 cert_and_key_with_email_signed_by("alice@hs.co", &ca_certificate, &ca_signing_key);
1241 let x509_signer = X509Signer::new(Arc::new(
1242 RustRawX509Signer::new_from_pem_data(&certificate.pem(), &signing_key.serialize_pem())
1243 .unwrap(),
1244 ));
1245 let account = Account::with_device_id(user_id!("@alice:hs.co"), device_id!("ALICEDEV"));
1246 let alice_private_identity =
1247 PrivateCrossSigningIdentity::for_account(&account, Some(&x509_signer)).await.unwrap();
1248
1249 let mut device =
1252 create_signed_device_of_unverified_user(account.device_keys(), &alice_private_identity)
1253 .await;
1254 assert!(device.is_cross_signed_by_owner());
1255
1256 assert!(!device.is_cross_signing_trusted());
1258
1259 device.x509_verifier = Some(X509Verifier::new(Arc::new(
1261 RustRawX509Verifier::new_from_pem_data(&ca_certificate.pem()).unwrap(),
1262 )));
1263 assert!(device.is_cross_signing_trusted());
1264 assert!(device.is_verified());
1265
1266 let (wrong_ca_certificate, _) = ca_cert();
1268 device.x509_verifier = Some(X509Verifier::new(Arc::new(
1269 RustRawX509Verifier::new_from_pem_data(&wrong_ca_certificate.pem()).unwrap(),
1270 )));
1271 assert!(!device.is_cross_signing_trusted());
1272 }
1273}