1use std::{
16 collections::{BTreeMap, HashMap},
17 fmt,
18 ops::{Deref, Not as _},
19 sync::Arc,
20 time::Duration,
21};
22
23use hkdf::Hkdf;
24use js_option::JsOption;
25use matrix_sdk_common::deserialized_responses::{
26 AlgorithmInfo, DeviceLinkProblem, EncryptionInfo, VerificationLevel, VerificationState,
27};
28#[cfg(test)]
29use ruma::api::client::dehydrated_device::DehydratedDeviceV1;
30use ruma::{
31 CanonicalJsonValue, DeviceId, DeviceKeyAlgorithm, DeviceKeyId, MilliSecondsSinceUnixEpoch,
32 OneTimeKeyAlgorithm, OneTimeKeyId, OwnedDeviceId, OwnedDeviceKeyId, OwnedOneTimeKeyId,
33 OwnedUserId, RoomId, SecondsSinceUnixEpoch, UInt, UserId,
34 api::client::{
35 dehydrated_device::{DehydratedDeviceData, DehydratedDeviceV2},
36 keys::{
37 upload_keys,
38 upload_signatures::v3::{Request as SignatureUploadRequest, SignedKeys},
39 },
40 },
41 canonical_json::to_canonical_value,
42 events::{AnyToDeviceEvent, room::history_visibility::HistoryVisibility},
43 serde::Raw,
44};
45use serde::{Deserialize, Serialize, de::Error};
46use serde_json::value::{RawValue as RawJsonValue, to_raw_value};
47use sha2::{Digest, Sha256};
48use tokio::sync::Mutex;
49use tracing::{Span, debug, field::debug, info, instrument, trace, warn};
50use vodozemac::{
51 Curve25519PublicKey, Ed25519Signature, KeyId, PickleError, base64_encode,
52 olm::{
53 Account as InnerAccount, AccountPickle, IdentityKeys, OlmMessage,
54 OneTimeKeyGenerationResult, PreKeyMessage, SessionConfig,
55 },
56};
57
58use super::{
59 EncryptionSettings, InboundGroupSession, OutboundGroupSession, PrivateCrossSigningIdentity,
60 Session, SessionCreationError as MegolmSessionCreationError, utility::SignJson,
61};
62#[cfg(feature = "experimental-algorithms")]
63use crate::types::events::room::encrypted::OlmV2Curve25519AesSha2Content;
64use crate::{
65 DecryptionSettings, Device, OlmError, SignatureError, TrustRequirement,
66 dehydrated_devices::DehydrationError,
67 error::{EventError, OlmResult, SessionCreationError},
68 identities::DeviceData,
69 olm::SenderData,
70 store::{
71 Store,
72 types::{Changes, DeviceChanges},
73 },
74 types::{
75 CrossSigningKey, DeviceKeys, EventEncryptionAlgorithm, MasterPubkey, OneTimeKey, SignedKey,
76 events::{
77 olm_v1::AnyDecryptedOlmEvent,
78 room::encrypted::{
79 EncryptedToDeviceEvent, OlmV1Curve25519AesSha2Content,
80 ToDeviceEncryptedEventContent,
81 },
82 },
83 requests::UploadSigningKeysRequest,
84 },
85};
86
87#[derive(Debug)]
88enum PrekeyBundle {
89 Olm3DH { key: SignedKey },
90}
91
92#[derive(Debug, Clone)]
93pub(crate) enum SessionType {
94 New(Session),
95 Existing(Session),
96}
97
98#[derive(Debug)]
99pub struct InboundCreationResult {
100 pub session: Session,
101 pub plaintext: String,
102}
103
104impl SessionType {
105 #[cfg(test)]
106 pub fn session(self) -> Session {
107 match self {
108 SessionType::New(s) => s,
109 SessionType::Existing(s) => s,
110 }
111 }
112}
113
114#[derive(Debug)]
120pub(crate) struct OlmDecryptionInfo {
121 pub session: SessionType,
122 pub message_hash: OlmMessageHash,
123 pub inbound_group_session: Option<InboundGroupSession>,
124 pub result: DecryptionResult,
125}
126
127#[derive(Debug)]
128pub(crate) struct DecryptionResult {
129 pub event: Box<AnyDecryptedOlmEvent>,
131 pub raw_event: Raw<AnyToDeviceEvent>,
132 pub sender_key: Curve25519PublicKey,
133 pub encryption_info: EncryptionInfo,
134}
135
136#[derive(Debug, Clone, Serialize, Deserialize)]
140pub struct OlmMessageHash {
141 pub sender_key: String,
143 pub hash: String,
145}
146
147impl OlmMessageHash {
148 fn new(sender_key: Curve25519PublicKey, ciphertext: &OlmMessage) -> Self {
149 let (message_type, ciphertext) = ciphertext.clone().to_parts();
150 let sender_key = sender_key.to_base64();
151
152 let sha = Sha256::new()
153 .chain_update(sender_key.as_bytes())
154 .chain_update([message_type as u8])
155 .chain_update(ciphertext)
156 .finalize();
157
158 Self { sender_key, hash: base64_encode(sha.as_slice()) }
159 }
160}
161
162#[derive(Clone)]
167#[cfg_attr(not(tarpaulin_include), derive(Debug))]
168pub struct StaticAccountData {
169 pub user_id: OwnedUserId,
171 pub device_id: OwnedDeviceId,
173 pub identity_keys: Arc<IdentityKeys>,
175 pub dehydrated: bool,
177 creation_local_time: MilliSecondsSinceUnixEpoch,
179}
180
181impl StaticAccountData {
182 const ALGORITHMS: &'static [&'static EventEncryptionAlgorithm] = &[
183 &EventEncryptionAlgorithm::OlmV1Curve25519AesSha2,
184 #[cfg(feature = "experimental-algorithms")]
185 &EventEncryptionAlgorithm::OlmV2Curve25519AesSha2,
186 &EventEncryptionAlgorithm::MegolmV1AesSha2,
187 #[cfg(feature = "experimental-algorithms")]
188 &EventEncryptionAlgorithm::MegolmV2AesSha2,
189 ];
190
191 pub async fn create_group_session_pair(
206 &self,
207 room_id: &RoomId,
208 settings: EncryptionSettings,
209 own_sender_data: SenderData,
210 ) -> Result<(OutboundGroupSession, InboundGroupSession), MegolmSessionCreationError> {
211 trace!(?room_id, algorithm = settings.algorithm.as_str(), "Creating a new room key");
212
213 let visibility = settings.history_visibility.clone();
214 let algorithm = settings.algorithm.to_owned();
215
216 let outbound = OutboundGroupSession::new(
217 self.device_id.clone(),
218 self.identity_keys.clone(),
219 room_id,
220 settings,
221 )?;
222
223 let identity_keys = &self.identity_keys;
224
225 let sender_key = identity_keys.curve25519;
226 let signing_key = identity_keys.ed25519;
227 let shared_history = shared_history_from_history_visibility(&visibility);
228
229 let inbound = InboundGroupSession::new(
230 sender_key,
231 signing_key,
232 room_id,
233 &outbound.session_key().await,
234 own_sender_data,
235 None,
236 algorithm,
237 Some(visibility),
238 shared_history,
239 )?;
240
241 Ok((outbound, inbound))
242 }
243
244 #[cfg(any(test, feature = "testing"))]
245 #[allow(dead_code)]
246 pub async fn create_group_session_pair_with_defaults(
249 &self,
250 room_id: &RoomId,
251 ) -> (OutboundGroupSession, InboundGroupSession) {
252 self.create_group_session_pair(
253 room_id,
254 EncryptionSettings::default(),
255 SenderData::unknown(),
256 )
257 .await
258 .expect("Can't create default group session pair")
259 }
260
261 pub fn signing_key_id(&self) -> OwnedDeviceKeyId {
263 DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, self.device_id())
264 }
265
266 pub fn has_signed_raw(
276 &self,
277 signatures: &crate::types::Signatures,
278 canonical_json: &str,
279 ) -> Result<(), SignatureError> {
280 use crate::olm::utility::VerifyJson;
281
282 let signing_key = self.identity_keys.ed25519;
283
284 signing_key.verify_canonicalized_json(
285 &self.user_id,
286 &DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, self.device_id()),
287 signatures,
288 canonical_json,
289 )
290 }
291
292 pub fn unsigned_device_keys(&self) -> DeviceKeys {
294 let identity_keys = self.identity_keys();
295 let keys = BTreeMap::from([
296 (
297 DeviceKeyId::from_parts(DeviceKeyAlgorithm::Curve25519, &self.device_id),
298 identity_keys.curve25519.into(),
299 ),
300 (
301 DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, &self.device_id),
302 identity_keys.ed25519.into(),
303 ),
304 ]);
305
306 let mut ret = DeviceKeys::new(
307 (*self.user_id).to_owned(),
308 (*self.device_id).to_owned(),
309 Self::ALGORITHMS.iter().map(|a| (**a).clone()).collect(),
310 keys,
311 Default::default(),
312 );
313 if self.dehydrated {
314 ret.dehydrated = JsOption::Some(true);
315 }
316 ret
317 }
318
319 pub fn user_id(&self) -> &UserId {
321 &self.user_id
322 }
323
324 pub fn device_id(&self) -> &DeviceId {
326 &self.device_id
327 }
328
329 pub fn identity_keys(&self) -> IdentityKeys {
331 *self.identity_keys
332 }
333
334 pub fn creation_local_time(&self) -> MilliSecondsSinceUnixEpoch {
336 self.creation_local_time
337 }
338}
339
340pub struct Account {
345 pub(crate) static_data: StaticAccountData,
346 inner: Box<InnerAccount>,
348 shared: bool,
351 uploaded_signed_key_count: u64,
356 fallback_creation_timestamp: Option<MilliSecondsSinceUnixEpoch>,
364}
365
366impl Deref for Account {
367 type Target = StaticAccountData;
368
369 fn deref(&self) -> &Self::Target {
370 &self.static_data
371 }
372}
373
374#[derive(Serialize, Deserialize)]
379#[allow(missing_debug_implementations)]
380pub struct PickledAccount {
381 pub user_id: OwnedUserId,
383 pub device_id: OwnedDeviceId,
385 pub pickle: AccountPickle,
387 pub shared: bool,
389 #[serde(default)]
391 pub dehydrated: bool,
392 pub uploaded_signed_key_count: u64,
394 #[serde(default = "default_account_creation_time")]
397 pub creation_local_time: MilliSecondsSinceUnixEpoch,
398 #[serde(default)]
400 pub fallback_key_creation_timestamp: Option<MilliSecondsSinceUnixEpoch>,
401}
402
403fn default_account_creation_time() -> MilliSecondsSinceUnixEpoch {
404 MilliSecondsSinceUnixEpoch(UInt::default())
405}
406
407#[cfg(not(tarpaulin_include))]
408impl fmt::Debug for Account {
409 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
410 f.debug_struct("Account")
411 .field("identity_keys", &self.identity_keys())
412 .field("shared", &self.shared())
413 .finish()
414 }
415}
416
417pub type OneTimeKeys = BTreeMap<OwnedOneTimeKeyId, Raw<ruma::encryption::OneTimeKey>>;
418pub type FallbackKeys = OneTimeKeys;
419
420impl Account {
421 pub(crate) fn new_helper(
422 mut account: InnerAccount,
423 user_id: &UserId,
424 device_id: &DeviceId,
425 ) -> Self {
426 let identity_keys = account.identity_keys();
427
428 account.generate_one_time_keys(account.max_number_of_one_time_keys());
440
441 Self {
442 static_data: StaticAccountData {
443 user_id: user_id.into(),
444 device_id: device_id.into(),
445 identity_keys: Arc::new(identity_keys),
446 dehydrated: false,
447 creation_local_time: MilliSecondsSinceUnixEpoch::now(),
448 },
449 inner: Box::new(account),
450 shared: false,
451 uploaded_signed_key_count: 0,
452 fallback_creation_timestamp: None,
453 }
454 }
455
456 pub fn with_device_id(user_id: &UserId, device_id: &DeviceId) -> Self {
458 let account = InnerAccount::new();
459
460 Self::new_helper(account, user_id, device_id)
461 }
462
463 pub fn new(user_id: &UserId) -> Self {
466 let account = InnerAccount::new();
467 let device_id: OwnedDeviceId =
468 base64_encode(account.identity_keys().curve25519.as_bytes()).into();
469
470 Self::new_helper(account, user_id, &device_id)
471 }
472
473 pub fn new_dehydrated(user_id: &UserId) -> Self {
475 let account = InnerAccount::new();
476 let device_id: OwnedDeviceId =
477 base64_encode(account.identity_keys().curve25519.as_bytes()).into();
478
479 let mut ret = Self::new_helper(account, user_id, &device_id);
480 ret.static_data.dehydrated = true;
481 ret
482 }
483
484 pub fn static_data(&self) -> &StaticAccountData {
486 &self.static_data
487 }
488
489 pub fn update_uploaded_key_count(&mut self, new_count: u64) {
495 self.uploaded_signed_key_count = new_count;
496 }
497
498 pub fn uploaded_key_count(&self) -> u64 {
500 self.uploaded_signed_key_count
501 }
502
503 pub fn shared(&self) -> bool {
505 self.shared
506 }
507
508 pub fn mark_as_shared(&mut self) {
513 self.shared = true;
514 }
515
516 pub fn one_time_keys(&self) -> HashMap<KeyId, Curve25519PublicKey> {
520 self.inner.one_time_keys()
521 }
522
523 pub fn generate_one_time_keys(&mut self, count: usize) -> OneTimeKeyGenerationResult {
525 self.inner.generate_one_time_keys(count)
526 }
527
528 pub fn max_one_time_keys(&self) -> usize {
530 self.inner.max_number_of_one_time_keys()
531 }
532
533 pub(crate) fn update_key_counts(
534 &mut self,
535 one_time_key_counts: &BTreeMap<OneTimeKeyAlgorithm, UInt>,
536 unused_fallback_keys: Option<&[OneTimeKeyAlgorithm]>,
537 ) {
538 if let Some(count) = one_time_key_counts.get(&OneTimeKeyAlgorithm::SignedCurve25519) {
539 let count: u64 = (*count).into();
540 let old_count = self.uploaded_key_count();
541
542 if count != old_count {
546 debug!(
547 "Updated uploaded one-time key count {} -> {count}.",
548 self.uploaded_key_count(),
549 );
550 }
551
552 self.update_uploaded_key_count(count);
553 self.generate_one_time_keys_if_needed();
554 }
555
556 if unused_fallback_keys.is_some() || self.fallback_creation_timestamp.is_some() {
560 self.generate_fallback_key_if_needed();
561 }
562 }
563
564 #[instrument(skip_all)]
573 pub fn generate_one_time_keys_if_needed(&mut self) -> Option<u64> {
574 if !self.one_time_keys().is_empty() {
578 return Some(0);
579 }
580
581 let count = self.uploaded_key_count();
582 let max_keys = self.max_one_time_keys();
583
584 if count >= max_keys as u64 {
585 return None;
586 }
587
588 let key_count = (max_keys as u64) - count;
589 let key_count: usize = key_count.try_into().unwrap_or(max_keys);
590
591 let result = self.generate_one_time_keys(key_count);
592
593 debug!(
594 count = key_count,
595 discarded_keys = ?result.removed,
596 created_keys = ?result.created,
597 "Generated new one-time keys"
598 );
599
600 Some(key_count as u64)
601 }
602
603 pub(crate) fn generate_fallback_key_if_needed(&mut self) {
610 if self.inner.fallback_key().is_empty() && self.fallback_key_expired() {
611 let removed_fallback_key = self.inner.generate_fallback_key();
612 self.fallback_creation_timestamp = Some(MilliSecondsSinceUnixEpoch::now());
613
614 debug!(
615 ?removed_fallback_key,
616 "The fallback key either expired or we didn't have one: generated a new fallback key.",
617 );
618 }
619 }
620
621 fn fallback_key_expired(&self) -> bool {
629 const FALLBACK_KEY_MAX_AGE: Duration = Duration::from_secs(3600 * 24 * 7);
630
631 if let Some(time) = self.fallback_creation_timestamp {
632 let Some(system_time) = time.to_system_time() else {
636 return true;
637 };
638
639 let Ok(elapsed) = system_time.elapsed() else {
643 return true;
644 };
645
646 elapsed > FALLBACK_KEY_MAX_AGE
651 } else {
652 true
655 }
656 }
657
658 fn fallback_key(&self) -> HashMap<KeyId, Curve25519PublicKey> {
659 self.inner.fallback_key()
660 }
661
662 pub fn keys_for_upload(&self) -> (Option<DeviceKeys>, OneTimeKeys, FallbackKeys) {
668 let device_keys = self.shared().not().then(|| self.device_keys());
669
670 let one_time_keys = self.signed_one_time_keys();
671 let fallback_keys = self.signed_fallback_keys();
672
673 (device_keys, one_time_keys, fallback_keys)
674 }
675
676 pub fn mark_keys_as_published(&mut self) {
678 self.inner.mark_keys_as_published();
679 }
680
681 pub fn sign(&self, string: &str) -> Ed25519Signature {
685 self.inner.sign(string)
686 }
687
688 pub fn pickle(&self) -> PickledAccount {
690 let pickle = self.inner.pickle();
691
692 PickledAccount {
693 user_id: self.user_id().to_owned(),
694 device_id: self.device_id().to_owned(),
695 pickle,
696 shared: self.shared(),
697 dehydrated: self.static_data.dehydrated,
698 uploaded_signed_key_count: self.uploaded_key_count(),
699 creation_local_time: self.static_data.creation_local_time,
700 fallback_key_creation_timestamp: self.fallback_creation_timestamp,
701 }
702 }
703
704 pub(crate) fn dehydrate(&self, pickle_key: &[u8; 32]) -> Raw<DehydratedDeviceData> {
705 let dehydration_result = self
706 .inner
707 .to_dehydrated_device(pickle_key)
708 .expect("We should be able to convert a freshly created Account into a libolm pickle");
709
710 let data = DehydratedDeviceData::V2(DehydratedDeviceV2::new(
711 dehydration_result.ciphertext,
712 dehydration_result.nonce,
713 ));
714 Raw::from_json(to_raw_value(&data).expect("Couldn't serialize our dehydrated device data"))
715 }
716
717 pub(crate) fn rehydrate(
718 pickle_key: &[u8; 32],
719 user_id: &UserId,
720 device_id: &DeviceId,
721 device_data: Raw<DehydratedDeviceData>,
722 ) -> Result<Self, DehydrationError> {
723 let data = device_data.deserialize()?;
724
725 match data {
726 DehydratedDeviceData::V1(d) => {
727 let pickle_key = expand_legacy_pickle_key(pickle_key, device_id);
728 let account =
729 InnerAccount::from_libolm_pickle(&d.device_pickle, pickle_key.as_ref())?;
730 Ok(Self::new_helper(account, user_id, device_id))
731 }
732 DehydratedDeviceData::V2(d) => {
733 let account =
734 InnerAccount::from_dehydrated_device(&d.device_pickle, &d.nonce, pickle_key)?;
735 Ok(Self::new_helper(account, user_id, device_id))
736 }
737 _ => Err(DehydrationError::Json(serde_json::Error::custom(format!(
738 "Unsupported dehydrated device algorithm {:?}",
739 data.algorithm()
740 )))),
741 }
742 }
743
744 #[cfg(test)]
747 pub(crate) fn legacy_dehydrate(&self, pickle_key: &[u8; 32]) -> Raw<DehydratedDeviceData> {
748 let pickle_key = expand_legacy_pickle_key(pickle_key, &self.device_id);
749 let device_pickle = self
750 .inner
751 .to_libolm_pickle(pickle_key.as_ref())
752 .expect("We should be able to convert a freshly created Account into a libolm pickle");
753
754 let data = DehydratedDeviceData::V1(DehydratedDeviceV1::new(device_pickle));
755 Raw::from_json(to_raw_value(&data).expect("Couldn't serialize our dehydrated device data"))
756 }
757
758 pub fn from_pickle(pickle: PickledAccount) -> Result<Self, PickleError> {
767 let account: vodozemac::olm::Account = pickle.pickle.into();
768 let identity_keys = account.identity_keys();
769
770 Ok(Self {
771 static_data: StaticAccountData {
772 user_id: (*pickle.user_id).into(),
773 device_id: (*pickle.device_id).into(),
774 identity_keys: Arc::new(identity_keys),
775 dehydrated: pickle.dehydrated,
776 creation_local_time: pickle.creation_local_time,
777 },
778 inner: Box::new(account),
779 shared: pickle.shared,
780 uploaded_signed_key_count: pickle.uploaded_signed_key_count,
781 fallback_creation_timestamp: pickle.fallback_key_creation_timestamp,
782 })
783 }
784
785 pub fn device_keys(&self) -> DeviceKeys {
788 let mut device_keys = self.unsigned_device_keys();
789
790 let json_device_keys =
793 to_canonical_value(&device_keys).expect("device key is always safe to serialize");
794 let signature = self
795 .sign_json(json_device_keys)
796 .expect("Newly created device keys can always be signed");
797
798 device_keys.signatures.add_signature(
799 self.user_id().to_owned(),
800 DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, &self.static_data.device_id),
801 signature,
802 );
803
804 device_keys
805 }
806
807 pub async fn bootstrap_cross_signing(
819 &self,
820 ) -> (PrivateCrossSigningIdentity, UploadSigningKeysRequest, SignatureUploadRequest) {
821 let identity = PrivateCrossSigningIdentity::for_account(self);
822
823 let signature_request = identity
824 .sign_account(self.static_data())
825 .await
826 .expect("Can't sign own device with new cross signing keys");
827
828 let upload_request = identity.as_upload_request().await;
829
830 (identity, upload_request, signature_request)
831 }
832
833 pub fn sign_cross_signing_key(
835 &self,
836 cross_signing_key: &mut CrossSigningKey,
837 ) -> Result<(), SignatureError> {
838 let signature = self.sign_json(to_canonical_value(&cross_signing_key)?)?;
839
840 cross_signing_key.signatures.add_signature(
841 self.user_id().to_owned(),
842 DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, self.device_id()),
843 signature,
844 );
845
846 Ok(())
847 }
848
849 pub fn sign_master_key(
851 &self,
852 master_key: &MasterPubkey,
853 ) -> Result<SignatureUploadRequest, SignatureError> {
854 let public_key =
855 master_key.get_first_key().ok_or(SignatureError::MissingSigningKey)?.to_base64().into();
856
857 let mut cross_signing_key: CrossSigningKey = master_key.as_ref().clone();
858 cross_signing_key.signatures.clear();
859 self.sign_cross_signing_key(&mut cross_signing_key)?;
860
861 let mut user_signed_keys = SignedKeys::new();
862 user_signed_keys.add_cross_signing_keys(public_key, cross_signing_key.to_raw());
863
864 let signed_keys = [(self.user_id().to_owned(), user_signed_keys)].into();
865 Ok(SignatureUploadRequest::new(signed_keys))
866 }
867
868 pub fn sign_json(&self, json: CanonicalJsonValue) -> Result<Ed25519Signature, SignatureError> {
876 self.inner.sign_json(json)
877 }
878
879 pub fn signed_one_time_keys(&self) -> OneTimeKeys {
883 let one_time_keys = self.one_time_keys();
884
885 if one_time_keys.is_empty() {
886 BTreeMap::new()
887 } else {
888 self.signed_keys(one_time_keys, false)
889 }
890 }
891
892 pub fn signed_fallback_keys(&self) -> FallbackKeys {
896 let fallback_key = self.fallback_key();
897
898 if fallback_key.is_empty() { BTreeMap::new() } else { self.signed_keys(fallback_key, true) }
899 }
900
901 fn signed_keys(
902 &self,
903 keys: HashMap<KeyId, Curve25519PublicKey>,
904 fallback: bool,
905 ) -> OneTimeKeys {
906 let mut keys_map = BTreeMap::new();
907
908 for (key_id, key) in keys {
909 let signed_key = self.sign_key(key, fallback);
910
911 keys_map.insert(
912 OneTimeKeyId::from_parts(
913 OneTimeKeyAlgorithm::SignedCurve25519,
914 key_id.to_base64().as_str().into(),
915 ),
916 signed_key.into_raw(),
917 );
918 }
919
920 keys_map
921 }
922
923 fn sign_key(&self, key: Curve25519PublicKey, fallback: bool) -> SignedKey {
924 let mut key = if fallback {
925 SignedKey::new_fallback(key.to_owned())
926 } else {
927 SignedKey::new(key.to_owned())
928 };
929
930 let signature = self
931 .sign_json(to_canonical_value(&key).expect("Can't serialize a signed key"))
932 .expect("Newly created one-time keys can always be signed");
933
934 key.signatures_mut().add_signature(
935 self.user_id().to_owned(),
936 DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, self.device_id()),
937 signature,
938 );
939
940 key
941 }
942
943 pub fn create_outbound_session_helper(
963 &self,
964 config: SessionConfig,
965 identity_key: Curve25519PublicKey,
966 one_time_key: Curve25519PublicKey,
967 fallback_used: bool,
968 our_device_keys: DeviceKeys,
969 ) -> Session {
970 let session = self.inner.create_outbound_session(config, identity_key, one_time_key);
971
972 let now = SecondsSinceUnixEpoch::now();
973 let session_id = session.session_id();
974
975 Session {
976 inner: Arc::new(Mutex::new(session)),
977 session_id: session_id.into(),
978 sender_key: identity_key,
979 our_device_keys,
980 created_using_fallback_key: fallback_used,
981 creation_time: now,
982 last_use_time: now,
983 }
984 }
985
986 #[instrument(
987 skip_all,
988 fields(
989 user_id = ?device.user_id(),
990 device_id = ?device.device_id(),
991 algorithms = ?device.algorithms()
992 )
993 )]
994 fn find_pre_key_bundle(
995 device: &DeviceData,
996 key_map: &OneTimeKeys,
997 ) -> Result<PrekeyBundle, SessionCreationError> {
998 let mut keys = key_map.iter();
999
1000 let first_key = keys.next().ok_or_else(|| {
1001 SessionCreationError::OneTimeKeyMissing(
1002 device.user_id().to_owned(),
1003 device.device_id().into(),
1004 )
1005 })?;
1006
1007 let first_key_id = first_key.0.to_owned();
1008 let first_key = OneTimeKey::deserialize(first_key_id.algorithm(), first_key.1)?;
1009
1010 let result = match first_key {
1011 OneTimeKey::SignedKey(key) => Ok(PrekeyBundle::Olm3DH { key }),
1012 };
1013
1014 trace!(?result, "Finished searching for a valid pre-key bundle");
1015
1016 result
1017 }
1018
1019 #[allow(clippy::result_large_err)]
1034 pub fn create_outbound_session(
1035 &self,
1036 device: &DeviceData,
1037 key_map: &OneTimeKeys,
1038 our_device_keys: DeviceKeys,
1039 ) -> Result<Session, SessionCreationError> {
1040 let pre_key_bundle = Self::find_pre_key_bundle(device, key_map)?;
1041
1042 match pre_key_bundle {
1043 PrekeyBundle::Olm3DH { key } => {
1044 device.verify_one_time_key(&key).map_err(|error| {
1045 SessionCreationError::InvalidSignature {
1046 signing_key: device.ed25519_key().map(Box::new),
1047 one_time_key: key.clone().into(),
1048 error: error.into(),
1049 }
1050 })?;
1051
1052 let identity_key = device.curve25519_key().ok_or_else(|| {
1053 SessionCreationError::DeviceMissingCurveKey(
1054 device.user_id().to_owned(),
1055 device.device_id().into(),
1056 )
1057 })?;
1058
1059 let is_fallback = key.fallback();
1060 let one_time_key = key.key();
1061 let config = device.olm_session_config();
1062
1063 Ok(self.create_outbound_session_helper(
1064 config,
1065 identity_key,
1066 one_time_key,
1067 is_fallback,
1068 our_device_keys,
1069 ))
1070 }
1071 }
1072 }
1073
1074 pub fn create_inbound_session(
1089 &mut self,
1090 their_identity_key: Curve25519PublicKey,
1091 our_device_keys: DeviceKeys,
1092 message: &PreKeyMessage,
1093 ) -> Result<InboundCreationResult, SessionCreationError> {
1094 Span::current().record("session_id", debug(message.session_id()));
1095 trace!("Creating a new Olm session from a pre-key message");
1096
1097 let result = self.inner.create_inbound_session(their_identity_key, message)?;
1098 let now = SecondsSinceUnixEpoch::now();
1099 let session_id = result.session.session_id();
1100
1101 debug!(session=?result.session, "Decrypted an Olm message from a new Olm session");
1102
1103 let session = Session {
1104 inner: Arc::new(Mutex::new(result.session)),
1105 session_id: session_id.into(),
1106 sender_key: their_identity_key,
1107 our_device_keys,
1108 created_using_fallback_key: false,
1109 creation_time: now,
1110 last_use_time: now,
1111 };
1112
1113 let plaintext = String::from_utf8_lossy(&result.plaintext).to_string();
1114
1115 Ok(InboundCreationResult { session, plaintext })
1116 }
1117
1118 #[cfg(any(test, feature = "testing"))]
1119 #[allow(dead_code)]
1120 pub async fn create_session_for_test_helper(
1122 &mut self,
1123 other: &mut Account,
1124 ) -> (Session, Session) {
1125 use ruma::events::dummy::ToDeviceDummyEventContent;
1126
1127 other.generate_one_time_keys(1);
1128 let one_time_map = other.signed_one_time_keys();
1129 let device = DeviceData::from_account(other);
1130
1131 let mut our_session =
1132 self.create_outbound_session(&device, &one_time_map, self.device_keys()).unwrap();
1133
1134 other.mark_keys_as_published();
1135
1136 let message = our_session
1137 .encrypt(&device, "m.dummy", ToDeviceDummyEventContent::new(), None)
1138 .await
1139 .unwrap()
1140 .deserialize()
1141 .unwrap();
1142
1143 #[cfg(feature = "experimental-algorithms")]
1144 let content = if let ToDeviceEncryptedEventContent::OlmV2Curve25519AesSha2(c) = message {
1145 c
1146 } else {
1147 panic!("Invalid encrypted event algorithm {}", message.algorithm());
1148 };
1149
1150 #[cfg(not(feature = "experimental-algorithms"))]
1151 let ToDeviceEncryptedEventContent::OlmV1Curve25519AesSha2(content) = message else {
1152 panic!("Invalid encrypted event algorithm {}", message.algorithm());
1153 };
1154
1155 let OlmMessage::PreKey(prekey) = content.ciphertext else {
1156 panic!("Wrong Olm message type");
1157 };
1158
1159 let our_device = DeviceData::from_account(self);
1160 let other_session = other
1161 .create_inbound_session(
1162 our_device.curve25519_key().unwrap(),
1163 other.device_keys(),
1164 &prekey,
1165 )
1166 .unwrap();
1167
1168 (our_session, other_session.session)
1169 }
1170
1171 async fn decrypt_olm_helper(
1172 &mut self,
1173 store: &Store,
1174 sender: &UserId,
1175 sender_key: Curve25519PublicKey,
1176 ciphertext: &OlmMessage,
1177 decryption_settings: &DecryptionSettings,
1178 ) -> OlmResult<OlmDecryptionInfo> {
1179 let message_hash = OlmMessageHash::new(sender_key, ciphertext);
1180
1181 match self
1182 .decrypt_and_parse_olm_message(
1183 store,
1184 sender,
1185 sender_key,
1186 ciphertext,
1187 decryption_settings,
1188 )
1189 .await
1190 {
1191 Ok((session, result)) => {
1192 Ok(OlmDecryptionInfo { session, message_hash, result, inbound_group_session: None })
1193 }
1194 Err(OlmError::SessionWedged(user_id, sender_key)) => {
1195 if store.is_message_known(&message_hash).await? {
1196 info!(?sender_key, "An Olm message got replayed, decryption failed");
1197 Err(OlmError::ReplayedMessage(user_id, sender_key))
1198 } else {
1199 Err(OlmError::SessionWedged(user_id, sender_key))
1200 }
1201 }
1202 Err(e) => Err(e),
1203 }
1204 }
1205
1206 #[cfg(feature = "experimental-algorithms")]
1207 async fn decrypt_olm_v2(
1208 &mut self,
1209 store: &Store,
1210 sender: &UserId,
1211 content: &OlmV2Curve25519AesSha2Content,
1212 decryption_settings: &DecryptionSettings,
1213 ) -> OlmResult<OlmDecryptionInfo> {
1214 self.decrypt_olm_helper(
1215 store,
1216 sender,
1217 content.sender_key,
1218 &content.ciphertext,
1219 decryption_settings,
1220 )
1221 .await
1222 }
1223
1224 #[instrument(skip_all, fields(sender, sender_key = ?content.sender_key))]
1225 async fn decrypt_olm_v1(
1226 &mut self,
1227 store: &Store,
1228 sender: &UserId,
1229 content: &OlmV1Curve25519AesSha2Content,
1230 decryption_settings: &DecryptionSettings,
1231 ) -> OlmResult<OlmDecryptionInfo> {
1232 if content.recipient_key != self.static_data.identity_keys.curve25519 {
1233 warn!("Olm event doesn't contain a ciphertext for our key");
1234
1235 Err(EventError::MissingCiphertext.into())
1236 } else {
1237 Box::pin(self.decrypt_olm_helper(
1238 store,
1239 sender,
1240 content.sender_key,
1241 &content.ciphertext,
1242 decryption_settings,
1243 ))
1244 .await
1245 }
1246 }
1247
1248 #[instrument(skip_all, fields(algorithm = ?event.content.algorithm()))]
1249 pub(crate) async fn decrypt_to_device_event(
1250 &mut self,
1251 store: &Store,
1252 event: &EncryptedToDeviceEvent,
1253 decryption_settings: &DecryptionSettings,
1254 ) -> OlmResult<OlmDecryptionInfo> {
1255 trace!("Decrypting a to-device event");
1256
1257 match &event.content {
1258 ToDeviceEncryptedEventContent::OlmV1Curve25519AesSha2(c) => {
1259 self.decrypt_olm_v1(store, &event.sender, c, decryption_settings).await
1260 }
1261 #[cfg(feature = "experimental-algorithms")]
1262 ToDeviceEncryptedEventContent::OlmV2Curve25519AesSha2(c) => {
1263 self.decrypt_olm_v2(store, &event.sender, c, decryption_settings).await
1264 }
1265 ToDeviceEncryptedEventContent::Unknown(_) => {
1266 warn!(
1267 "Error decrypting an to-device event, unsupported \
1268 encryption algorithm"
1269 );
1270
1271 Err(EventError::UnsupportedAlgorithm.into())
1272 }
1273 }
1274 }
1275
1276 pub fn receive_keys_upload_response(
1278 &mut self,
1279 response: &upload_keys::v3::Response,
1280 ) -> OlmResult<()> {
1281 if !self.shared() {
1282 debug!("Marking account as shared");
1283 }
1284 self.mark_as_shared();
1285
1286 debug!("Marking one-time keys as published");
1287 self.mark_keys_as_published();
1290 self.update_key_counts(&response.one_time_key_counts, None);
1291
1292 Ok(())
1293 }
1294
1295 async fn decrypt_olm_message(
1297 &mut self,
1298 store: &Store,
1299 sender: &UserId,
1300 sender_key: Curve25519PublicKey,
1301 message: &OlmMessage,
1302 ) -> Result<(SessionType, String), OlmError> {
1303 let existing_sessions = store.get_sessions(&sender_key.to_base64()).await?;
1304
1305 match message {
1306 OlmMessage::Normal(_) => {
1307 let mut errors_by_olm_session = Vec::new();
1308
1309 if let Some(sessions) = existing_sessions {
1310 for session in sessions.lock().await.iter_mut() {
1313 match session.decrypt(message).await {
1314 Ok(p) => {
1315 return Ok((SessionType::Existing(session.clone()), p));
1317 }
1318
1319 Err(e) => {
1320 errors_by_olm_session.push((session.session_id().to_owned(), e));
1325 }
1326 }
1327 }
1328 }
1329
1330 warn!(
1331 ?errors_by_olm_session,
1332 "Failed to decrypt a non-pre-key message with all available sessions"
1333 );
1334 Err(OlmError::SessionWedged(sender.to_owned(), sender_key))
1335 }
1336
1337 OlmMessage::PreKey(prekey_message) => {
1338 if let Some(sessions) = existing_sessions {
1340 for session in sessions.lock().await.iter_mut() {
1341 if prekey_message.session_id() != session.session_id() {
1342 continue;
1344 }
1345
1346 if let Ok(p) = session.decrypt(message).await {
1347 return Ok((SessionType::Existing(session.clone()), p));
1349 }
1350
1351 warn!(
1365 session_id = session.session_id(),
1366 "Failed to decrypt a pre-key message with the corresponding session"
1367 );
1368
1369 return Err(OlmError::SessionWedged(
1370 session.our_device_keys.user_id.to_owned(),
1371 session.sender_key(),
1372 ));
1373 }
1374 }
1375
1376 let device_keys = store.get_own_device().await?.as_device_keys().clone();
1377 let result =
1378 match self.create_inbound_session(sender_key, device_keys, prekey_message) {
1379 Ok(r) => r,
1380 Err(e) => {
1381 warn!(
1382 "Failed to create a new Olm session from a pre-key message: {e:?}"
1383 );
1384 return Err(OlmError::SessionWedged(sender.to_owned(), sender_key));
1385 }
1386 };
1387
1388 let mut changes =
1393 Changes { sessions: vec![result.session.clone()], ..Default::default() };
1394
1395 if let Some(device) = store.get_device_from_curve_key(sender, sender_key).await? {
1402 let mut device_data = device.inner;
1403 device_data.olm_wedging_index.increment();
1404
1405 changes.devices =
1406 DeviceChanges { changed: vec![device_data], ..Default::default() };
1407 }
1408
1409 store.save_changes(changes).await?;
1410
1411 Ok((SessionType::New(result.session), result.plaintext))
1412 }
1413 }
1414 }
1415
1416 #[instrument(skip(self, store), fields(session, session_id))]
1419 async fn decrypt_and_parse_olm_message(
1420 &mut self,
1421 store: &Store,
1422 sender: &UserId,
1423 sender_key: Curve25519PublicKey,
1424 message: &OlmMessage,
1425 decryption_settings: &DecryptionSettings,
1426 ) -> OlmResult<(SessionType, DecryptionResult)> {
1427 let (session, plaintext) =
1428 self.decrypt_olm_message(store, sender, sender_key, message).await?;
1429
1430 trace!("Successfully decrypted an Olm message");
1431
1432 match self
1433 .parse_decrypted_to_device_event(
1434 store,
1435 sender,
1436 sender_key,
1437 plaintext,
1438 decryption_settings,
1439 )
1440 .await
1441 {
1442 Ok(result) => Ok((session, result)),
1443 Err(e) => {
1444 match session {
1448 SessionType::New(s) | SessionType::Existing(s) => {
1449 store.save_sessions(&[s]).await?;
1450 }
1451 }
1452
1453 warn!(
1454 error = ?e,
1455 "A to-device message was successfully decrypted but \
1456 parsing and checking the event fields failed"
1457 );
1458
1459 Err(e)
1460 }
1461 }
1462 }
1463
1464 async fn parse_decrypted_to_device_event(
1485 &self,
1486 store: &Store,
1487 sender: &UserId,
1488 sender_key: Curve25519PublicKey,
1489 plaintext: String,
1490 decryption_settings: &DecryptionSettings,
1491 ) -> OlmResult<DecryptionResult> {
1492 let event: Box<AnyDecryptedOlmEvent> = serde_json::from_str(&plaintext)?;
1493 let identity_keys = &self.static_data.identity_keys;
1494
1495 if event.recipient() != self.static_data.user_id {
1496 Err(EventError::MismatchedSender(
1497 event.recipient().to_owned(),
1498 self.static_data.user_id.clone(),
1499 )
1500 .into())
1501 }
1502 else if event.sender() != sender {
1505 Err(EventError::MismatchedSender(event.sender().to_owned(), sender.to_owned()).into())
1506 } else if identity_keys.ed25519 != event.recipient_keys().ed25519 {
1507 Err(EventError::MismatchedKeys(
1508 identity_keys.ed25519.into(),
1509 event.recipient_keys().ed25519.into(),
1510 )
1511 .into())
1512 } else {
1513 let sender_device = Self::get_event_sender_device(store, sender_key, &event).await?;
1514 let encryption_info = Self::get_olm_encryption_info(sender_key, sender, &sender_device);
1515
1516 let result = DecryptionResult {
1517 event,
1518 raw_event: Raw::from_json(RawJsonValue::from_string(plaintext)?),
1519 sender_key,
1520 encryption_info,
1521 };
1522
1523 if !self.is_from_verified_device_or_allowed_type(decryption_settings, &result) {
1525 Err(OlmError::UnverifiedSenderDevice)
1526 } else {
1527 Ok(result)
1529 }
1530 }
1531 }
1532
1533 async fn get_event_sender_device(
1547 store: &Store,
1548 sender_key: Curve25519PublicKey,
1549 event: &AnyDecryptedOlmEvent,
1550 ) -> OlmResult<Option<Device>> {
1551 let sender_device_keys = Self::check_sender_device_keys(event, sender_key)?;
1556 if let AnyDecryptedOlmEvent::RoomKey(_) = event {
1557 return Ok(None);
1562 }
1563
1564 if let AnyDecryptedOlmEvent::RoomKeyBundle(_) = event {
1567 sender_device_keys.ok_or(EventError::MissingSigningKey).inspect_err(|_| {
1568 warn!("The room key bundle was missing the sender device keys in the event")
1569 })?;
1570 }
1571
1572 let store_device = store.get_device_from_curve_key(event.sender(), sender_key).await?;
1577
1578 match (store_device, sender_device_keys) {
1579 (Some(device), _) => {
1582 let key = device.ed25519_key().ok_or(EventError::MissingSigningKey)?;
1583 if key != event.keys().ed25519 {
1584 return Err(EventError::MismatchedKeys(
1585 key.into(),
1586 event.keys().ed25519.into(),
1587 )
1588 .into());
1589 }
1590 Ok(Some(device))
1591 }
1592
1593 (None, Some(sender_device_keys)) => {
1594 let sender_device_data = sender_device_keys.try_into().expect("Conversion of DeviceKeys to DeviceData failed despite the signature already having been checked");
1597 Ok(Some(store.wrap_device_data(sender_device_data).await?))
1598 }
1599
1600 (None, None) => Err(OlmError::EventError(EventError::MissingSigningKey)),
1601 }
1602 }
1603
1604 fn is_from_verified_device_or_allowed_type(
1617 &self,
1618 decryption_settings: &DecryptionSettings,
1619 result: &DecryptionResult,
1620 ) -> bool {
1621 let event_type = result.event.event_type();
1622
1623 match event_type {
1635 "m.room_key"
1636 | "m.room_key.withheld"
1637 | "m.room_key_request"
1638 | "m.secret.request"
1639 | "m.key.verification.key"
1640 | "m.key.verification.mac"
1641 | "m.key.verification.done"
1642 | "m.key.verification.ready"
1643 | "m.key.verification.start"
1644 | "m.key.verification.accept"
1645 | "m.key.verification.cancel"
1646 | "m.key.verification.request" => {
1647 true
1650 }
1651 _ => {
1652 satisfies_sender_trust_requirement(
1655 &result.encryption_info,
1656 &decryption_settings.sender_device_trust_requirement,
1657 )
1658 }
1659 }
1660 }
1661
1662 fn get_olm_encryption_info(
1669 sender_key: Curve25519PublicKey,
1670 sender_id: &UserId,
1671 sender_device: &Option<Device>,
1672 ) -> EncryptionInfo {
1673 let verification_state = sender_device
1674 .as_ref()
1675 .map(|device| {
1676 if device.is_verified() {
1677 VerificationState::Verified
1679 } else if device.is_cross_signed_by_owner() {
1680 if device
1682 .device_owner_identity
1683 .as_ref()
1684 .expect("A device cross-signed by the owner must have an owner identity")
1685 .was_previously_verified()
1686 {
1687 VerificationState::Unverified(VerificationLevel::VerificationViolation)
1688 } else {
1689 VerificationState::Unverified(VerificationLevel::UnverifiedIdentity)
1690 }
1691 } else {
1692 VerificationState::Unverified(VerificationLevel::UnsignedDevice)
1694 }
1695 })
1696 .unwrap_or(VerificationState::Unverified(VerificationLevel::None(
1697 DeviceLinkProblem::MissingDevice,
1698 )));
1699
1700 EncryptionInfo {
1701 sender: sender_id.to_owned(),
1702 sender_device: sender_device.as_ref().map(|d| d.device_id().to_owned()),
1703 forwarder: None,
1704 algorithm_info: AlgorithmInfo::OlmV1Curve25519AesSha2 {
1705 curve25519_public_key_base64: sender_key.to_base64(),
1706 },
1707 verification_state,
1708 }
1709 }
1710
1711 fn check_sender_device_keys(
1756 event: &AnyDecryptedOlmEvent,
1757 sender_key: Curve25519PublicKey,
1758 ) -> OlmResult<Option<&DeviceKeys>> {
1759 let Some(sender_device_keys) = event.sender_device_keys() else {
1760 return Ok(None);
1761 };
1762
1763 sender_device_keys.check_self_signature().map_err(|err| {
1765 warn!(
1766 "Received a to-device message with sender_device_keys with \
1767 invalid signature: {err:?}",
1768 );
1769 OlmError::EventError(EventError::InvalidSenderDeviceKeys)
1770 })?;
1771
1772 if sender_device_keys.ed25519_key() != Some(event.keys().ed25519) {
1775 warn!(
1776 "Received a to-device message with sender_device_keys with incorrect \
1777 ed25519 key: expected {:?}, got {:?}",
1778 event.keys().ed25519,
1779 sender_device_keys.ed25519_key(),
1780 );
1781 return Err(OlmError::EventError(EventError::InvalidSenderDeviceKeys));
1782 }
1783
1784 if sender_device_keys.curve25519_key() != Some(sender_key) {
1787 warn!(
1788 "Received a to-device message with sender_device_keys with incorrect \
1789 curve25519 key: expected {sender_key:?}, got {:?}",
1790 sender_device_keys.curve25519_key(),
1791 );
1792 return Err(OlmError::EventError(EventError::InvalidSenderDeviceKeys));
1793 }
1794
1795 Ok(Some(sender_device_keys))
1796 }
1797
1798 #[doc(hidden)]
1803 pub fn deep_clone(&self) -> Self {
1804 Self::from_pickle(self.pickle()).unwrap()
1806 }
1807}
1808
1809impl PartialEq for Account {
1810 fn eq(&self, other: &Self) -> bool {
1811 self.identity_keys() == other.identity_keys() && self.shared() == other.shared()
1812 }
1813}
1814
1815pub(crate) fn shared_history_from_history_visibility(
1835 history_visibility: &HistoryVisibility,
1836) -> bool {
1837 match history_visibility {
1838 HistoryVisibility::Shared | HistoryVisibility::WorldReadable => true,
1839 HistoryVisibility::Invited | HistoryVisibility::Joined | _ => false,
1840 }
1841}
1842
1843fn expand_legacy_pickle_key(key: &[u8; 32], device_id: &DeviceId) -> Box<[u8; 32]> {
1862 let kdf: Hkdf<Sha256> = Hkdf::new(Some(device_id.as_bytes()), key);
1863 let mut key = Box::new([0u8; 32]);
1864
1865 kdf.expand(b"dehydrated-device-pickle-key", key.as_mut_slice())
1866 .expect("We should be able to expand the 32 byte pickle key");
1867
1868 key
1869}
1870
1871fn satisfies_sender_trust_requirement(
1874 encryption_info: &EncryptionInfo,
1875 trust_requirement: &TrustRequirement,
1876) -> bool {
1877 trace!(
1878 verification_state = ?encryption_info.verification_state,
1879 ?trust_requirement, "check_to_device_sender_trust_requirement",
1880 );
1881
1882 match (&encryption_info.verification_state, trust_requirement) {
1883 (_, TrustRequirement::Untrusted) => true,
1885
1886 (VerificationState::Verified, _) => true,
1888
1889 (
1892 VerificationState::Unverified(verification_level),
1893 TrustRequirement::CrossSignedOrLegacy | TrustRequirement::CrossSigned,
1894 ) => match verification_level {
1895 VerificationLevel::UnverifiedIdentity => true,
1897
1898 VerificationLevel::UnsignedDevice
1901 | VerificationLevel::None(_)
1902 | VerificationLevel::VerificationViolation
1903 | VerificationLevel::MismatchedSender => false,
1904 },
1905 }
1906}
1907
1908#[cfg(test)]
1909mod tests {
1910 use std::{
1911 collections::{BTreeMap, BTreeSet},
1912 ops::Deref,
1913 time::Duration,
1914 };
1915
1916 use anyhow::Result;
1917 use matrix_sdk_test::async_test;
1918 use ruma::{
1919 DeviceId, MilliSecondsSinceUnixEpoch, OneTimeKeyAlgorithm, OneTimeKeyId, UserId, device_id,
1920 events::room::history_visibility::HistoryVisibility, room_id, user_id,
1921 };
1922 use serde_json::json;
1923
1924 use super::Account;
1925 use crate::{
1926 DeviceData, EncryptionSettings,
1927 olm::{SignedJsonObject, account::shared_history_from_history_visibility},
1928 types::{DeviceKeys, SignedKey},
1929 };
1930
1931 fn user_id() -> &'static UserId {
1932 user_id!("@alice:localhost")
1933 }
1934
1935 fn device_id() -> &'static DeviceId {
1936 device_id!("DEVICEID")
1937 }
1938
1939 #[test]
1940 fn test_one_time_key_creation() -> Result<()> {
1941 let mut account = Account::with_device_id(user_id(), device_id());
1942
1943 let (_, one_time_keys, _) = account.keys_for_upload();
1944 assert!(!one_time_keys.is_empty());
1945
1946 let (_, second_one_time_keys, _) = account.keys_for_upload();
1947 assert!(!second_one_time_keys.is_empty());
1948
1949 let one_time_key_ids: BTreeSet<&OneTimeKeyId> =
1950 one_time_keys.keys().map(Deref::deref).collect();
1951 let second_one_time_key_ids: BTreeSet<&OneTimeKeyId> =
1952 second_one_time_keys.keys().map(Deref::deref).collect();
1953
1954 assert_eq!(one_time_key_ids, second_one_time_key_ids);
1955
1956 account.mark_keys_as_published();
1957 account.update_uploaded_key_count(50);
1958 account.generate_one_time_keys_if_needed();
1959
1960 let (_, third_one_time_keys, _) = account.keys_for_upload();
1961 assert!(third_one_time_keys.is_empty());
1962
1963 account.update_uploaded_key_count(0);
1964 account.generate_one_time_keys_if_needed();
1965
1966 let (_, fourth_one_time_keys, _) = account.keys_for_upload();
1967 assert!(!fourth_one_time_keys.is_empty());
1968
1969 let fourth_one_time_key_ids: BTreeSet<&OneTimeKeyId> =
1970 fourth_one_time_keys.keys().map(Deref::deref).collect();
1971
1972 assert_ne!(one_time_key_ids, fourth_one_time_key_ids);
1973 Ok(())
1974 }
1975
1976 #[test]
1977 fn test_fallback_key_creation() -> Result<()> {
1978 let mut account = Account::with_device_id(user_id(), device_id());
1979
1980 let (_, _, fallback_keys) = account.keys_for_upload();
1981
1982 assert!(
1986 fallback_keys.is_empty(),
1987 "We should not upload fallback keys until we know if the server supports them."
1988 );
1989
1990 let one_time_keys = BTreeMap::from([(OneTimeKeyAlgorithm::SignedCurve25519, 50u8.into())]);
1991
1992 account.update_key_counts(&one_time_keys, None);
1995 let (_, _, fallback_keys) = account.keys_for_upload();
1996 assert!(
1997 fallback_keys.is_empty(),
1998 "We should not upload a fallback key if we're certain that the server doesn't support \
1999 them."
2000 );
2001
2002 let unused_fallback_keys = &[];
2006 account.update_key_counts(&one_time_keys, Some(unused_fallback_keys.as_ref()));
2007 let (_, _, fallback_keys) = account.keys_for_upload();
2008 assert!(
2009 !fallback_keys.is_empty(),
2010 "We should upload the initial fallback key if the server supports them."
2011 );
2012 account.mark_keys_as_published();
2013
2014 let unused_fallback_keys = &[];
2017 account.update_key_counts(&one_time_keys, Some(unused_fallback_keys.as_ref()));
2018 let (_, _, fallback_keys) = account.keys_for_upload();
2019 assert!(
2020 fallback_keys.is_empty(),
2021 "We should not upload new fallback keys unless our current fallback key expires."
2022 );
2023
2024 let fallback_key_timestamp =
2025 account.fallback_creation_timestamp.unwrap().to_system_time().unwrap()
2026 - Duration::from_secs(3600 * 24 * 30);
2027
2028 account.fallback_creation_timestamp =
2029 Some(MilliSecondsSinceUnixEpoch::from_system_time(fallback_key_timestamp).unwrap());
2030
2031 account.update_key_counts(&one_time_keys, None);
2032 let (_, _, fallback_keys) = account.keys_for_upload();
2033 assert!(
2034 !fallback_keys.is_empty(),
2035 "Now that our fallback key has expired, we should try to upload a new one, even if the \
2036 server supposedly doesn't support fallback keys anymore"
2037 );
2038
2039 Ok(())
2040 }
2041
2042 #[test]
2043 fn test_fallback_key_signing() -> Result<()> {
2044 let key = vodozemac::Curve25519PublicKey::from_base64(
2045 "7PUPP6Ijt5R8qLwK2c8uK5hqCNF9tOzWYgGaAay5JBs",
2046 )?;
2047 let account = Account::with_device_id(user_id(), device_id());
2048
2049 let key = account.sign_key(key, true);
2050
2051 let canonical_key = key.to_canonical_json()?;
2052
2053 assert_eq!(
2054 canonical_key,
2055 "{\"fallback\":true,\"key\":\"7PUPP6Ijt5R8qLwK2c8uK5hqCNF9tOzWYgGaAay5JBs\"}"
2056 );
2057
2058 account
2059 .has_signed_raw(key.signatures(), &canonical_key)
2060 .expect("Couldn't verify signature");
2061
2062 let device = DeviceData::from_account(&account);
2063 device.verify_one_time_key(&key).expect("The device can verify its own signature");
2064
2065 Ok(())
2066 }
2067
2068 #[test]
2069 fn test_account_and_device_creation_timestamp() -> Result<()> {
2070 let now = MilliSecondsSinceUnixEpoch::now();
2071 let account = Account::with_device_id(user_id(), device_id());
2072 let then = MilliSecondsSinceUnixEpoch::now();
2073
2074 assert!(account.creation_local_time() >= now);
2075 assert!(account.creation_local_time() <= then);
2076
2077 let device = DeviceData::from_account(&account);
2078 assert_eq!(account.creation_local_time(), device.first_time_seen_ts());
2079
2080 Ok(())
2081 }
2082
2083 #[async_test]
2084 async fn test_fallback_key_signature_verification() -> Result<()> {
2085 let fallback_key = json!({
2086 "fallback": true,
2087 "key": "XPFqtLvBepBmW6jSAbBuJbhEpprBhQOX1IjUu+cnMF4",
2088 "signatures": {
2089 "@dkasak_c:matrix.org": {
2090 "ed25519:EXPDYDPWZH": "RJCBMJPL5hvjxgq8rmLmqkNOuPsaan7JeL1wsE+gW6R39G894lb2sBmzapHeKCn/KFjmkonPLkICApRDS+zyDw"
2091 }
2092 }
2093 });
2094
2095 let device_keys = json!({
2096 "algorithms": [
2097 "m.olm.v1.curve25519-aes-sha2",
2098 "m.megolm.v1.aes-sha2"
2099 ],
2100 "device_id": "EXPDYDPWZH",
2101 "keys": {
2102 "curve25519:EXPDYDPWZH": "k7f3igo0Vrdm88JSSA5d3OCuUfHYELChB2b57aOROB8",
2103 "ed25519:EXPDYDPWZH": "GdjYI8fxs175gSpYRJkyN6FRfvcyTsNOhJ2OR/Ggp+E"
2104 },
2105 "signatures": {
2106 "@dkasak_c:matrix.org": {
2107 "ed25519:EXPDYDPWZH": "kzrtfQMbJXWXQ1uzhybtwFnGk0JJBS4Mg8VPMusMu6U8MPJccwoHVZKo5+owuHTzIodI+GZYqLmMSzvfvsChAA"
2108 }
2109 },
2110 "user_id": "@dkasak_c:matrix.org",
2111 "unsigned": {}
2112 });
2113
2114 let device_keys: DeviceKeys = serde_json::from_value(device_keys).unwrap();
2115 let device = DeviceData::try_from(&device_keys).unwrap();
2116 let fallback_key: SignedKey = serde_json::from_value(fallback_key).unwrap();
2117
2118 device
2119 .verify_one_time_key(&fallback_key)
2120 .expect("The fallback key should pass the signature verification");
2121
2122 Ok(())
2123 }
2124
2125 #[test]
2126 fn test_shared_history_flag_from_history_visibility() {
2127 assert!(
2128 shared_history_from_history_visibility(&HistoryVisibility::WorldReadable),
2129 "The world readable visibility should set the shared history flag to true"
2130 );
2131
2132 assert!(
2133 shared_history_from_history_visibility(&HistoryVisibility::Shared),
2134 "The shared visibility should set the shared history flag to true"
2135 );
2136
2137 assert!(
2138 !shared_history_from_history_visibility(&HistoryVisibility::Joined),
2139 "The joined visibility should set the shared history flag to false"
2140 );
2141
2142 assert!(
2143 !shared_history_from_history_visibility(&HistoryVisibility::Invited),
2144 "The invited visibility should set the shared history flag to false"
2145 );
2146
2147 let visibility = HistoryVisibility::from("custom_visibility");
2148 assert!(
2149 !shared_history_from_history_visibility(&visibility),
2150 "A custom visibility should set the shared history flag to false"
2151 );
2152 }
2153
2154 #[async_test]
2155 async fn test_shared_history_set_when_creating_group_sessions() {
2156 let account = Account::new(user_id());
2157 let room_id = room_id!("!room:id");
2158 let settings = EncryptionSettings {
2159 history_visibility: HistoryVisibility::Shared,
2160 ..Default::default()
2161 };
2162
2163 let (_, session) = account
2164 .create_group_session_pair(room_id, settings, Default::default())
2165 .await
2166 .expect("We should be able to create a group session pair");
2167
2168 assert!(
2169 session.shared_history(),
2170 "The shared history flag should have been set when we created the new session"
2171 );
2172 }
2173}