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);
279 let curve25519_comparison = self.curve25519_key().map(|k| k == session.sender_key());
280
281 match (ed25519_comparison, curve25519_comparison) {
282 (_, 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 (Some(true), Some(true)) => Ok(true),
292 _ => Ok(false),
296 }
297 } else {
298 Ok(false)
299 }
300 }
301
302 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 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 pub fn request_verification(&self) -> (VerificationRequest, OutgoingVerificationRequest) {
326 self.request_verification_helper(None)
327 }
328
329 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 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 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 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 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 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 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 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 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 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 pub fn is_dehydrated(&self) -> bool {
540 self.inner.is_dehydrated()
541 }
542}
543
544#[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 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 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 pub fn keys(&self) -> impl Iterator<Item = &DeviceId> {
599 self.inner.keys().map(Deref::deref)
600 }
601
602 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#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
617#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
618pub enum LocalTrust {
619 Verified = 0,
621 BlackListed = 1,
623 Ignored = 2,
625 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 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 pub fn user_id(&self) -> &UserId {
658 &self.device_keys.user_id
659 }
660
661 pub fn device_id(&self) -> &DeviceId {
663 &self.device_keys.device_id
664 }
665
666 pub fn display_name(&self) -> Option<&str> {
668 self.device_keys.unsigned.device_display_name.as_deref()
669 }
670
671 pub fn get_key(&self, algorithm: DeviceKeyAlgorithm) -> Option<&DeviceKey> {
673 self.device_keys.get_key(algorithm)
674 }
675
676 pub fn curve25519_key(&self) -> Option<Curve25519PublicKey> {
678 self.device_keys.curve25519_key()
679 }
680
681 pub fn ed25519_key(&self) -> Option<Ed25519PublicKey> {
683 self.device_keys.ed25519_key()
684 }
685
686 pub fn keys(&self) -> &BTreeMap<OwnedDeviceKeyId, DeviceKey> {
688 &self.device_keys.keys
689 }
690
691 pub fn signatures(&self) -> &Signatures {
693 &self.device_keys.signatures
694 }
695
696 pub fn local_trust_state(&self) -> LocalTrust {
698 *self.trust_state.read()
699 }
700
701 pub fn is_locally_trusted(&self) -> bool {
703 self.local_trust_state() == LocalTrust::Verified
704 }
705
706 pub fn is_blacklisted(&self) -> bool {
710 self.local_trust_state() == LocalTrust::BlackListed
711 }
712
713 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 pub fn was_withheld_code_sent(&self) -> bool {
728 self.withheld_code_sent.load(Ordering::Relaxed)
729 }
730
731 pub fn algorithms(&self) -> &[EventEncryptionAlgorithm] {
733 &self.device_keys.algorithms
734 }
735
736 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 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 #[cfg(feature = "experimental-algorithms")]
773 pub fn supports_olm_v2(&self) -> bool {
774 self.algorithms().contains(&EventEncryptionAlgorithm::OlmV2Curve25519AesSha2)
775 }
776
777 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 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 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 UserIdentityData::Own(identity) => identity.is_device_signed(self),
847 UserIdentityData::Other(device_identity) => device_identity.is_device_signed(self),
850 }
851 }
852
853 #[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 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 Ok(false)
962 }
963 }
964
965 pub fn as_device_keys(&self) -> &DeviceKeys {
967 &self.device_keys
968 }
969
970 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 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 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 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 pub fn first_time_seen_ts(&self) -> MilliSecondsSinceUnixEpoch {
1036 self.first_time_seen_ts
1037 }
1038
1039 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#[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 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 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 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 #[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 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 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 assert!(!device.is_cross_signing_trusted());
1266
1267 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 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}