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