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)
213 } else if let Some(key) =
214 session.signing_keys().get(&DeviceKeyAlgorithm::Ed25519).and_then(|k| k.ed25519())
215 {
216 let ed25519_comparison = self.ed25519_key().map(|k| k == key);
281 let curve25519_comparison = self.curve25519_key().map(|k| k == session.sender_key());
282
283 match (ed25519_comparison, curve25519_comparison) {
284 (_, Some(false)) | (Some(false), _) => Err(MismatchedIdentityKeysError {
287 key_ed25519: key.into(),
288 device_ed25519: self.ed25519_key().map(Into::into),
289 key_curve25519: session.sender_key().into(),
290 device_curve25519: self.curve25519_key().map(Into::into),
291 }),
292 (Some(true), Some(true)) => Ok(true),
294 _ => Ok(false),
298 }
299 } else {
300 Ok(false)
301 }
302 }
303
304 pub fn is_cross_signed_by_owner(&self) -> bool {
306 self.device_owner_identity
307 .as_ref()
308 .is_some_and(|owner_identity| self.inner.is_cross_signed_by_owner(owner_identity))
309 }
310
311 pub fn is_device_owner_verified(&self) -> bool {
313 self.device_owner_identity.as_ref().is_some_and(|id| match id {
314 UserIdentityData::Own(own_identity) => own_identity.is_verified(),
315 UserIdentityData::Other(other_identity) => other_identity.is_verified(
316 self.own_identity.as_ref(),
317 #[cfg(feature = "experimental-x509-identity-verification")]
318 self.x509_verifier.as_ref(),
319 ),
320 })
321 }
322
323 pub fn request_verification(&self) -> (VerificationRequest, OutgoingVerificationRequest) {
328 self.request_verification_helper(None)
329 }
330
331 pub fn request_verification_with_methods(
340 &self,
341 methods: Vec<VerificationMethod>,
342 ) -> (VerificationRequest, OutgoingVerificationRequest) {
343 self.request_verification_helper(Some(methods))
344 }
345
346 fn request_verification_helper(
347 &self,
348 methods: Option<Vec<VerificationMethod>>,
349 ) -> (VerificationRequest, OutgoingVerificationRequest) {
350 self.verification_machine.request_to_device_verification(
351 self.user_id(),
352 vec![self.device_id().to_owned()],
353 methods,
354 )
355 }
356
357 pub(crate) async fn get_most_recent_session(&self) -> OlmResult<Option<Session>> {
359 self.inner.get_most_recent_session(self.verification_machine.store.inner()).await
360 }
361
362 pub fn is_verified(&self) -> bool {
370 self.inner.is_verified(
371 &self.own_identity,
372 &self.device_owner_identity,
373 #[cfg(feature = "experimental-x509-identity-verification")]
374 self.x509_verifier.as_ref(),
375 )
376 }
377
378 pub fn is_cross_signing_trusted(&self) -> bool {
380 self.inner.is_cross_signing_trusted(
381 &self.own_identity,
382 &self.device_owner_identity,
383 #[cfg(feature = "experimental-x509-identity-verification")]
384 self.x509_verifier.as_ref(),
385 )
386 }
387
388 pub async fn verify(&self) -> Result<SignatureUploadRequest, SignatureError> {
402 if self.user_id() == self.verification_machine.own_user_id() {
403 Ok(self
404 .verification_machine
405 .store
406 .private_identity
407 .lock()
408 .await
409 .sign_device(&self.inner)
410 .await?)
411 } else {
412 Err(SignatureError::UserIdMismatch)
413 }
414 }
415
416 pub async fn set_local_trust(&self, trust_state: LocalTrust) -> StoreResult<()> {
425 self.inner.set_trust_state(trust_state);
426
427 let changes = Changes {
428 devices: DeviceChanges { changed: vec![self.inner.clone()], ..Default::default() },
429 ..Default::default()
430 };
431
432 self.verification_machine.store.save_changes(changes).await
433 }
434
435 pub(crate) async fn encrypt(
452 &self,
453 event_type: &str,
454 content: impl Serialize,
455 ) -> OlmResult<(Session, Raw<ToDeviceEncryptedEventContent>, String)> {
456 self.inner.encrypt(self.verification_machine.store.inner(), event_type, content).await
457 }
458
459 pub async fn encrypt_room_key_for_forwarding(
462 &self,
463 session: InboundGroupSession,
464 message_index: Option<u32>,
465 ) -> OlmResult<(Session, Raw<ToDeviceEncryptedEventContent>)> {
466 let content: ForwardedRoomKeyContent = {
467 let export = if let Some(index) = message_index {
468 session.export_at_index(index).await
469 } else {
470 session.export().await
471 };
472
473 export.try_into()?
474 };
475
476 let event_type = content.event_type().to_owned();
477
478 self.encrypt(&event_type, content)
479 .await
480 .map(|(session, message, _message_id)| (session, message))
481 }
482
483 pub async fn encrypt_event_raw(
511 &self,
512 event_type: &str,
513 content: &Value,
514 share_strategy: CollectStrategy,
515 ) -> OlmResult<Raw<ToDeviceEncryptedEventContent>> {
516 if let Some(withheld_code) = withheld_code_for_device_for_share_strategy(
517 &self.inner,
518 share_strategy,
519 &self.own_identity,
520 &self.device_owner_identity,
521 #[cfg(feature = "experimental-x509-identity-verification")]
522 self.x509_verifier.as_ref(),
523 )
524 .await?
525 {
526 return Err(OlmError::Withheld(withheld_code));
527 }
528
529 let (used_session, raw_encrypted, _message_id) = self.encrypt(event_type, content).await?;
530
531 self.verification_machine
533 .store
534 .save_changes(Changes { sessions: vec![used_session], ..Default::default() })
535 .await?;
536
537 Ok(raw_encrypted)
538 }
539
540 pub fn is_dehydrated(&self) -> bool {
542 self.inner.is_dehydrated()
543 }
544}
545
546#[derive(Debug)]
548pub struct UserDevices {
549 pub(crate) inner: HashMap<OwnedDeviceId, DeviceData>,
550 pub(crate) verification_machine: VerificationMachine,
551 pub(crate) own_identity: Option<OwnUserIdentityData>,
552 pub(crate) device_owner_identity: Option<UserIdentityData>,
553 #[cfg(feature = "experimental-x509-identity-verification")]
554 pub(crate) x509_verifier: Option<X509Verifier>,
555}
556
557impl UserDevices {
558 pub fn get(&self, device_id: &DeviceId) -> Option<Device> {
560 self.inner.get(device_id).map(|d| Device {
561 inner: d.clone(),
562 verification_machine: self.verification_machine.clone(),
563 own_identity: self.own_identity.clone(),
564 device_owner_identity: self.device_owner_identity.clone(),
565 #[cfg(feature = "experimental-x509-identity-verification")]
566 x509_verifier: self.x509_verifier.clone(),
567 })
568 }
569
570 fn own_user_id(&self) -> &UserId {
571 self.verification_machine.own_user_id()
572 }
573
574 fn own_device_id(&self) -> &DeviceId {
575 self.verification_machine.own_device_id()
576 }
577
578 pub fn is_any_verified(&self) -> bool {
584 self.inner
585 .values()
586 .filter(|d| {
587 !(d.user_id() == self.own_user_id() && d.device_id() == self.own_device_id())
588 })
589 .any(|d| {
590 d.is_verified(
591 &self.own_identity,
592 &self.device_owner_identity,
593 #[cfg(feature = "experimental-x509-identity-verification")]
594 self.x509_verifier.as_ref(),
595 )
596 })
597 }
598
599 pub fn keys(&self) -> impl Iterator<Item = &DeviceId> {
601 self.inner.keys().map(Deref::deref)
602 }
603
604 pub fn devices(&self) -> impl Iterator<Item = Device> + '_ {
606 self.inner.values().map(move |d| Device {
607 inner: d.clone(),
608 verification_machine: self.verification_machine.clone(),
609 own_identity: self.own_identity.clone(),
610 device_owner_identity: self.device_owner_identity.clone(),
611 #[cfg(feature = "experimental-x509-identity-verification")]
612 x509_verifier: self.x509_verifier.clone(),
613 })
614 }
615}
616
617#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
619#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
620pub enum LocalTrust {
621 Verified = 0,
623 BlackListed = 1,
625 Ignored = 2,
627 Unset = 3,
629}
630
631impl From<i64> for LocalTrust {
632 fn from(state: i64) -> Self {
633 match state {
634 0 => LocalTrust::Verified,
635 1 => LocalTrust::BlackListed,
636 2 => LocalTrust::Ignored,
637 3 => LocalTrust::Unset,
638 _ => LocalTrust::Unset,
639 }
640 }
641}
642
643impl DeviceData {
644 pub fn new(device_keys: DeviceKeys, trust_state: LocalTrust) -> Self {
648 Self {
649 device_keys: device_keys.into(),
650 trust_state: Arc::new(RwLock::new(trust_state)),
651 deleted: Arc::new(AtomicBool::new(false)),
652 withheld_code_sent: Arc::new(AtomicBool::new(false)),
653 first_time_seen_ts: MilliSecondsSinceUnixEpoch::now(),
654 olm_wedging_index: Default::default(),
655 }
656 }
657
658 pub fn user_id(&self) -> &UserId {
660 &self.device_keys.user_id
661 }
662
663 pub fn device_id(&self) -> &DeviceId {
665 &self.device_keys.device_id
666 }
667
668 pub fn display_name(&self) -> Option<&str> {
670 self.device_keys.unsigned.device_display_name.as_deref()
671 }
672
673 pub fn get_key(&self, algorithm: DeviceKeyAlgorithm) -> Option<&DeviceKey> {
675 self.device_keys.get_key(algorithm)
676 }
677
678 pub fn curve25519_key(&self) -> Option<Curve25519PublicKey> {
680 self.device_keys.curve25519_key()
681 }
682
683 pub fn ed25519_key(&self) -> Option<Ed25519PublicKey> {
685 self.device_keys.ed25519_key()
686 }
687
688 pub fn keys(&self) -> &BTreeMap<OwnedDeviceKeyId, DeviceKey> {
690 &self.device_keys.keys
691 }
692
693 pub fn signatures(&self) -> &Signatures {
695 &self.device_keys.signatures
696 }
697
698 pub fn local_trust_state(&self) -> LocalTrust {
700 *self.trust_state.read()
701 }
702
703 pub fn is_locally_trusted(&self) -> bool {
705 self.local_trust_state() == LocalTrust::Verified
706 }
707
708 pub fn is_blacklisted(&self) -> bool {
712 self.local_trust_state() == LocalTrust::BlackListed
713 }
714
715 pub(crate) fn set_trust_state(&self, state: LocalTrust) {
720 *self.trust_state.write() = state;
721 }
722
723 pub(crate) fn mark_withheld_code_as_sent(&self) {
724 self.withheld_code_sent.store(true, Ordering::Relaxed)
725 }
726
727 pub fn was_withheld_code_sent(&self) -> bool {
730 self.withheld_code_sent.load(Ordering::Relaxed)
731 }
732
733 pub fn algorithms(&self) -> &[EventEncryptionAlgorithm] {
735 &self.device_keys.algorithms
736 }
737
738 pub fn supports_olm(&self) -> bool {
740 #[cfg(feature = "experimental-algorithms")]
741 {
742 self.algorithms().contains(&EventEncryptionAlgorithm::OlmV1Curve25519AesSha2)
743 || self.algorithms().contains(&EventEncryptionAlgorithm::OlmV2Curve25519AesSha2)
744 }
745
746 #[cfg(not(feature = "experimental-algorithms"))]
747 {
748 self.algorithms().contains(&EventEncryptionAlgorithm::OlmV1Curve25519AesSha2)
749 }
750 }
751
752 pub(crate) async fn get_most_recent_session(
755 &self,
756 store: &CryptoStoreWrapper,
757 ) -> OlmResult<Option<Session>> {
758 if let Some(sender_key) = self.curve25519_key() {
759 if let Some(sessions) = store.get_sessions(&sender_key.to_base64()).await? {
760 let mut sessions = sessions.lock().await;
761 sessions.sort_by_key(|s| s.creation_time);
762
763 Ok(sessions.last().cloned())
764 } else {
765 Ok(None)
766 }
767 } else {
768 Ok(None)
769 }
770 }
771
772 #[cfg(feature = "experimental-algorithms")]
775 pub fn supports_olm_v2(&self) -> bool {
776 self.algorithms().contains(&EventEncryptionAlgorithm::OlmV2Curve25519AesSha2)
777 }
778
779 pub fn olm_session_config(&self) -> SessionConfig {
781 #[cfg(feature = "experimental-algorithms")]
782 if self.supports_olm_v2() {
783 SessionConfig::version_2()
784 } else {
785 SessionConfig::version_1()
786 }
787
788 #[cfg(not(feature = "experimental-algorithms"))]
789 SessionConfig::version_1()
790 }
791
792 pub fn is_deleted(&self) -> bool {
794 self.deleted.load(Ordering::Relaxed)
795 }
796
797 pub(crate) fn is_verified(
798 &self,
799 own_identity: &Option<OwnUserIdentityData>,
800 device_owner: &Option<UserIdentityData>,
801 #[cfg(feature = "experimental-x509-identity-verification")] x509_verifier: Option<
802 &X509Verifier,
803 >,
804 ) -> bool {
805 self.is_locally_trusted()
806 || self.is_cross_signing_trusted(
807 own_identity,
808 device_owner,
809 #[cfg(feature = "experimental-x509-identity-verification")]
810 x509_verifier,
811 )
812 }
813
814 pub(crate) fn is_cross_signing_trusted(
815 &self,
816 own_identity: &Option<OwnUserIdentityData>,
817 device_owner: &Option<UserIdentityData>,
818 #[cfg(feature = "experimental-x509-identity-verification")] x509_verifier: Option<
819 &X509Verifier,
820 >,
821 ) -> bool {
822 device_owner.as_ref().is_some_and(|device_identity| match device_identity {
823 UserIdentityData::Own(_) => own_identity.as_ref().is_some_and(|own_identity| {
824 own_identity.is_verified() && own_identity.is_device_signed(self)
825 }),
826
827 UserIdentityData::Other(device_identity) => {
832 device_identity.is_verified(
833 own_identity.as_ref(),
834 #[cfg(feature = "experimental-x509-identity-verification")]
835 x509_verifier,
836 ) && device_identity.is_device_signed(self)
837 }
838 })
839 }
840
841 pub(crate) fn is_cross_signed_by_owner(
842 &self,
843 device_owner_identity: &UserIdentityData,
844 ) -> bool {
845 match device_owner_identity {
846 UserIdentityData::Own(identity) => identity.is_device_signed(self),
849 UserIdentityData::Other(device_identity) => device_identity.is_device_signed(self),
852 }
853 }
854
855 #[instrument(
874 skip_all,
875 fields(
876 recipient = ?self.user_id(),
877 recipient_device = ?self.device_id(),
878 recipient_key = ?self.curve25519_key(),
879 event_type,
880 message_id,
881 ))
882 ]
883 pub(crate) async fn encrypt(
884 &self,
885 store: &CryptoStoreWrapper,
886 event_type: &str,
887 content: impl Serialize,
888 ) -> OlmResult<(Session, Raw<ToDeviceEncryptedEventContent>, String)> {
889 #[cfg(not(target_family = "wasm"))]
890 let message_id = ulid::Ulid::generate().to_string();
891 #[cfg(target_family = "wasm")]
892 let message_id = ruma::TransactionId::new().to_string();
893
894 tracing::Span::current().record("message_id", &message_id);
895
896 let session = self.get_most_recent_session(store).await?;
897
898 if let Some(mut session) = session {
899 let message =
900 session.encrypt(self, event_type, content, Some(message_id.clone())).await?;
901
902 Ok((session, message, message_id))
903 } else {
904 trace!("Trying to encrypt an event for a device, but no Olm session is found.");
905 Err(OlmError::MissingSession)
906 }
907 }
908
909 pub(crate) async fn maybe_encrypt_room_key(
910 &self,
911 store: &CryptoStoreWrapper,
912 session: OutboundGroupSession,
913 ) -> OlmResult<MaybeEncryptedRoomKey> {
914 let content = session.as_content().await;
915 let message_index = session.message_index().await;
916 let event_type = content.event_type().to_owned();
917
918 match self.encrypt(store, &event_type, content).await {
919 Ok((session, encrypted, _)) => Ok(MaybeEncryptedRoomKey::Encrypted {
920 share_info: Box::new(ShareInfo::new_shared(
921 session.sender_key().to_owned(),
922 message_index,
923 self.olm_wedging_index,
924 )),
925 used_session: Box::new(session),
926 message: encrypted.cast(),
927 }),
928
929 Err(OlmError::MissingSession) => Ok(MaybeEncryptedRoomKey::MissingSession),
930 Err(e) => Err(e),
931 }
932 }
933
934 pub(crate) fn update_device(
938 &mut self,
939 device_keys: &DeviceKeys,
940 ) -> Result<bool, SignatureError> {
941 device_keys.check_self_signature()?;
942
943 if self.user_id() != device_keys.user_id || self.device_id() != device_keys.device_id {
944 Err(SignatureError::UserIdMismatch)
945 } else if self.ed25519_key() != device_keys.ed25519_key() {
946 Err(SignatureError::SigningKeyChanged(
947 self.ed25519_key().map(Box::new),
948 device_keys.ed25519_key().map(Box::new),
949 ))
950 } else if self.device_keys.as_ref() != device_keys {
951 trace!(
952 user_id = ?self.user_id(),
953 device_id = ?self.device_id(),
954 keys = ?self.keys(),
955 "Updated a device",
956 );
957
958 self.device_keys = device_keys.clone().into();
959
960 Ok(true)
961 } else {
962 Ok(false)
964 }
965 }
966
967 pub fn as_device_keys(&self) -> &DeviceKeys {
969 &self.device_keys
970 }
971
972 pub(crate) fn has_signed_raw(
982 &self,
983 signatures: &Signatures,
984 canonical_json: &str,
985 ) -> Result<(), SignatureError> {
986 let key = self.ed25519_key().ok_or(SignatureError::MissingSigningKey)?;
987 let user_id = self.user_id();
988 let key_id = &DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, self.device_id());
989
990 key.verify_canonicalized_json(user_id, key_id, signatures, canonical_json)
991 }
992
993 pub(crate) fn verify_one_time_key(
994 &self,
995 one_time_key: &SignedKey,
996 ) -> Result<(), SignatureError> {
997 self.device_keys.has_signed(one_time_key)
998 }
999
1000 pub(crate) fn mark_as_deleted(&self) {
1002 self.deleted.store(true, Ordering::Relaxed);
1003 }
1004
1005 #[cfg(any(test, feature = "testing"))]
1006 #[allow(dead_code)]
1007 pub async fn from_machine_test_helper(
1009 machine: &OlmMachine,
1010 ) -> Result<DeviceData, crate::CryptoStoreError> {
1011 Ok(DeviceData::from_account(&*machine.store().cache().await?.account().await?))
1012 }
1013
1014 pub fn from_account(account: &Account) -> DeviceData {
1027 let device_keys = account.device_keys();
1028 let mut device = DeviceData::try_from(&device_keys)
1029 .expect("Creating a device from our own account should always succeed");
1030 device.first_time_seen_ts = account.creation_local_time();
1031
1032 device
1033 }
1034
1035 pub fn first_time_seen_ts(&self) -> MilliSecondsSinceUnixEpoch {
1038 self.first_time_seen_ts
1039 }
1040
1041 pub fn is_dehydrated(&self) -> bool {
1043 self.device_keys.dehydrated.unwrap_or(false)
1044 }
1045}
1046
1047impl TryFrom<&DeviceKeys> for DeviceData {
1048 type Error = SignatureError;
1049
1050 fn try_from(device_keys: &DeviceKeys) -> Result<Self, Self::Error> {
1051 device_keys.check_self_signature()?;
1052 Ok(Self {
1053 device_keys: device_keys.clone().into(),
1054 deleted: Arc::new(AtomicBool::new(false)),
1055 trust_state: Arc::new(RwLock::new(LocalTrust::Unset)),
1056 withheld_code_sent: Arc::new(AtomicBool::new(false)),
1057 first_time_seen_ts: MilliSecondsSinceUnixEpoch::now(),
1058 olm_wedging_index: Default::default(),
1059 })
1060 }
1061}
1062
1063impl PartialEq for DeviceData {
1064 fn eq(&self, other: &Self) -> bool {
1065 self.user_id() == other.user_id() && self.device_id() == other.device_id()
1066 }
1067}
1068
1069#[cfg(any(test, feature = "testing"))]
1071#[allow(dead_code)]
1072pub(crate) mod testing {
1073 use serde_json::json;
1074
1075 use crate::{identities::DeviceData, types::DeviceKeys};
1076
1077 pub fn device_keys() -> DeviceKeys {
1079 let device_keys = json!({
1080 "algorithms": vec![
1081 "m.olm.v1.curve25519-aes-sha2",
1082 "m.megolm.v1.aes-sha2"
1083 ],
1084 "device_id": "BNYQQWUMXO",
1085 "user_id": "@example:localhost",
1086 "keys": {
1087 "curve25519:BNYQQWUMXO": "xfgbLIC5WAl1OIkpOzoxpCe8FsRDT6nch7NQsOb15nc",
1088 "ed25519:BNYQQWUMXO": "2/5LWJMow5zhJqakV88SIc7q/1pa8fmkfgAzx72w9G4"
1089 },
1090 "signatures": {
1091 "@example:localhost": {
1092 "ed25519:BNYQQWUMXO": "kTwMrbsLJJM/uFGOj/oqlCaRuw7i9p/6eGrTlXjo8UJMCFAetoyWzoMcF35vSe4S6FTx8RJmqX6rM7ep53MHDQ"
1093 }
1094 },
1095 "unsigned": {
1096 "device_display_name": "Alice's mobile phone"
1097 }
1098 });
1099
1100 serde_json::from_value(device_keys).unwrap()
1101 }
1102
1103 pub fn get_device() -> DeviceData {
1105 let device_keys = device_keys();
1106 DeviceData::try_from(&device_keys).unwrap()
1107 }
1108}
1109
1110#[cfg(test)]
1111pub(crate) mod tests {
1112 use ruma::{MilliSecondsSinceUnixEpoch, user_id};
1113 use serde_json::json;
1114 use vodozemac::{Curve25519PublicKey, Ed25519PublicKey};
1115
1116 use super::testing::{device_keys, get_device};
1117 use crate::{DeviceData, identities::LocalTrust};
1118
1119 #[test]
1120 fn create_a_device() {
1121 let now = MilliSecondsSinceUnixEpoch::now();
1122 let user_id = user_id!("@example:localhost");
1123 let device_id = "BNYQQWUMXO";
1124
1125 let device = get_device();
1126
1127 assert_eq!(user_id, device.user_id());
1128 assert_eq!(device_id, device.device_id());
1129 assert_eq!(device.algorithms().len(), 2);
1130 assert_eq!(LocalTrust::Unset, device.local_trust_state());
1131 assert_eq!("Alice's mobile phone", device.display_name().unwrap());
1132 assert_eq!(
1133 device.curve25519_key().unwrap(),
1134 Curve25519PublicKey::from_base64("xfgbLIC5WAl1OIkpOzoxpCe8FsRDT6nch7NQsOb15nc")
1135 .unwrap(),
1136 );
1137 assert_eq!(
1138 device.ed25519_key().unwrap(),
1139 Ed25519PublicKey::from_base64("2/5LWJMow5zhJqakV88SIc7q/1pa8fmkfgAzx72w9G4").unwrap(),
1140 );
1141
1142 let then = MilliSecondsSinceUnixEpoch::now();
1143
1144 assert!(device.first_time_seen_ts() >= now);
1145 assert!(device.first_time_seen_ts() <= then);
1146 }
1147
1148 #[test]
1149 fn update_a_device() {
1150 let mut device = get_device();
1151
1152 assert_eq!("Alice's mobile phone", device.display_name().unwrap());
1153
1154 let display_name = "Alice's work computer".to_owned();
1155
1156 let mut device_keys = device_keys();
1157 device_keys.unsigned.device_display_name = Some(display_name.clone());
1158 assert!(device.update_device(&device_keys).unwrap());
1159 assert_eq!(&display_name, device.display_name().as_ref().unwrap());
1160
1161 assert!(!device.update_device(&device_keys).unwrap());
1164 }
1165
1166 #[test]
1167 #[allow(clippy::redundant_clone)]
1168 fn delete_a_device() {
1169 let device = get_device();
1170 assert!(!device.is_deleted());
1171
1172 let device_clone = device.clone();
1173
1174 device.mark_as_deleted();
1175 assert!(device.is_deleted());
1176 assert!(device_clone.is_deleted());
1177 }
1178
1179 #[test]
1180 fn deserialize_device() {
1181 let user_id = user_id!("@example:localhost");
1182 let device_id = "BNYQQWUMXO";
1183
1184 let device = json!({
1185 "inner": {
1186 "user_id": user_id,
1187 "device_id": device_id,
1188 "algorithms": ["m.olm.v1.curve25519-aes-sha2","m.megolm.v1.aes-sha2"],
1189 "keys": {
1190 "curve25519:BNYQQWUMXO": "xfgbLIC5WAl1OIkpOzoxpCe8FsRDT6nch7NQsOb15nc",
1191 "ed25519:BNYQQWUMXO": "2/5LWJMow5zhJqakV88SIc7q/1pa8fmkfgAzx72w9G4"
1192 },
1193 "signatures": {
1194 "@example:localhost": {
1195 "ed25519:BNYQQWUMXO": "kTwMrbsLJJM/uFGOj/oqlCaRuw7i9p/6eGrTlXjo8UJMCFAetoyWzoMcF35vSe4S6FTx8RJmqX6rM7ep53MHDQ"
1196 }
1197 },
1198 "unsigned": {
1199 "device_display_name": "Alice's mobile phone"
1200 }
1201 },
1202 "deleted": false,
1203 "trust_state": "Verified",
1204 "withheld_code_sent": false,
1205 "first_time_seen_ts": 1696931068314u64
1206 });
1207
1208 let device: DeviceData =
1209 serde_json::from_value(device).expect("We should be able to deserialize our device");
1210
1211 assert_eq!(user_id, device.user_id());
1212 assert_eq!(device_id, device.device_id());
1213 assert_eq!(device.algorithms().len(), 2);
1214 assert_eq!(LocalTrust::Verified, device.local_trust_state());
1215 assert_eq!("Alice's mobile phone", device.display_name().unwrap());
1216 assert_eq!(
1217 device.curve25519_key().unwrap(),
1218 Curve25519PublicKey::from_base64("xfgbLIC5WAl1OIkpOzoxpCe8FsRDT6nch7NQsOb15nc")
1219 .unwrap(),
1220 );
1221 assert_eq!(
1222 device.ed25519_key().unwrap(),
1223 Ed25519PublicKey::from_base64("2/5LWJMow5zhJqakV88SIc7q/1pa8fmkfgAzx72w9G4").unwrap(),
1224 );
1225 }
1226
1227 #[cfg(feature = "experimental-x509-identity-verification")]
1231 #[matrix_sdk_test::async_test]
1232 async fn test_x509_verified_owner_confers_device_trust() {
1233 use std::sync::Arc;
1234
1235 use ruma::device_id;
1236
1237 use crate::{
1238 machine::test_helpers::create_signed_device_of_unverified_user,
1239 olm::{Account, PrivateCrossSigningIdentity},
1240 x509::{
1241 RustRawX509Signer, RustRawX509Verifier, X509Signer, X509Verifier,
1242 tests::{ca_cert, cert_and_key_with_email_signed_by},
1243 },
1244 };
1245
1246 let (ca_certificate, ca_signing_key) = ca_cert();
1249 let (certificate, signing_key) =
1250 cert_and_key_with_email_signed_by("alice@hs.co", &ca_certificate, &ca_signing_key);
1251 let x509_signer = X509Signer::new(Arc::new(
1252 RustRawX509Signer::new_from_pem_data(&certificate.pem(), &signing_key.serialize_pem())
1253 .unwrap(),
1254 ));
1255 let account = Account::with_device_id(user_id!("@alice:hs.co"), device_id!("ALICEDEV"));
1256 let alice_private_identity =
1257 PrivateCrossSigningIdentity::for_account(&account, Some(&x509_signer)).await.unwrap();
1258
1259 let mut device =
1262 create_signed_device_of_unverified_user(account.device_keys(), &alice_private_identity)
1263 .await;
1264 assert!(device.is_cross_signed_by_owner());
1265
1266 assert!(!device.is_cross_signing_trusted());
1268
1269 device.x509_verifier = Some(X509Verifier::new(Arc::new(
1271 RustRawX509Verifier::new_from_pem_data(&ca_certificate.pem()).unwrap(),
1272 )));
1273 assert!(device.is_cross_signing_trusted());
1274 assert!(device.is_verified());
1275
1276 let (wrong_ca_certificate, _) = ca_cert();
1278 device.x509_verifier = Some(X509Verifier::new(Arc::new(
1279 RustRawX509Verifier::new_from_pem_data(&wrong_ca_certificate.pem()).unwrap(),
1280 )));
1281 assert!(!device.is_cross_signing_trusted());
1282 }
1283}