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 fn other_devices(&self) -> impl Iterator<Item = &DeviceData> {
581 self.inner.values().filter(|d| {
582 !(d.user_id() == self.own_user_id() && d.device_id() == self.own_device_id())
583 })
584 }
585
586 pub fn is_any_verified(&self) -> bool {
592 self.other_devices().any(|d| {
593 d.is_verified(
594 &self.own_identity,
595 &self.device_owner_identity,
596 #[cfg(feature = "experimental-x509-identity-verification")]
597 self.x509_verifier.as_ref(),
598 )
599 })
600 }
601
602 pub fn has_other_devices_to_verify_against(&self) -> bool {
615 let Some(owner_identity) = &self.device_owner_identity else {
616 return false;
617 };
618
619 self.other_devices().any(|d| {
620 d.is_cross_signed_by_owner(owner_identity)
621 && d.curve25519_key().is_some()
622 && !d.is_dehydrated()
623 })
624 }
625
626 pub fn keys(&self) -> impl Iterator<Item = &DeviceId> {
628 self.inner.keys().map(Deref::deref)
629 }
630
631 pub fn devices(&self) -> impl Iterator<Item = Device> + '_ {
633 self.inner.values().map(move |d| Device {
634 inner: d.clone(),
635 verification_machine: self.verification_machine.clone(),
636 own_identity: self.own_identity.clone(),
637 device_owner_identity: self.device_owner_identity.clone(),
638 #[cfg(feature = "experimental-x509-identity-verification")]
639 x509_verifier: self.x509_verifier.clone(),
640 })
641 }
642}
643
644#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
646#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
647pub enum LocalTrust {
648 Verified = 0,
650 BlackListed = 1,
652 Ignored = 2,
654 Unset = 3,
656}
657
658impl From<i64> for LocalTrust {
659 fn from(state: i64) -> Self {
660 match state {
661 0 => LocalTrust::Verified,
662 1 => LocalTrust::BlackListed,
663 2 => LocalTrust::Ignored,
664 3 => LocalTrust::Unset,
665 _ => LocalTrust::Unset,
666 }
667 }
668}
669
670impl DeviceData {
671 pub fn new(device_keys: DeviceKeys, trust_state: LocalTrust) -> Self {
675 Self {
676 device_keys: device_keys.into(),
677 trust_state: Arc::new(RwLock::new(trust_state)),
678 deleted: Arc::new(AtomicBool::new(false)),
679 withheld_code_sent: Arc::new(AtomicBool::new(false)),
680 first_time_seen_ts: MilliSecondsSinceUnixEpoch::now(),
681 olm_wedging_index: Default::default(),
682 }
683 }
684
685 pub fn user_id(&self) -> &UserId {
687 &self.device_keys.user_id
688 }
689
690 pub fn device_id(&self) -> &DeviceId {
692 &self.device_keys.device_id
693 }
694
695 pub fn display_name(&self) -> Option<&str> {
697 self.device_keys.unsigned.device_display_name.as_deref()
698 }
699
700 pub fn get_key(&self, algorithm: DeviceKeyAlgorithm) -> Option<&DeviceKey> {
702 self.device_keys.get_key(algorithm)
703 }
704
705 pub fn curve25519_key(&self) -> Option<Curve25519PublicKey> {
707 self.device_keys.curve25519_key()
708 }
709
710 pub fn ed25519_key(&self) -> Option<Ed25519PublicKey> {
712 self.device_keys.ed25519_key()
713 }
714
715 pub fn keys(&self) -> &BTreeMap<OwnedDeviceKeyId, DeviceKey> {
717 &self.device_keys.keys
718 }
719
720 pub fn signatures(&self) -> &Signatures {
722 &self.device_keys.signatures
723 }
724
725 pub fn local_trust_state(&self) -> LocalTrust {
727 *self.trust_state.read()
728 }
729
730 pub fn is_locally_trusted(&self) -> bool {
732 self.local_trust_state() == LocalTrust::Verified
733 }
734
735 pub fn is_blacklisted(&self) -> bool {
739 self.local_trust_state() == LocalTrust::BlackListed
740 }
741
742 pub(crate) fn set_trust_state(&self, state: LocalTrust) {
747 *self.trust_state.write() = state;
748 }
749
750 pub(crate) fn mark_withheld_code_as_sent(&self) {
751 self.withheld_code_sent.store(true, Ordering::Relaxed)
752 }
753
754 pub fn was_withheld_code_sent(&self) -> bool {
757 self.withheld_code_sent.load(Ordering::Relaxed)
758 }
759
760 pub fn algorithms(&self) -> &[EventEncryptionAlgorithm] {
762 &self.device_keys.algorithms
763 }
764
765 pub fn supports_olm(&self) -> bool {
767 #[cfg(feature = "experimental-algorithms")]
768 {
769 self.algorithms().contains(&EventEncryptionAlgorithm::OlmV1Curve25519AesSha2)
770 || self.algorithms().contains(&EventEncryptionAlgorithm::OlmV2Curve25519AesSha2)
771 }
772
773 #[cfg(not(feature = "experimental-algorithms"))]
774 {
775 self.algorithms().contains(&EventEncryptionAlgorithm::OlmV1Curve25519AesSha2)
776 }
777 }
778
779 pub(crate) async fn get_most_recent_session(
782 &self,
783 store: &CryptoStoreWrapper,
784 ) -> OlmResult<Option<Session>> {
785 if let Some(sender_key) = self.curve25519_key() {
786 if let Some(sessions) = store.get_sessions(&sender_key.to_base64()).await? {
787 let mut sessions = sessions.lock().await;
788 sessions.sort_by_key(|s| s.creation_time);
789
790 Ok(sessions.last().cloned())
791 } else {
792 Ok(None)
793 }
794 } else {
795 Ok(None)
796 }
797 }
798
799 #[cfg(feature = "experimental-algorithms")]
802 pub fn supports_olm_v2(&self) -> bool {
803 self.algorithms().contains(&EventEncryptionAlgorithm::OlmV2Curve25519AesSha2)
804 }
805
806 pub fn olm_session_config(&self) -> SessionConfig {
808 #[cfg(feature = "experimental-algorithms")]
809 if self.supports_olm_v2() {
810 SessionConfig::version_2()
811 } else {
812 SessionConfig::version_1()
813 }
814
815 #[cfg(not(feature = "experimental-algorithms"))]
816 SessionConfig::version_1()
817 }
818
819 pub fn is_deleted(&self) -> bool {
821 self.deleted.load(Ordering::Relaxed)
822 }
823
824 pub(crate) fn is_verified(
825 &self,
826 own_identity: &Option<OwnUserIdentityData>,
827 device_owner: &Option<UserIdentityData>,
828 #[cfg(feature = "experimental-x509-identity-verification")] x509_verifier: Option<
829 &X509Verifier,
830 >,
831 ) -> bool {
832 self.is_locally_trusted()
833 || self.is_cross_signing_trusted(
834 own_identity,
835 device_owner,
836 #[cfg(feature = "experimental-x509-identity-verification")]
837 x509_verifier,
838 )
839 }
840
841 pub(crate) fn is_cross_signing_trusted(
842 &self,
843 own_identity: &Option<OwnUserIdentityData>,
844 device_owner: &Option<UserIdentityData>,
845 #[cfg(feature = "experimental-x509-identity-verification")] x509_verifier: Option<
846 &X509Verifier,
847 >,
848 ) -> bool {
849 device_owner.as_ref().is_some_and(|device_identity| match device_identity {
850 UserIdentityData::Own(_) => own_identity.as_ref().is_some_and(|own_identity| {
851 own_identity.is_verified() && own_identity.is_device_signed(self)
852 }),
853
854 UserIdentityData::Other(device_identity) => {
859 device_identity.is_verified(
860 own_identity.as_ref(),
861 #[cfg(feature = "experimental-x509-identity-verification")]
862 x509_verifier,
863 ) && device_identity.is_device_signed(self)
864 }
865 })
866 }
867
868 pub(crate) fn is_cross_signed_by_owner(
869 &self,
870 device_owner_identity: &UserIdentityData,
871 ) -> bool {
872 match device_owner_identity {
873 UserIdentityData::Own(identity) => identity.is_device_signed(self),
876 UserIdentityData::Other(device_identity) => device_identity.is_device_signed(self),
879 }
880 }
881
882 #[instrument(
901 skip_all,
902 fields(
903 recipient = ?self.user_id(),
904 recipient_device = ?self.device_id(),
905 recipient_key = ?self.curve25519_key(),
906 event_type,
907 message_id,
908 ))
909 ]
910 pub(crate) async fn encrypt(
911 &self,
912 store: &CryptoStoreWrapper,
913 event_type: &str,
914 content: impl Serialize,
915 ) -> OlmResult<(Session, Raw<ToDeviceEncryptedEventContent>, String)> {
916 #[cfg(not(target_family = "wasm"))]
917 let message_id = ulid::Ulid::generate().to_string();
918 #[cfg(target_family = "wasm")]
919 let message_id = ruma::TransactionId::new().to_string();
920
921 tracing::Span::current().record("message_id", &message_id);
922
923 let session = self.get_most_recent_session(store).await?;
924
925 if let Some(mut session) = session {
926 let message =
927 session.encrypt(self, event_type, content, Some(message_id.clone())).await?;
928
929 Ok((session, message, message_id))
930 } else {
931 trace!("Trying to encrypt an event for a device, but no Olm session is found.");
932 Err(OlmError::MissingSession)
933 }
934 }
935
936 pub(crate) async fn maybe_encrypt_room_key(
937 &self,
938 store: &CryptoStoreWrapper,
939 session: OutboundGroupSession,
940 ) -> OlmResult<MaybeEncryptedRoomKey> {
941 let content = session.as_content().await;
942 let message_index = session.message_index().await;
943 let event_type = content.event_type().to_owned();
944
945 match self.encrypt(store, &event_type, content).await {
946 Ok((session, encrypted, _)) => Ok(MaybeEncryptedRoomKey::Encrypted {
947 share_info: Box::new(ShareInfo::new_shared(
948 session.sender_key().to_owned(),
949 message_index,
950 self.olm_wedging_index,
951 )),
952 used_session: Box::new(session),
953 message: encrypted.cast(),
954 }),
955
956 Err(OlmError::MissingSession) => Ok(MaybeEncryptedRoomKey::MissingSession),
957 Err(e) => Err(e),
958 }
959 }
960
961 pub(crate) fn update_device(
965 &mut self,
966 device_keys: &DeviceKeys,
967 ) -> Result<bool, SignatureError> {
968 device_keys.check_self_signature()?;
969
970 if self.user_id() != device_keys.user_id || self.device_id() != device_keys.device_id {
971 Err(SignatureError::UserIdMismatch)
972 } else if self.ed25519_key() != device_keys.ed25519_key() {
973 Err(SignatureError::SigningKeyChanged(
974 self.ed25519_key().map(Box::new),
975 device_keys.ed25519_key().map(Box::new),
976 ))
977 } else if self.device_keys.as_ref() != device_keys {
978 trace!(
979 user_id = ?self.user_id(),
980 device_id = ?self.device_id(),
981 keys = ?self.keys(),
982 "Updated a device",
983 );
984
985 self.device_keys = device_keys.clone().into();
986
987 Ok(true)
988 } else {
989 Ok(false)
991 }
992 }
993
994 pub fn as_device_keys(&self) -> &DeviceKeys {
996 &self.device_keys
997 }
998
999 pub(crate) fn has_signed_raw(
1009 &self,
1010 signatures: &Signatures,
1011 canonical_json: &str,
1012 ) -> Result<(), SignatureError> {
1013 let key = self.ed25519_key().ok_or(SignatureError::MissingSigningKey)?;
1014 let user_id = self.user_id();
1015 let key_id = &DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, self.device_id());
1016
1017 key.verify_canonicalized_json(user_id, key_id, signatures, canonical_json)
1018 }
1019
1020 pub(crate) fn verify_one_time_key(
1021 &self,
1022 one_time_key: &SignedKey,
1023 ) -> Result<(), SignatureError> {
1024 self.device_keys.has_signed(one_time_key)
1025 }
1026
1027 pub(crate) fn mark_as_deleted(&self) {
1029 self.deleted.store(true, Ordering::Relaxed);
1030 }
1031
1032 #[cfg(any(test, feature = "testing"))]
1033 #[allow(dead_code)]
1034 pub async fn from_machine_test_helper(
1036 machine: &OlmMachine,
1037 ) -> Result<DeviceData, crate::CryptoStoreError> {
1038 Ok(DeviceData::from_account(&*machine.store().cache().await?.account().await?))
1039 }
1040
1041 pub fn from_account(account: &Account) -> DeviceData {
1054 let device_keys = account.device_keys();
1055 let mut device = DeviceData::try_from(&device_keys)
1056 .expect("Creating a device from our own account should always succeed");
1057 device.first_time_seen_ts = account.creation_local_time();
1058
1059 device
1060 }
1061
1062 pub fn first_time_seen_ts(&self) -> MilliSecondsSinceUnixEpoch {
1065 self.first_time_seen_ts
1066 }
1067
1068 pub fn is_dehydrated(&self) -> bool {
1070 self.device_keys.dehydrated.unwrap_or(false)
1071 }
1072}
1073
1074impl TryFrom<&DeviceKeys> for DeviceData {
1075 type Error = SignatureError;
1076
1077 fn try_from(device_keys: &DeviceKeys) -> Result<Self, Self::Error> {
1078 device_keys.check_self_signature()?;
1079 Ok(Self {
1080 device_keys: device_keys.clone().into(),
1081 deleted: Arc::new(AtomicBool::new(false)),
1082 trust_state: Arc::new(RwLock::new(LocalTrust::Unset)),
1083 withheld_code_sent: Arc::new(AtomicBool::new(false)),
1084 first_time_seen_ts: MilliSecondsSinceUnixEpoch::now(),
1085 olm_wedging_index: Default::default(),
1086 })
1087 }
1088}
1089
1090impl PartialEq for DeviceData {
1091 fn eq(&self, other: &Self) -> bool {
1092 self.user_id() == other.user_id() && self.device_id() == other.device_id()
1093 }
1094}
1095
1096#[cfg(any(test, feature = "testing"))]
1098#[allow(dead_code)]
1099pub(crate) mod testing {
1100 use serde_json::json;
1101
1102 use crate::{identities::DeviceData, types::DeviceKeys};
1103
1104 pub fn device_keys() -> DeviceKeys {
1106 let device_keys = json!({
1107 "algorithms": vec![
1108 "m.olm.v1.curve25519-aes-sha2",
1109 "m.megolm.v1.aes-sha2"
1110 ],
1111 "device_id": "BNYQQWUMXO",
1112 "user_id": "@example:localhost",
1113 "keys": {
1114 "curve25519:BNYQQWUMXO": "xfgbLIC5WAl1OIkpOzoxpCe8FsRDT6nch7NQsOb15nc",
1115 "ed25519:BNYQQWUMXO": "2/5LWJMow5zhJqakV88SIc7q/1pa8fmkfgAzx72w9G4"
1116 },
1117 "signatures": {
1118 "@example:localhost": {
1119 "ed25519:BNYQQWUMXO": "kTwMrbsLJJM/uFGOj/oqlCaRuw7i9p/6eGrTlXjo8UJMCFAetoyWzoMcF35vSe4S6FTx8RJmqX6rM7ep53MHDQ"
1120 }
1121 },
1122 "unsigned": {
1123 "device_display_name": "Alice's mobile phone"
1124 }
1125 });
1126
1127 serde_json::from_value(device_keys).unwrap()
1128 }
1129
1130 pub fn get_device() -> DeviceData {
1132 let device_keys = device_keys();
1133 DeviceData::try_from(&device_keys).unwrap()
1134 }
1135}
1136
1137#[cfg(test)]
1138pub(crate) mod tests {
1139 use ruma::{MilliSecondsSinceUnixEpoch, user_id};
1140 use serde_json::json;
1141 use vodozemac::{Curve25519PublicKey, Ed25519PublicKey};
1142
1143 use super::testing::{device_keys, get_device};
1144 use crate::{DeviceData, identities::LocalTrust};
1145
1146 #[test]
1147 fn create_a_device() {
1148 let now = MilliSecondsSinceUnixEpoch::now();
1149 let user_id = user_id!("@example:localhost");
1150 let device_id = "BNYQQWUMXO";
1151
1152 let device = get_device();
1153
1154 assert_eq!(user_id, device.user_id());
1155 assert_eq!(device_id, device.device_id());
1156 assert_eq!(device.algorithms().len(), 2);
1157 assert_eq!(LocalTrust::Unset, device.local_trust_state());
1158 assert_eq!("Alice's mobile phone", device.display_name().unwrap());
1159 assert_eq!(
1160 device.curve25519_key().unwrap(),
1161 Curve25519PublicKey::from_base64("xfgbLIC5WAl1OIkpOzoxpCe8FsRDT6nch7NQsOb15nc")
1162 .unwrap(),
1163 );
1164 assert_eq!(
1165 device.ed25519_key().unwrap(),
1166 Ed25519PublicKey::from_base64("2/5LWJMow5zhJqakV88SIc7q/1pa8fmkfgAzx72w9G4").unwrap(),
1167 );
1168
1169 let then = MilliSecondsSinceUnixEpoch::now();
1170
1171 assert!(device.first_time_seen_ts() >= now);
1172 assert!(device.first_time_seen_ts() <= then);
1173 }
1174
1175 #[test]
1176 fn update_a_device() {
1177 let mut device = get_device();
1178
1179 assert_eq!("Alice's mobile phone", device.display_name().unwrap());
1180
1181 let display_name = "Alice's work computer".to_owned();
1182
1183 let mut device_keys = device_keys();
1184 device_keys.unsigned.device_display_name = Some(display_name.clone());
1185 assert!(device.update_device(&device_keys).unwrap());
1186 assert_eq!(&display_name, device.display_name().as_ref().unwrap());
1187
1188 assert!(!device.update_device(&device_keys).unwrap());
1191 }
1192
1193 #[test]
1194 #[allow(clippy::redundant_clone)]
1195 fn delete_a_device() {
1196 let device = get_device();
1197 assert!(!device.is_deleted());
1198
1199 let device_clone = device.clone();
1200
1201 device.mark_as_deleted();
1202 assert!(device.is_deleted());
1203 assert!(device_clone.is_deleted());
1204 }
1205
1206 #[test]
1207 fn deserialize_device() {
1208 let user_id = user_id!("@example:localhost");
1209 let device_id = "BNYQQWUMXO";
1210
1211 let device = json!({
1212 "inner": {
1213 "user_id": user_id,
1214 "device_id": device_id,
1215 "algorithms": ["m.olm.v1.curve25519-aes-sha2","m.megolm.v1.aes-sha2"],
1216 "keys": {
1217 "curve25519:BNYQQWUMXO": "xfgbLIC5WAl1OIkpOzoxpCe8FsRDT6nch7NQsOb15nc",
1218 "ed25519:BNYQQWUMXO": "2/5LWJMow5zhJqakV88SIc7q/1pa8fmkfgAzx72w9G4"
1219 },
1220 "signatures": {
1221 "@example:localhost": {
1222 "ed25519:BNYQQWUMXO": "kTwMrbsLJJM/uFGOj/oqlCaRuw7i9p/6eGrTlXjo8UJMCFAetoyWzoMcF35vSe4S6FTx8RJmqX6rM7ep53MHDQ"
1223 }
1224 },
1225 "unsigned": {
1226 "device_display_name": "Alice's mobile phone"
1227 }
1228 },
1229 "deleted": false,
1230 "trust_state": "Verified",
1231 "withheld_code_sent": false,
1232 "first_time_seen_ts": 1696931068314u64
1233 });
1234
1235 let device: DeviceData =
1236 serde_json::from_value(device).expect("We should be able to deserialize our device");
1237
1238 assert_eq!(user_id, device.user_id());
1239 assert_eq!(device_id, device.device_id());
1240 assert_eq!(device.algorithms().len(), 2);
1241 assert_eq!(LocalTrust::Verified, device.local_trust_state());
1242 assert_eq!("Alice's mobile phone", device.display_name().unwrap());
1243 assert_eq!(
1244 device.curve25519_key().unwrap(),
1245 Curve25519PublicKey::from_base64("xfgbLIC5WAl1OIkpOzoxpCe8FsRDT6nch7NQsOb15nc")
1246 .unwrap(),
1247 );
1248 assert_eq!(
1249 device.ed25519_key().unwrap(),
1250 Ed25519PublicKey::from_base64("2/5LWJMow5zhJqakV88SIc7q/1pa8fmkfgAzx72w9G4").unwrap(),
1251 );
1252 }
1253
1254 #[cfg(feature = "experimental-x509-identity-verification")]
1258 #[matrix_sdk_test::async_test]
1259 async fn test_x509_verified_owner_confers_device_trust() {
1260 use std::sync::Arc;
1261
1262 use ruma::device_id;
1263
1264 use crate::{
1265 machine::test_helpers::create_signed_device_of_unverified_user,
1266 olm::{Account, PrivateCrossSigningIdentity},
1267 x509::{
1268 RustRawX509Signer, RustRawX509Verifier, X509Signer, X509Verifier,
1269 tests::{ca_cert, cert_and_key_with_email_signed_by},
1270 },
1271 };
1272
1273 let (ca_certificate, ca_signing_key) = ca_cert();
1276 let (certificate, signing_key) =
1277 cert_and_key_with_email_signed_by("alice@hs.co", &ca_certificate, &ca_signing_key);
1278 let x509_signer = X509Signer::new(Arc::new(
1279 RustRawX509Signer::new_from_pem_data(&certificate.pem(), &signing_key.serialize_pem())
1280 .unwrap(),
1281 ));
1282 let account = Account::with_device_id(user_id!("@alice:hs.co"), device_id!("ALICEDEV"));
1283 let alice_private_identity =
1284 PrivateCrossSigningIdentity::for_account(&account, Some(&x509_signer)).await.unwrap();
1285
1286 let mut device =
1289 create_signed_device_of_unverified_user(account.device_keys(), &alice_private_identity)
1290 .await;
1291 assert!(device.is_cross_signed_by_owner());
1292
1293 assert!(!device.is_cross_signing_trusted());
1295
1296 device.x509_verifier = Some(X509Verifier::new(Arc::new(
1298 RustRawX509Verifier::new_from_pem_data(&ca_certificate.pem()).unwrap(),
1299 )));
1300 assert!(device.is_cross_signing_trusted());
1301 assert!(device.is_verified());
1302
1303 let (wrong_ca_certificate, _) = ca_cert();
1305 device.x509_verifier = Some(X509Verifier::new(Arc::new(
1306 RustRawX509Verifier::new_from_pem_data(&wrong_ca_certificate.pem()).unwrap(),
1307 )));
1308 assert!(!device.is_cross_signing_trusted());
1309 }
1310}