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 api::client::{
32 dehydrated_device::{DehydratedDeviceData, DehydratedDeviceV2},
33 keys::{
34 upload_keys,
35 upload_signatures::v3::{Request as SignatureUploadRequest, SignedKeys},
36 },
37 },
38 events::{room::history_visibility::HistoryVisibility, AnyToDeviceEvent},
39 serde::Raw,
40 DeviceId, DeviceKeyAlgorithm, DeviceKeyId, MilliSecondsSinceUnixEpoch, OneTimeKeyAlgorithm,
41 OneTimeKeyId, OwnedDeviceId, OwnedDeviceKeyId, OwnedOneTimeKeyId, OwnedUserId, RoomId,
42 SecondsSinceUnixEpoch, UInt, UserId,
43};
44use serde::{de::Error, Deserialize, Serialize};
45use serde_json::{
46 value::{to_raw_value, RawValue as RawJsonValue},
47 Value,
48};
49use sha2::{Digest, Sha256};
50use tokio::sync::Mutex;
51use tracing::{debug, field::debug, info, instrument, trace, warn, Span};
52use vodozemac::{
53 base64_encode,
54 olm::{
55 Account as InnerAccount, AccountPickle, IdentityKeys, OlmMessage,
56 OneTimeKeyGenerationResult, PreKeyMessage, SessionConfig,
57 },
58 Curve25519PublicKey, Ed25519Signature, KeyId, PickleError,
59};
60
61use super::{
62 utility::SignJson, EncryptionSettings, InboundGroupSession, OutboundGroupSession,
63 PrivateCrossSigningIdentity, Session, SessionCreationError as MegolmSessionCreationError,
64};
65#[cfg(feature = "experimental-algorithms")]
66use crate::types::events::room::encrypted::OlmV2Curve25519AesSha2Content;
67use crate::{
68 dehydrated_devices::DehydrationError,
69 error::{EventError, OlmResult, SessionCreationError},
70 identities::DeviceData,
71 olm::SenderData,
72 store::{
73 types::{Changes, DeviceChanges},
74 Store,
75 },
76 types::{
77 events::{
78 olm_v1::AnyDecryptedOlmEvent,
79 room::encrypted::{
80 EncryptedToDeviceEvent, OlmV1Curve25519AesSha2Content,
81 ToDeviceEncryptedEventContent,
82 },
83 },
84 requests::UploadSigningKeysRequest,
85 CrossSigningKey, DeviceKeys, EventEncryptionAlgorithm, MasterPubkey, OneTimeKey, SignedKey,
86 },
87 Device, OlmError, SignatureError,
88};
89
90#[derive(Debug)]
91enum PrekeyBundle {
92 Olm3DH { key: SignedKey },
93}
94
95#[derive(Debug, Clone)]
96pub(crate) enum SessionType {
97 New(Session),
98 Existing(Session),
99}
100
101#[derive(Debug)]
102pub struct InboundCreationResult {
103 pub session: Session,
104 pub plaintext: String,
105}
106
107impl SessionType {
108 #[cfg(test)]
109 pub fn session(self) -> Session {
110 match self {
111 SessionType::New(s) => s,
112 SessionType::Existing(s) => s,
113 }
114 }
115}
116
117#[derive(Debug)]
123pub(crate) struct OlmDecryptionInfo {
124 pub session: SessionType,
125 pub message_hash: OlmMessageHash,
126 pub inbound_group_session: Option<InboundGroupSession>,
127 pub result: DecryptionResult,
128}
129
130#[derive(Debug)]
131pub(crate) struct DecryptionResult {
132 pub event: Box<AnyDecryptedOlmEvent>,
134 pub raw_event: Raw<AnyToDeviceEvent>,
135 pub sender_key: Curve25519PublicKey,
136 pub encryption_info: EncryptionInfo,
137}
138
139#[derive(Debug, Clone, Serialize, Deserialize)]
143pub struct OlmMessageHash {
144 pub sender_key: String,
146 pub hash: String,
148}
149
150impl OlmMessageHash {
151 fn new(sender_key: Curve25519PublicKey, ciphertext: &OlmMessage) -> Self {
152 let (message_type, ciphertext) = ciphertext.clone().to_parts();
153 let sender_key = sender_key.to_base64();
154
155 let sha = Sha256::new()
156 .chain_update(sender_key.as_bytes())
157 .chain_update([message_type as u8])
158 .chain_update(ciphertext)
159 .finalize();
160
161 Self { sender_key, hash: base64_encode(sha.as_slice()) }
162 }
163}
164
165#[derive(Clone)]
170#[cfg_attr(not(tarpaulin_include), derive(Debug))]
171pub struct StaticAccountData {
172 pub user_id: OwnedUserId,
174 pub device_id: OwnedDeviceId,
176 pub identity_keys: Arc<IdentityKeys>,
178 pub dehydrated: bool,
180 creation_local_time: MilliSecondsSinceUnixEpoch,
182}
183
184impl StaticAccountData {
185 const ALGORITHMS: &'static [&'static EventEncryptionAlgorithm] = &[
186 &EventEncryptionAlgorithm::OlmV1Curve25519AesSha2,
187 #[cfg(feature = "experimental-algorithms")]
188 &EventEncryptionAlgorithm::OlmV2Curve25519AesSha2,
189 &EventEncryptionAlgorithm::MegolmV1AesSha2,
190 #[cfg(feature = "experimental-algorithms")]
191 &EventEncryptionAlgorithm::MegolmV2AesSha2,
192 ];
193
194 pub async fn create_group_session_pair(
209 &self,
210 room_id: &RoomId,
211 settings: EncryptionSettings,
212 own_sender_data: SenderData,
213 ) -> Result<(OutboundGroupSession, InboundGroupSession), MegolmSessionCreationError> {
214 trace!(?room_id, algorithm = settings.algorithm.as_str(), "Creating a new room key");
215
216 let visibility = settings.history_visibility.clone();
217 let algorithm = settings.algorithm.to_owned();
218
219 let outbound = OutboundGroupSession::new(
220 self.device_id.clone(),
221 self.identity_keys.clone(),
222 room_id,
223 settings,
224 )?;
225
226 let identity_keys = &self.identity_keys;
227
228 let sender_key = identity_keys.curve25519;
229 let signing_key = identity_keys.ed25519;
230 let shared_history = shared_history_from_history_visibility(&visibility);
231
232 let inbound = InboundGroupSession::new(
233 sender_key,
234 signing_key,
235 room_id,
236 &outbound.session_key().await,
237 own_sender_data,
238 algorithm,
239 Some(visibility),
240 shared_history,
241 )?;
242
243 Ok((outbound, inbound))
244 }
245
246 #[cfg(any(test, feature = "testing"))]
247 #[allow(dead_code)]
248 pub async fn create_group_session_pair_with_defaults(
251 &self,
252 room_id: &RoomId,
253 ) -> (OutboundGroupSession, InboundGroupSession) {
254 self.create_group_session_pair(
255 room_id,
256 EncryptionSettings::default(),
257 SenderData::unknown(),
258 )
259 .await
260 .expect("Can't create default group session pair")
261 }
262
263 pub fn signing_key_id(&self) -> OwnedDeviceKeyId {
265 DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, self.device_id())
266 }
267
268 pub fn has_signed_raw(
278 &self,
279 signatures: &crate::types::Signatures,
280 canonical_json: &str,
281 ) -> Result<(), SignatureError> {
282 use crate::olm::utility::VerifyJson;
283
284 let signing_key = self.identity_keys.ed25519;
285
286 signing_key.verify_canonicalized_json(
287 &self.user_id,
288 &DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, self.device_id()),
289 signatures,
290 canonical_json,
291 )
292 }
293
294 pub fn unsigned_device_keys(&self) -> DeviceKeys {
296 let identity_keys = self.identity_keys();
297 let keys = BTreeMap::from([
298 (
299 DeviceKeyId::from_parts(DeviceKeyAlgorithm::Curve25519, &self.device_id),
300 identity_keys.curve25519.into(),
301 ),
302 (
303 DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, &self.device_id),
304 identity_keys.ed25519.into(),
305 ),
306 ]);
307
308 let mut ret = DeviceKeys::new(
309 (*self.user_id).to_owned(),
310 (*self.device_id).to_owned(),
311 Self::ALGORITHMS.iter().map(|a| (**a).clone()).collect(),
312 keys,
313 Default::default(),
314 );
315 if self.dehydrated {
316 ret.dehydrated = JsOption::Some(true);
317 }
318 ret
319 }
320
321 pub fn user_id(&self) -> &UserId {
323 &self.user_id
324 }
325
326 pub fn device_id(&self) -> &DeviceId {
328 &self.device_id
329 }
330
331 pub fn identity_keys(&self) -> IdentityKeys {
333 *self.identity_keys
334 }
335
336 pub fn creation_local_time(&self) -> MilliSecondsSinceUnixEpoch {
338 self.creation_local_time
339 }
340}
341
342pub struct Account {
347 pub(crate) static_data: StaticAccountData,
348 inner: Box<InnerAccount>,
350 shared: bool,
353 uploaded_signed_key_count: u64,
358 fallback_creation_timestamp: Option<MilliSecondsSinceUnixEpoch>,
366}
367
368impl Deref for Account {
369 type Target = StaticAccountData;
370
371 fn deref(&self) -> &Self::Target {
372 &self.static_data
373 }
374}
375
376#[derive(Serialize, Deserialize)]
381#[allow(missing_debug_implementations)]
382pub struct PickledAccount {
383 pub user_id: OwnedUserId,
385 pub device_id: OwnedDeviceId,
387 pub pickle: AccountPickle,
389 pub shared: bool,
391 #[serde(default)]
393 pub dehydrated: bool,
394 pub uploaded_signed_key_count: u64,
396 #[serde(default = "default_account_creation_time")]
399 pub creation_local_time: MilliSecondsSinceUnixEpoch,
400 #[serde(default)]
402 pub fallback_key_creation_timestamp: Option<MilliSecondsSinceUnixEpoch>,
403}
404
405fn default_account_creation_time() -> MilliSecondsSinceUnixEpoch {
406 MilliSecondsSinceUnixEpoch(UInt::default())
407}
408
409#[cfg(not(tarpaulin_include))]
410impl fmt::Debug for Account {
411 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
412 f.debug_struct("Account")
413 .field("identity_keys", &self.identity_keys())
414 .field("shared", &self.shared())
415 .finish()
416 }
417}
418
419pub type OneTimeKeys = BTreeMap<OwnedOneTimeKeyId, Raw<ruma::encryption::OneTimeKey>>;
420pub type FallbackKeys = OneTimeKeys;
421
422impl Account {
423 pub(crate) fn new_helper(
424 mut account: InnerAccount,
425 user_id: &UserId,
426 device_id: &DeviceId,
427 ) -> Self {
428 let identity_keys = account.identity_keys();
429
430 account.generate_one_time_keys(account.max_number_of_one_time_keys());
442
443 Self {
444 static_data: StaticAccountData {
445 user_id: user_id.into(),
446 device_id: device_id.into(),
447 identity_keys: Arc::new(identity_keys),
448 dehydrated: false,
449 creation_local_time: MilliSecondsSinceUnixEpoch::now(),
450 },
451 inner: Box::new(account),
452 shared: false,
453 uploaded_signed_key_count: 0,
454 fallback_creation_timestamp: None,
455 }
456 }
457
458 pub fn with_device_id(user_id: &UserId, device_id: &DeviceId) -> Self {
460 let account = InnerAccount::new();
461
462 Self::new_helper(account, user_id, device_id)
463 }
464
465 pub fn new(user_id: &UserId) -> Self {
468 let account = InnerAccount::new();
469 let device_id: OwnedDeviceId =
470 base64_encode(account.identity_keys().curve25519.as_bytes()).into();
471
472 Self::new_helper(account, user_id, &device_id)
473 }
474
475 pub fn new_dehydrated(user_id: &UserId) -> Self {
477 let account = InnerAccount::new();
478 let device_id: OwnedDeviceId =
479 base64_encode(account.identity_keys().curve25519.as_bytes()).into();
480
481 let mut ret = Self::new_helper(account, user_id, &device_id);
482 ret.static_data.dehydrated = true;
483 ret
484 }
485
486 pub fn static_data(&self) -> &StaticAccountData {
488 &self.static_data
489 }
490
491 pub fn update_uploaded_key_count(&mut self, new_count: u64) {
497 self.uploaded_signed_key_count = new_count;
498 }
499
500 pub fn uploaded_key_count(&self) -> u64 {
502 self.uploaded_signed_key_count
503 }
504
505 pub fn shared(&self) -> bool {
507 self.shared
508 }
509
510 pub fn mark_as_shared(&mut self) {
515 self.shared = true;
516 }
517
518 pub fn one_time_keys(&self) -> HashMap<KeyId, Curve25519PublicKey> {
522 self.inner.one_time_keys()
523 }
524
525 pub fn generate_one_time_keys(&mut self, count: usize) -> OneTimeKeyGenerationResult {
527 self.inner.generate_one_time_keys(count)
528 }
529
530 pub fn max_one_time_keys(&self) -> usize {
532 self.inner.max_number_of_one_time_keys()
533 }
534
535 pub(crate) fn update_key_counts(
536 &mut self,
537 one_time_key_counts: &BTreeMap<OneTimeKeyAlgorithm, UInt>,
538 unused_fallback_keys: Option<&[OneTimeKeyAlgorithm]>,
539 ) {
540 if let Some(count) = one_time_key_counts.get(&OneTimeKeyAlgorithm::SignedCurve25519) {
541 let count: u64 = (*count).into();
542 let old_count = self.uploaded_key_count();
543
544 if count != old_count {
548 debug!(
549 "Updated uploaded one-time key count {} -> {count}.",
550 self.uploaded_key_count(),
551 );
552 }
553
554 self.update_uploaded_key_count(count);
555 self.generate_one_time_keys_if_needed();
556 }
557
558 if unused_fallback_keys.is_some() || self.fallback_creation_timestamp.is_some() {
562 self.generate_fallback_key_if_needed();
563 }
564 }
565
566 #[instrument(skip_all)]
575 pub fn generate_one_time_keys_if_needed(&mut self) -> Option<u64> {
576 if !self.one_time_keys().is_empty() {
580 return Some(0);
581 }
582
583 let count = self.uploaded_key_count();
584 let max_keys = self.max_one_time_keys();
585
586 if count >= max_keys as u64 {
587 return None;
588 }
589
590 let key_count = (max_keys as u64) - count;
591 let key_count: usize = key_count.try_into().unwrap_or(max_keys);
592
593 let result = self.generate_one_time_keys(key_count);
594
595 debug!(
596 count = key_count,
597 discarded_keys = ?result.removed,
598 created_keys = ?result.created,
599 "Generated new one-time keys"
600 );
601
602 Some(key_count as u64)
603 }
604
605 pub(crate) fn generate_fallback_key_if_needed(&mut self) {
612 if self.inner.fallback_key().is_empty() && self.fallback_key_expired() {
613 let removed_fallback_key = self.inner.generate_fallback_key();
614 self.fallback_creation_timestamp = Some(MilliSecondsSinceUnixEpoch::now());
615
616 debug!(
617 ?removed_fallback_key,
618 "The fallback key either expired or we didn't have one: generated a new fallback key.",
619 );
620 }
621 }
622
623 fn fallback_key_expired(&self) -> bool {
631 const FALLBACK_KEY_MAX_AGE: Duration = Duration::from_secs(3600 * 24 * 7);
632
633 if let Some(time) = self.fallback_creation_timestamp {
634 let Some(system_time) = time.to_system_time() else {
638 return true;
639 };
640
641 let Ok(elapsed) = system_time.elapsed() else {
645 return true;
646 };
647
648 elapsed > FALLBACK_KEY_MAX_AGE
653 } else {
654 true
657 }
658 }
659
660 fn fallback_key(&self) -> HashMap<KeyId, Curve25519PublicKey> {
661 self.inner.fallback_key()
662 }
663
664 pub fn keys_for_upload(&self) -> (Option<DeviceKeys>, OneTimeKeys, FallbackKeys) {
670 let device_keys = self.shared().not().then(|| self.device_keys());
671
672 let one_time_keys = self.signed_one_time_keys();
673 let fallback_keys = self.signed_fallback_keys();
674
675 (device_keys, one_time_keys, fallback_keys)
676 }
677
678 pub fn mark_keys_as_published(&mut self) {
680 self.inner.mark_keys_as_published();
681 }
682
683 pub fn sign(&self, string: &str) -> Ed25519Signature {
687 self.inner.sign(string)
688 }
689
690 pub fn pickle(&self) -> PickledAccount {
692 let pickle = self.inner.pickle();
693
694 PickledAccount {
695 user_id: self.user_id().to_owned(),
696 device_id: self.device_id().to_owned(),
697 pickle,
698 shared: self.shared(),
699 dehydrated: self.static_data.dehydrated,
700 uploaded_signed_key_count: self.uploaded_key_count(),
701 creation_local_time: self.static_data.creation_local_time,
702 fallback_key_creation_timestamp: self.fallback_creation_timestamp,
703 }
704 }
705
706 pub(crate) fn dehydrate(&self, pickle_key: &[u8; 32]) -> Raw<DehydratedDeviceData> {
707 let dehydration_result = self
708 .inner
709 .to_dehydrated_device(pickle_key)
710 .expect("We should be able to convert a freshly created Account into a libolm pickle");
711
712 let data = DehydratedDeviceData::V2(DehydratedDeviceV2::new(
713 dehydration_result.ciphertext,
714 dehydration_result.nonce,
715 ));
716 Raw::from_json(to_raw_value(&data).expect("Couldn't serialize our dehydrated device data"))
717 }
718
719 pub(crate) fn rehydrate(
720 pickle_key: &[u8; 32],
721 user_id: &UserId,
722 device_id: &DeviceId,
723 device_data: Raw<DehydratedDeviceData>,
724 ) -> Result<Self, DehydrationError> {
725 let data = device_data.deserialize()?;
726
727 match data {
728 DehydratedDeviceData::V1(d) => {
729 let pickle_key = expand_legacy_pickle_key(pickle_key, device_id);
730 let account =
731 InnerAccount::from_libolm_pickle(&d.device_pickle, pickle_key.as_ref())?;
732 Ok(Self::new_helper(account, user_id, device_id))
733 }
734 DehydratedDeviceData::V2(d) => {
735 let account =
736 InnerAccount::from_dehydrated_device(&d.device_pickle, &d.nonce, pickle_key)?;
737 Ok(Self::new_helper(account, user_id, device_id))
738 }
739 _ => Err(DehydrationError::Json(serde_json::Error::custom(format!(
740 "Unsupported dehydrated device algorithm {:?}",
741 data.algorithm()
742 )))),
743 }
744 }
745
746 #[cfg(test)]
749 pub(crate) fn legacy_dehydrate(&self, pickle_key: &[u8; 32]) -> Raw<DehydratedDeviceData> {
750 let pickle_key = expand_legacy_pickle_key(pickle_key, &self.device_id);
751 let device_pickle = self
752 .inner
753 .to_libolm_pickle(pickle_key.as_ref())
754 .expect("We should be able to convert a freshly created Account into a libolm pickle");
755
756 let data = DehydratedDeviceData::V1(DehydratedDeviceV1::new(device_pickle));
757 Raw::from_json(to_raw_value(&data).expect("Couldn't serialize our dehydrated device data"))
758 }
759
760 pub fn from_pickle(pickle: PickledAccount) -> Result<Self, PickleError> {
769 let account: vodozemac::olm::Account = pickle.pickle.into();
770 let identity_keys = account.identity_keys();
771
772 Ok(Self {
773 static_data: StaticAccountData {
774 user_id: (*pickle.user_id).into(),
775 device_id: (*pickle.device_id).into(),
776 identity_keys: Arc::new(identity_keys),
777 dehydrated: pickle.dehydrated,
778 creation_local_time: pickle.creation_local_time,
779 },
780 inner: Box::new(account),
781 shared: pickle.shared,
782 uploaded_signed_key_count: pickle.uploaded_signed_key_count,
783 fallback_creation_timestamp: pickle.fallback_key_creation_timestamp,
784 })
785 }
786
787 pub fn device_keys(&self) -> DeviceKeys {
790 let mut device_keys = self.unsigned_device_keys();
791
792 let json_device_keys =
795 serde_json::to_value(&device_keys).expect("device key is always safe to serialize");
796 let signature = self
797 .sign_json(json_device_keys)
798 .expect("Newly created device keys can always be signed");
799
800 device_keys.signatures.add_signature(
801 self.user_id().to_owned(),
802 DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, &self.static_data.device_id),
803 signature,
804 );
805
806 device_keys
807 }
808
809 pub async fn bootstrap_cross_signing(
811 &self,
812 ) -> (PrivateCrossSigningIdentity, UploadSigningKeysRequest, SignatureUploadRequest) {
813 PrivateCrossSigningIdentity::with_account(self).await
814 }
815
816 pub fn sign_cross_signing_key(
818 &self,
819 cross_signing_key: &mut CrossSigningKey,
820 ) -> Result<(), SignatureError> {
821 #[allow(clippy::needless_borrows_for_generic_args)]
822 let signature = self.sign_json(serde_json::to_value(&cross_signing_key)?)?;
824
825 cross_signing_key.signatures.add_signature(
826 self.user_id().to_owned(),
827 DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, self.device_id()),
828 signature,
829 );
830
831 Ok(())
832 }
833
834 pub fn sign_master_key(
836 &self,
837 master_key: &MasterPubkey,
838 ) -> Result<SignatureUploadRequest, SignatureError> {
839 let public_key =
840 master_key.get_first_key().ok_or(SignatureError::MissingSigningKey)?.to_base64().into();
841
842 let mut cross_signing_key: CrossSigningKey = master_key.as_ref().clone();
843 cross_signing_key.signatures.clear();
844 self.sign_cross_signing_key(&mut cross_signing_key)?;
845
846 let mut user_signed_keys = SignedKeys::new();
847 user_signed_keys.add_cross_signing_keys(public_key, cross_signing_key.to_raw());
848
849 let signed_keys = [(self.user_id().to_owned(), user_signed_keys)].into();
850 Ok(SignatureUploadRequest::new(signed_keys))
851 }
852
853 pub fn sign_json(&self, json: Value) -> Result<Ed25519Signature, SignatureError> {
861 self.inner.sign_json(json)
862 }
863
864 pub fn signed_one_time_keys(&self) -> OneTimeKeys {
868 let one_time_keys = self.one_time_keys();
869
870 if one_time_keys.is_empty() {
871 BTreeMap::new()
872 } else {
873 self.signed_keys(one_time_keys, false)
874 }
875 }
876
877 pub fn signed_fallback_keys(&self) -> FallbackKeys {
881 let fallback_key = self.fallback_key();
882
883 if fallback_key.is_empty() {
884 BTreeMap::new()
885 } else {
886 self.signed_keys(fallback_key, true)
887 }
888 }
889
890 fn signed_keys(
891 &self,
892 keys: HashMap<KeyId, Curve25519PublicKey>,
893 fallback: bool,
894 ) -> OneTimeKeys {
895 let mut keys_map = BTreeMap::new();
896
897 for (key_id, key) in keys {
898 let signed_key = self.sign_key(key, fallback);
899
900 keys_map.insert(
901 OneTimeKeyId::from_parts(
902 OneTimeKeyAlgorithm::SignedCurve25519,
903 key_id.to_base64().as_str().into(),
904 ),
905 signed_key.into_raw(),
906 );
907 }
908
909 keys_map
910 }
911
912 fn sign_key(&self, key: Curve25519PublicKey, fallback: bool) -> SignedKey {
913 let mut key = if fallback {
914 SignedKey::new_fallback(key.to_owned())
915 } else {
916 SignedKey::new(key.to_owned())
917 };
918
919 let signature = self
920 .sign_json(serde_json::to_value(&key).expect("Can't serialize a signed key"))
921 .expect("Newly created one-time keys can always be signed");
922
923 key.signatures_mut().add_signature(
924 self.user_id().to_owned(),
925 DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, self.device_id()),
926 signature,
927 );
928
929 key
930 }
931
932 pub fn create_outbound_session_helper(
952 &self,
953 config: SessionConfig,
954 identity_key: Curve25519PublicKey,
955 one_time_key: Curve25519PublicKey,
956 fallback_used: bool,
957 our_device_keys: DeviceKeys,
958 ) -> Session {
959 let session = self.inner.create_outbound_session(config, identity_key, one_time_key);
960
961 let now = SecondsSinceUnixEpoch::now();
962 let session_id = session.session_id();
963
964 Session {
965 inner: Arc::new(Mutex::new(session)),
966 session_id: session_id.into(),
967 sender_key: identity_key,
968 our_device_keys,
969 created_using_fallback_key: fallback_used,
970 creation_time: now,
971 last_use_time: now,
972 }
973 }
974
975 #[instrument(
976 skip_all,
977 fields(
978 user_id = ?device.user_id(),
979 device_id = ?device.device_id(),
980 algorithms = ?device.algorithms()
981 )
982 )]
983 fn find_pre_key_bundle(
984 device: &DeviceData,
985 key_map: &OneTimeKeys,
986 ) -> Result<PrekeyBundle, SessionCreationError> {
987 let mut keys = key_map.iter();
988
989 let first_key = keys.next().ok_or_else(|| {
990 SessionCreationError::OneTimeKeyMissing(
991 device.user_id().to_owned(),
992 device.device_id().into(),
993 )
994 })?;
995
996 let first_key_id = first_key.0.to_owned();
997 let first_key = OneTimeKey::deserialize(first_key_id.algorithm(), first_key.1)?;
998
999 let result = match first_key {
1000 OneTimeKey::SignedKey(key) => Ok(PrekeyBundle::Olm3DH { key }),
1001 };
1002
1003 trace!(?result, "Finished searching for a valid pre-key bundle");
1004
1005 result
1006 }
1007
1008 #[allow(clippy::result_large_err)]
1023 pub fn create_outbound_session(
1024 &self,
1025 device: &DeviceData,
1026 key_map: &OneTimeKeys,
1027 our_device_keys: DeviceKeys,
1028 ) -> Result<Session, SessionCreationError> {
1029 let pre_key_bundle = Self::find_pre_key_bundle(device, key_map)?;
1030
1031 match pre_key_bundle {
1032 PrekeyBundle::Olm3DH { key } => {
1033 device.verify_one_time_key(&key).map_err(|error| {
1034 SessionCreationError::InvalidSignature {
1035 signing_key: device.ed25519_key().map(Box::new),
1036 one_time_key: key.clone().into(),
1037 error: error.into(),
1038 }
1039 })?;
1040
1041 let identity_key = device.curve25519_key().ok_or_else(|| {
1042 SessionCreationError::DeviceMissingCurveKey(
1043 device.user_id().to_owned(),
1044 device.device_id().into(),
1045 )
1046 })?;
1047
1048 let is_fallback = key.fallback();
1049 let one_time_key = key.key();
1050 let config = device.olm_session_config();
1051
1052 Ok(self.create_outbound_session_helper(
1053 config,
1054 identity_key,
1055 one_time_key,
1056 is_fallback,
1057 our_device_keys,
1058 ))
1059 }
1060 }
1061 }
1062
1063 pub fn create_inbound_session(
1078 &mut self,
1079 their_identity_key: Curve25519PublicKey,
1080 our_device_keys: DeviceKeys,
1081 message: &PreKeyMessage,
1082 ) -> Result<InboundCreationResult, SessionCreationError> {
1083 Span::current().record("session_id", debug(message.session_id()));
1084 trace!("Creating a new Olm session from a pre-key message");
1085
1086 let result = self.inner.create_inbound_session(their_identity_key, message)?;
1087 let now = SecondsSinceUnixEpoch::now();
1088 let session_id = result.session.session_id();
1089
1090 debug!(session=?result.session, "Decrypted an Olm message from a new Olm session");
1091
1092 let session = Session {
1093 inner: Arc::new(Mutex::new(result.session)),
1094 session_id: session_id.into(),
1095 sender_key: their_identity_key,
1096 our_device_keys,
1097 created_using_fallback_key: false,
1098 creation_time: now,
1099 last_use_time: now,
1100 };
1101
1102 let plaintext = String::from_utf8_lossy(&result.plaintext).to_string();
1103
1104 Ok(InboundCreationResult { session, plaintext })
1105 }
1106
1107 #[cfg(any(test, feature = "testing"))]
1108 #[allow(dead_code)]
1109 pub async fn create_session_for_test_helper(
1111 &mut self,
1112 other: &mut Account,
1113 ) -> (Session, Session) {
1114 use ruma::events::dummy::ToDeviceDummyEventContent;
1115
1116 other.generate_one_time_keys(1);
1117 let one_time_map = other.signed_one_time_keys();
1118 let device = DeviceData::from_account(other);
1119
1120 let mut our_session =
1121 self.create_outbound_session(&device, &one_time_map, self.device_keys()).unwrap();
1122
1123 other.mark_keys_as_published();
1124
1125 let message = our_session
1126 .encrypt(&device, "m.dummy", ToDeviceDummyEventContent::new(), None)
1127 .await
1128 .unwrap()
1129 .deserialize()
1130 .unwrap();
1131
1132 #[cfg(feature = "experimental-algorithms")]
1133 let content = if let ToDeviceEncryptedEventContent::OlmV2Curve25519AesSha2(c) = message {
1134 c
1135 } else {
1136 panic!("Invalid encrypted event algorithm {}", message.algorithm());
1137 };
1138
1139 #[cfg(not(feature = "experimental-algorithms"))]
1140 let ToDeviceEncryptedEventContent::OlmV1Curve25519AesSha2(content) = message
1141 else {
1142 panic!("Invalid encrypted event algorithm {}", message.algorithm());
1143 };
1144
1145 let OlmMessage::PreKey(prekey) = content.ciphertext else {
1146 panic!("Wrong Olm message type");
1147 };
1148
1149 let our_device = DeviceData::from_account(self);
1150 let other_session = other
1151 .create_inbound_session(
1152 our_device.curve25519_key().unwrap(),
1153 other.device_keys(),
1154 &prekey,
1155 )
1156 .unwrap();
1157
1158 (our_session, other_session.session)
1159 }
1160
1161 async fn decrypt_olm_helper(
1162 &mut self,
1163 store: &Store,
1164 sender: &UserId,
1165 sender_key: Curve25519PublicKey,
1166 ciphertext: &OlmMessage,
1167 ) -> OlmResult<OlmDecryptionInfo> {
1168 let message_hash = OlmMessageHash::new(sender_key, ciphertext);
1169
1170 match self.decrypt_and_parse_olm_message(store, sender, sender_key, ciphertext).await {
1171 Ok((session, result)) => {
1172 Ok(OlmDecryptionInfo { session, message_hash, result, inbound_group_session: None })
1173 }
1174 Err(OlmError::SessionWedged(user_id, sender_key)) => {
1175 if store.is_message_known(&message_hash).await? {
1176 info!(?sender_key, "An Olm message got replayed, decryption failed");
1177 Err(OlmError::ReplayedMessage(user_id, sender_key))
1178 } else {
1179 Err(OlmError::SessionWedged(user_id, sender_key))
1180 }
1181 }
1182 Err(e) => Err(e),
1183 }
1184 }
1185
1186 #[cfg(feature = "experimental-algorithms")]
1187 async fn decrypt_olm_v2(
1188 &mut self,
1189 store: &Store,
1190 sender: &UserId,
1191 content: &OlmV2Curve25519AesSha2Content,
1192 ) -> OlmResult<OlmDecryptionInfo> {
1193 self.decrypt_olm_helper(store, sender, content.sender_key, &content.ciphertext).await
1194 }
1195
1196 #[instrument(skip_all, fields(sender, sender_key = ?content.sender_key))]
1197 async fn decrypt_olm_v1(
1198 &mut self,
1199 store: &Store,
1200 sender: &UserId,
1201 content: &OlmV1Curve25519AesSha2Content,
1202 ) -> OlmResult<OlmDecryptionInfo> {
1203 if content.recipient_key != self.static_data.identity_keys.curve25519 {
1204 warn!("Olm event doesn't contain a ciphertext for our key");
1205
1206 Err(EventError::MissingCiphertext.into())
1207 } else {
1208 Box::pin(self.decrypt_olm_helper(
1209 store,
1210 sender,
1211 content.sender_key,
1212 &content.ciphertext,
1213 ))
1214 .await
1215 }
1216 }
1217
1218 #[instrument(skip_all, fields(algorithm = ?event.content.algorithm()))]
1219 pub(crate) async fn decrypt_to_device_event(
1220 &mut self,
1221 store: &Store,
1222 event: &EncryptedToDeviceEvent,
1223 ) -> OlmResult<OlmDecryptionInfo> {
1224 trace!("Decrypting a to-device event");
1225
1226 match &event.content {
1227 ToDeviceEncryptedEventContent::OlmV1Curve25519AesSha2(c) => {
1228 self.decrypt_olm_v1(store, &event.sender, c).await
1229 }
1230 #[cfg(feature = "experimental-algorithms")]
1231 ToDeviceEncryptedEventContent::OlmV2Curve25519AesSha2(c) => {
1232 self.decrypt_olm_v2(store, &event.sender, c).await
1233 }
1234 ToDeviceEncryptedEventContent::Unknown(_) => {
1235 warn!(
1236 "Error decrypting an to-device event, unsupported \
1237 encryption algorithm"
1238 );
1239
1240 Err(EventError::UnsupportedAlgorithm.into())
1241 }
1242 }
1243 }
1244
1245 pub fn receive_keys_upload_response(
1247 &mut self,
1248 response: &upload_keys::v3::Response,
1249 ) -> OlmResult<()> {
1250 if !self.shared() {
1251 debug!("Marking account as shared");
1252 }
1253 self.mark_as_shared();
1254
1255 debug!("Marking one-time keys as published");
1256 self.mark_keys_as_published();
1259 self.update_key_counts(&response.one_time_key_counts, None);
1260
1261 Ok(())
1262 }
1263
1264 async fn decrypt_olm_message(
1266 &mut self,
1267 store: &Store,
1268 sender: &UserId,
1269 sender_key: Curve25519PublicKey,
1270 message: &OlmMessage,
1271 ) -> Result<(SessionType, String), OlmError> {
1272 let existing_sessions = store.get_sessions(&sender_key.to_base64()).await?;
1273
1274 match message {
1275 OlmMessage::Normal(_) => {
1276 let mut errors_by_olm_session = Vec::new();
1277
1278 if let Some(sessions) = existing_sessions {
1279 for session in sessions.lock().await.iter_mut() {
1282 match session.decrypt(message).await {
1283 Ok(p) => {
1284 return Ok((SessionType::Existing(session.clone()), p));
1286 }
1287
1288 Err(e) => {
1289 errors_by_olm_session.push((session.session_id().to_owned(), e));
1294 }
1295 }
1296 }
1297 }
1298
1299 warn!(
1300 ?errors_by_olm_session,
1301 "Failed to decrypt a non-pre-key message with all available sessions"
1302 );
1303 Err(OlmError::SessionWedged(sender.to_owned(), sender_key))
1304 }
1305
1306 OlmMessage::PreKey(prekey_message) => {
1307 if let Some(sessions) = existing_sessions {
1309 for session in sessions.lock().await.iter_mut() {
1310 if prekey_message.session_id() != session.session_id() {
1311 continue;
1313 }
1314
1315 if let Ok(p) = session.decrypt(message).await {
1316 return Ok((SessionType::Existing(session.clone()), p));
1318 }
1319
1320 warn!(
1334 session_id = session.session_id(),
1335 "Failed to decrypt a pre-key message with the corresponding session"
1336 );
1337
1338 return Err(OlmError::SessionWedged(
1339 session.our_device_keys.user_id.to_owned(),
1340 session.sender_key(),
1341 ));
1342 }
1343 }
1344
1345 let device_keys = store.get_own_device().await?.as_device_keys().clone();
1346 let result =
1347 match self.create_inbound_session(sender_key, device_keys, prekey_message) {
1348 Ok(r) => r,
1349 Err(e) => {
1350 warn!(
1351 "Failed to create a new Olm session from a pre-key message: {e:?}"
1352 );
1353 return Err(OlmError::SessionWedged(sender.to_owned(), sender_key));
1354 }
1355 };
1356
1357 let mut changes =
1362 Changes { sessions: vec![result.session.clone()], ..Default::default() };
1363
1364 if let Some(device) = store.get_device_from_curve_key(sender, sender_key).await? {
1371 let mut device_data = device.inner;
1372 device_data.olm_wedging_index.increment();
1373
1374 changes.devices =
1375 DeviceChanges { changed: vec![device_data], ..Default::default() };
1376 }
1377
1378 store.save_changes(changes).await?;
1379
1380 Ok((SessionType::New(result.session), result.plaintext))
1381 }
1382 }
1383 }
1384
1385 #[instrument(skip(self, store), fields(session, session_id))]
1388 async fn decrypt_and_parse_olm_message(
1389 &mut self,
1390 store: &Store,
1391 sender: &UserId,
1392 sender_key: Curve25519PublicKey,
1393 message: &OlmMessage,
1394 ) -> OlmResult<(SessionType, DecryptionResult)> {
1395 let (session, plaintext) =
1396 self.decrypt_olm_message(store, sender, sender_key, message).await?;
1397
1398 trace!("Successfully decrypted an Olm message");
1399
1400 match self.parse_decrypted_to_device_event(store, sender, sender_key, plaintext).await {
1401 Ok(result) => Ok((session, result)),
1402 Err(e) => {
1403 match session {
1407 SessionType::New(s) | SessionType::Existing(s) => {
1408 store.save_sessions(&[s]).await?;
1409 }
1410 }
1411
1412 warn!(
1413 error = ?e,
1414 "A to-device message was successfully decrypted but \
1415 parsing and checking the event fields failed"
1416 );
1417
1418 Err(e)
1419 }
1420 }
1421 }
1422
1423 async fn parse_decrypted_to_device_event(
1444 &self,
1445 store: &Store,
1446 sender: &UserId,
1447 sender_key: Curve25519PublicKey,
1448 plaintext: String,
1449 ) -> OlmResult<DecryptionResult> {
1450 let event: Box<AnyDecryptedOlmEvent> = serde_json::from_str(&plaintext)?;
1451 let identity_keys = &self.static_data.identity_keys;
1452
1453 if event.recipient() != self.static_data.user_id {
1454 Err(EventError::MismatchedSender(
1455 event.recipient().to_owned(),
1456 self.static_data.user_id.clone(),
1457 )
1458 .into())
1459 }
1460 else if event.sender() != sender {
1463 Err(EventError::MismatchedSender(event.sender().to_owned(), sender.to_owned()).into())
1464 } else if identity_keys.ed25519 != event.recipient_keys().ed25519 {
1465 Err(EventError::MismatchedKeys(
1466 identity_keys.ed25519.into(),
1467 event.recipient_keys().ed25519.into(),
1468 )
1469 .into())
1470 } else {
1471 Self::check_sender_device_keys(event.as_ref(), sender_key)?;
1476 let mut sender_device: Option<Device> = None;
1477 if let AnyDecryptedOlmEvent::RoomKey(_) = event.as_ref() {
1478 } else if let AnyDecryptedOlmEvent::RoomKeyBundle(_) = event.as_ref() {
1483 event.sender_device_keys().ok_or(EventError::MissingSigningKey).inspect_err(
1490 |_| {
1491 warn!("The room key bundle was missing the sender device keys in the event")
1492 },
1493 )?;
1494 } else {
1495 let device = store
1496 .get_device_from_curve_key(event.sender(), sender_key)
1497 .await?
1498 .ok_or(EventError::MissingSigningKey)?;
1499
1500 let key = device.ed25519_key().ok_or(EventError::MissingSigningKey)?;
1501
1502 if key != event.keys().ed25519 {
1503 return Err(EventError::MismatchedKeys(
1504 key.into(),
1505 event.keys().ed25519.into(),
1506 )
1507 .into());
1508 }
1509
1510 sender_device = Some(device);
1515 }
1516
1517 let encryption_info = Self::get_olm_encryption_info(sender_key, sender, &sender_device);
1518
1519 Ok(DecryptionResult {
1520 event,
1521 raw_event: Raw::from_json(RawJsonValue::from_string(plaintext)?),
1522 sender_key,
1523 encryption_info,
1524 })
1525 }
1526 }
1527
1528 fn get_olm_encryption_info(
1535 sender_key: Curve25519PublicKey,
1536 sender_id: &UserId,
1537 sender_device: &Option<Device>,
1538 ) -> EncryptionInfo {
1539 let verification_state = sender_device
1540 .as_ref()
1541 .map(|device| {
1542 if device.is_verified() {
1543 VerificationState::Verified
1545 } else if device.is_cross_signed_by_owner() {
1546 if device
1548 .device_owner_identity
1549 .as_ref()
1550 .expect("A device cross-signed by the owner must have an owner identity")
1551 .was_previously_verified()
1552 {
1553 VerificationState::Unverified(VerificationLevel::VerificationViolation)
1554 } else {
1555 VerificationState::Unverified(VerificationLevel::UnverifiedIdentity)
1556 }
1557 } else {
1558 VerificationState::Unverified(VerificationLevel::UnsignedDevice)
1560 }
1561 })
1562 .unwrap_or(VerificationState::Unverified(VerificationLevel::None(
1563 DeviceLinkProblem::MissingDevice,
1564 )));
1565
1566 let encryption_info = EncryptionInfo {
1567 sender: sender_id.to_owned(),
1568 sender_device: sender_device.as_ref().map(|d| d.device_id().to_owned()),
1569 algorithm_info: AlgorithmInfo::OlmV1Curve25519AesSha2 {
1570 curve25519_public_key_base64: sender_key.to_base64(),
1571 },
1572 verification_state,
1573 };
1574 encryption_info
1575 }
1576
1577 fn check_sender_device_keys(
1587 event: &AnyDecryptedOlmEvent,
1588 sender_key: Curve25519PublicKey,
1589 ) -> OlmResult<()> {
1590 let Some(sender_device_keys) = event.sender_device_keys() else {
1591 return Ok(());
1592 };
1593
1594 let sender_device_data = DeviceData::try_from(sender_device_keys).map_err(|err| {
1596 warn!(
1597 "Received a to-device message with sender_device_keys with \
1598 invalid signature: {err:?}",
1599 );
1600 OlmError::EventError(EventError::InvalidSenderDeviceKeys)
1601 })?;
1602
1603 if sender_device_data.ed25519_key() != Some(event.keys().ed25519) {
1606 warn!(
1607 "Received a to-device message with sender_device_keys with incorrect \
1608 ed25519 key: expected {:?}, got {:?}",
1609 event.keys().ed25519,
1610 sender_device_data.ed25519_key(),
1611 );
1612 return Err(OlmError::EventError(EventError::InvalidSenderDeviceKeys));
1613 }
1614
1615 if sender_device_data.curve25519_key() != Some(sender_key) {
1618 warn!(
1619 "Received a to-device message with sender_device_keys with incorrect \
1620 curve25519 key: expected {sender_key:?}, got {:?}",
1621 sender_device_data.curve25519_key(),
1622 );
1623 return Err(OlmError::EventError(EventError::InvalidSenderDeviceKeys));
1624 }
1625
1626 Ok(())
1627 }
1628
1629 #[doc(hidden)]
1634 pub fn deep_clone(&self) -> Self {
1635 Self::from_pickle(self.pickle()).unwrap()
1637 }
1638}
1639
1640impl PartialEq for Account {
1641 fn eq(&self, other: &Self) -> bool {
1642 self.identity_keys() == other.identity_keys() && self.shared() == other.shared()
1643 }
1644}
1645
1646pub(crate) fn shared_history_from_history_visibility(
1666 history_visibility: &HistoryVisibility,
1667) -> bool {
1668 match history_visibility {
1669 HistoryVisibility::Shared | HistoryVisibility::WorldReadable => true,
1670 HistoryVisibility::Invited | HistoryVisibility::Joined | _ => false,
1671 }
1672}
1673
1674fn expand_legacy_pickle_key(key: &[u8; 32], device_id: &DeviceId) -> Box<[u8; 32]> {
1693 let kdf: Hkdf<Sha256> = Hkdf::new(Some(device_id.as_bytes()), key);
1694 let mut key = Box::new([0u8; 32]);
1695
1696 kdf.expand(b"dehydrated-device-pickle-key", key.as_mut_slice())
1697 .expect("We should be able to expand the 32 byte pickle key");
1698
1699 key
1700}
1701
1702#[cfg(test)]
1703mod tests {
1704 use std::{
1705 collections::{BTreeMap, BTreeSet},
1706 ops::Deref,
1707 time::Duration,
1708 };
1709
1710 use anyhow::Result;
1711 use matrix_sdk_test::async_test;
1712 use ruma::{
1713 device_id, events::room::history_visibility::HistoryVisibility, room_id, user_id, DeviceId,
1714 MilliSecondsSinceUnixEpoch, OneTimeKeyAlgorithm, OneTimeKeyId, UserId,
1715 };
1716 use serde_json::json;
1717
1718 use super::Account;
1719 use crate::{
1720 olm::{account::shared_history_from_history_visibility, SignedJsonObject},
1721 types::{DeviceKeys, SignedKey},
1722 DeviceData, EncryptionSettings,
1723 };
1724
1725 fn user_id() -> &'static UserId {
1726 user_id!("@alice:localhost")
1727 }
1728
1729 fn device_id() -> &'static DeviceId {
1730 device_id!("DEVICEID")
1731 }
1732
1733 #[test]
1734 fn test_one_time_key_creation() -> Result<()> {
1735 let mut account = Account::with_device_id(user_id(), device_id());
1736
1737 let (_, one_time_keys, _) = account.keys_for_upload();
1738 assert!(!one_time_keys.is_empty());
1739
1740 let (_, second_one_time_keys, _) = account.keys_for_upload();
1741 assert!(!second_one_time_keys.is_empty());
1742
1743 let one_time_key_ids: BTreeSet<&OneTimeKeyId> =
1744 one_time_keys.keys().map(Deref::deref).collect();
1745 let second_one_time_key_ids: BTreeSet<&OneTimeKeyId> =
1746 second_one_time_keys.keys().map(Deref::deref).collect();
1747
1748 assert_eq!(one_time_key_ids, second_one_time_key_ids);
1749
1750 account.mark_keys_as_published();
1751 account.update_uploaded_key_count(50);
1752 account.generate_one_time_keys_if_needed();
1753
1754 let (_, third_one_time_keys, _) = account.keys_for_upload();
1755 assert!(third_one_time_keys.is_empty());
1756
1757 account.update_uploaded_key_count(0);
1758 account.generate_one_time_keys_if_needed();
1759
1760 let (_, fourth_one_time_keys, _) = account.keys_for_upload();
1761 assert!(!fourth_one_time_keys.is_empty());
1762
1763 let fourth_one_time_key_ids: BTreeSet<&OneTimeKeyId> =
1764 fourth_one_time_keys.keys().map(Deref::deref).collect();
1765
1766 assert_ne!(one_time_key_ids, fourth_one_time_key_ids);
1767 Ok(())
1768 }
1769
1770 #[test]
1771 fn test_fallback_key_creation() -> Result<()> {
1772 let mut account = Account::with_device_id(user_id(), device_id());
1773
1774 let (_, _, fallback_keys) = account.keys_for_upload();
1775
1776 assert!(
1780 fallback_keys.is_empty(),
1781 "We should not upload fallback keys until we know if the server supports them."
1782 );
1783
1784 let one_time_keys = BTreeMap::from([(OneTimeKeyAlgorithm::SignedCurve25519, 50u8.into())]);
1785
1786 account.update_key_counts(&one_time_keys, None);
1789 let (_, _, fallback_keys) = account.keys_for_upload();
1790 assert!(
1791 fallback_keys.is_empty(),
1792 "We should not upload a fallback key if we're certain that the server doesn't support \
1793 them."
1794 );
1795
1796 let unused_fallback_keys = &[];
1800 account.update_key_counts(&one_time_keys, Some(unused_fallback_keys.as_ref()));
1801 let (_, _, fallback_keys) = account.keys_for_upload();
1802 assert!(
1803 !fallback_keys.is_empty(),
1804 "We should upload the initial fallback key if the server supports them."
1805 );
1806 account.mark_keys_as_published();
1807
1808 let unused_fallback_keys = &[];
1811 account.update_key_counts(&one_time_keys, Some(unused_fallback_keys.as_ref()));
1812 let (_, _, fallback_keys) = account.keys_for_upload();
1813 assert!(
1814 fallback_keys.is_empty(),
1815 "We should not upload new fallback keys unless our current fallback key expires."
1816 );
1817
1818 let fallback_key_timestamp =
1819 account.fallback_creation_timestamp.unwrap().to_system_time().unwrap()
1820 - Duration::from_secs(3600 * 24 * 30);
1821
1822 account.fallback_creation_timestamp =
1823 Some(MilliSecondsSinceUnixEpoch::from_system_time(fallback_key_timestamp).unwrap());
1824
1825 account.update_key_counts(&one_time_keys, None);
1826 let (_, _, fallback_keys) = account.keys_for_upload();
1827 assert!(
1828 !fallback_keys.is_empty(),
1829 "Now that our fallback key has expired, we should try to upload a new one, even if the \
1830 server supposedly doesn't support fallback keys anymore"
1831 );
1832
1833 Ok(())
1834 }
1835
1836 #[test]
1837 fn test_fallback_key_signing() -> Result<()> {
1838 let key = vodozemac::Curve25519PublicKey::from_base64(
1839 "7PUPP6Ijt5R8qLwK2c8uK5hqCNF9tOzWYgGaAay5JBs",
1840 )?;
1841 let account = Account::with_device_id(user_id(), device_id());
1842
1843 let key = account.sign_key(key, true);
1844
1845 let canonical_key = key.to_canonical_json()?;
1846
1847 assert_eq!(
1848 canonical_key,
1849 "{\"fallback\":true,\"key\":\"7PUPP6Ijt5R8qLwK2c8uK5hqCNF9tOzWYgGaAay5JBs\"}"
1850 );
1851
1852 account
1853 .has_signed_raw(key.signatures(), &canonical_key)
1854 .expect("Couldn't verify signature");
1855
1856 let device = DeviceData::from_account(&account);
1857 device.verify_one_time_key(&key).expect("The device can verify its own signature");
1858
1859 Ok(())
1860 }
1861
1862 #[test]
1863 fn test_account_and_device_creation_timestamp() -> Result<()> {
1864 let now = MilliSecondsSinceUnixEpoch::now();
1865 let account = Account::with_device_id(user_id(), device_id());
1866 let then = MilliSecondsSinceUnixEpoch::now();
1867
1868 assert!(account.creation_local_time() >= now);
1869 assert!(account.creation_local_time() <= then);
1870
1871 let device = DeviceData::from_account(&account);
1872 assert_eq!(account.creation_local_time(), device.first_time_seen_ts());
1873
1874 Ok(())
1875 }
1876
1877 #[async_test]
1878 async fn test_fallback_key_signature_verification() -> Result<()> {
1879 let fallback_key = json!({
1880 "fallback": true,
1881 "key": "XPFqtLvBepBmW6jSAbBuJbhEpprBhQOX1IjUu+cnMF4",
1882 "signatures": {
1883 "@dkasak_c:matrix.org": {
1884 "ed25519:EXPDYDPWZH": "RJCBMJPL5hvjxgq8rmLmqkNOuPsaan7JeL1wsE+gW6R39G894lb2sBmzapHeKCn/KFjmkonPLkICApRDS+zyDw"
1885 }
1886 }
1887 });
1888
1889 let device_keys = json!({
1890 "algorithms": [
1891 "m.olm.v1.curve25519-aes-sha2",
1892 "m.megolm.v1.aes-sha2"
1893 ],
1894 "device_id": "EXPDYDPWZH",
1895 "keys": {
1896 "curve25519:EXPDYDPWZH": "k7f3igo0Vrdm88JSSA5d3OCuUfHYELChB2b57aOROB8",
1897 "ed25519:EXPDYDPWZH": "GdjYI8fxs175gSpYRJkyN6FRfvcyTsNOhJ2OR/Ggp+E"
1898 },
1899 "signatures": {
1900 "@dkasak_c:matrix.org": {
1901 "ed25519:EXPDYDPWZH": "kzrtfQMbJXWXQ1uzhybtwFnGk0JJBS4Mg8VPMusMu6U8MPJccwoHVZKo5+owuHTzIodI+GZYqLmMSzvfvsChAA"
1902 }
1903 },
1904 "user_id": "@dkasak_c:matrix.org",
1905 "unsigned": {}
1906 });
1907
1908 let device_keys: DeviceKeys = serde_json::from_value(device_keys).unwrap();
1909 let device = DeviceData::try_from(&device_keys).unwrap();
1910 let fallback_key: SignedKey = serde_json::from_value(fallback_key).unwrap();
1911
1912 device
1913 .verify_one_time_key(&fallback_key)
1914 .expect("The fallback key should pass the signature verification");
1915
1916 Ok(())
1917 }
1918
1919 #[test]
1920 fn test_shared_history_flag_from_history_visibility() {
1921 assert!(
1922 shared_history_from_history_visibility(&HistoryVisibility::WorldReadable),
1923 "The world readable visibility should set the shared history flag to true"
1924 );
1925
1926 assert!(
1927 shared_history_from_history_visibility(&HistoryVisibility::Shared),
1928 "The shared visibility should set the shared history flag to true"
1929 );
1930
1931 assert!(
1932 !shared_history_from_history_visibility(&HistoryVisibility::Joined),
1933 "The joined visibility should set the shared history flag to false"
1934 );
1935
1936 assert!(
1937 !shared_history_from_history_visibility(&HistoryVisibility::Invited),
1938 "The invited visibility should set the shared history flag to false"
1939 );
1940
1941 let visibility = HistoryVisibility::from("custom_visibility");
1942 assert!(
1943 !shared_history_from_history_visibility(&visibility),
1944 "A custom visibility should set the shared history flag to false"
1945 );
1946 }
1947
1948 #[async_test]
1949 async fn test_shared_history_set_when_creating_group_sessions() {
1950 let account = Account::new(user_id());
1951 let room_id = room_id!("!room:id");
1952 let settings = EncryptionSettings {
1953 history_visibility: HistoryVisibility::Shared,
1954 ..Default::default()
1955 };
1956
1957 let (_, session) = account
1958 .create_group_session_pair(room_id, settings, Default::default())
1959 .await
1960 .expect("We should be able to create a group session pair");
1961
1962 assert!(
1963 session.shared_history(),
1964 "The shared history flag should have been set when we created the new session"
1965 );
1966 }
1967}