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 DecryptionSettings, Device, OlmError, SignatureError, TrustRequirement,
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 decryption_settings: &DecryptionSettings,
1168 ) -> OlmResult<OlmDecryptionInfo> {
1169 let message_hash = OlmMessageHash::new(sender_key, ciphertext);
1170
1171 match self
1172 .decrypt_and_parse_olm_message(
1173 store,
1174 sender,
1175 sender_key,
1176 ciphertext,
1177 decryption_settings,
1178 )
1179 .await
1180 {
1181 Ok((session, result)) => {
1182 Ok(OlmDecryptionInfo { session, message_hash, result, inbound_group_session: None })
1183 }
1184 Err(OlmError::SessionWedged(user_id, sender_key)) => {
1185 if store.is_message_known(&message_hash).await? {
1186 info!(?sender_key, "An Olm message got replayed, decryption failed");
1187 Err(OlmError::ReplayedMessage(user_id, sender_key))
1188 } else {
1189 Err(OlmError::SessionWedged(user_id, sender_key))
1190 }
1191 }
1192 Err(e) => Err(e),
1193 }
1194 }
1195
1196 #[cfg(feature = "experimental-algorithms")]
1197 async fn decrypt_olm_v2(
1198 &mut self,
1199 store: &Store,
1200 sender: &UserId,
1201 content: &OlmV2Curve25519AesSha2Content,
1202 decryption_settings: &DecryptionSettings,
1203 ) -> OlmResult<OlmDecryptionInfo> {
1204 self.decrypt_olm_helper(
1205 store,
1206 sender,
1207 content.sender_key,
1208 &content.ciphertext,
1209 decryption_settings,
1210 )
1211 .await
1212 }
1213
1214 #[instrument(skip_all, fields(sender, sender_key = ?content.sender_key))]
1215 async fn decrypt_olm_v1(
1216 &mut self,
1217 store: &Store,
1218 sender: &UserId,
1219 content: &OlmV1Curve25519AesSha2Content,
1220 decryption_settings: &DecryptionSettings,
1221 ) -> OlmResult<OlmDecryptionInfo> {
1222 if content.recipient_key != self.static_data.identity_keys.curve25519 {
1223 warn!("Olm event doesn't contain a ciphertext for our key");
1224
1225 Err(EventError::MissingCiphertext.into())
1226 } else {
1227 Box::pin(self.decrypt_olm_helper(
1228 store,
1229 sender,
1230 content.sender_key,
1231 &content.ciphertext,
1232 decryption_settings,
1233 ))
1234 .await
1235 }
1236 }
1237
1238 #[instrument(skip_all, fields(algorithm = ?event.content.algorithm()))]
1239 pub(crate) async fn decrypt_to_device_event(
1240 &mut self,
1241 store: &Store,
1242 event: &EncryptedToDeviceEvent,
1243 decryption_settings: &DecryptionSettings,
1244 ) -> OlmResult<OlmDecryptionInfo> {
1245 trace!("Decrypting a to-device event");
1246
1247 match &event.content {
1248 ToDeviceEncryptedEventContent::OlmV1Curve25519AesSha2(c) => {
1249 self.decrypt_olm_v1(store, &event.sender, c, decryption_settings).await
1250 }
1251 #[cfg(feature = "experimental-algorithms")]
1252 ToDeviceEncryptedEventContent::OlmV2Curve25519AesSha2(c) => {
1253 self.decrypt_olm_v2(store, &event.sender, c, decryption_settings).await
1254 }
1255 ToDeviceEncryptedEventContent::Unknown(_) => {
1256 warn!(
1257 "Error decrypting an to-device event, unsupported \
1258 encryption algorithm"
1259 );
1260
1261 Err(EventError::UnsupportedAlgorithm.into())
1262 }
1263 }
1264 }
1265
1266 pub fn receive_keys_upload_response(
1268 &mut self,
1269 response: &upload_keys::v3::Response,
1270 ) -> OlmResult<()> {
1271 if !self.shared() {
1272 debug!("Marking account as shared");
1273 }
1274 self.mark_as_shared();
1275
1276 debug!("Marking one-time keys as published");
1277 self.mark_keys_as_published();
1280 self.update_key_counts(&response.one_time_key_counts, None);
1281
1282 Ok(())
1283 }
1284
1285 async fn decrypt_olm_message(
1287 &mut self,
1288 store: &Store,
1289 sender: &UserId,
1290 sender_key: Curve25519PublicKey,
1291 message: &OlmMessage,
1292 ) -> Result<(SessionType, String), OlmError> {
1293 let existing_sessions = store.get_sessions(&sender_key.to_base64()).await?;
1294
1295 match message {
1296 OlmMessage::Normal(_) => {
1297 let mut errors_by_olm_session = Vec::new();
1298
1299 if let Some(sessions) = existing_sessions {
1300 for session in sessions.lock().await.iter_mut() {
1303 match session.decrypt(message).await {
1304 Ok(p) => {
1305 return Ok((SessionType::Existing(session.clone()), p));
1307 }
1308
1309 Err(e) => {
1310 errors_by_olm_session.push((session.session_id().to_owned(), e));
1315 }
1316 }
1317 }
1318 }
1319
1320 warn!(
1321 ?errors_by_olm_session,
1322 "Failed to decrypt a non-pre-key message with all available sessions"
1323 );
1324 Err(OlmError::SessionWedged(sender.to_owned(), sender_key))
1325 }
1326
1327 OlmMessage::PreKey(prekey_message) => {
1328 if let Some(sessions) = existing_sessions {
1330 for session in sessions.lock().await.iter_mut() {
1331 if prekey_message.session_id() != session.session_id() {
1332 continue;
1334 }
1335
1336 if let Ok(p) = session.decrypt(message).await {
1337 return Ok((SessionType::Existing(session.clone()), p));
1339 }
1340
1341 warn!(
1355 session_id = session.session_id(),
1356 "Failed to decrypt a pre-key message with the corresponding session"
1357 );
1358
1359 return Err(OlmError::SessionWedged(
1360 session.our_device_keys.user_id.to_owned(),
1361 session.sender_key(),
1362 ));
1363 }
1364 }
1365
1366 let device_keys = store.get_own_device().await?.as_device_keys().clone();
1367 let result =
1368 match self.create_inbound_session(sender_key, device_keys, prekey_message) {
1369 Ok(r) => r,
1370 Err(e) => {
1371 warn!(
1372 "Failed to create a new Olm session from a pre-key message: {e:?}"
1373 );
1374 return Err(OlmError::SessionWedged(sender.to_owned(), sender_key));
1375 }
1376 };
1377
1378 let mut changes =
1383 Changes { sessions: vec![result.session.clone()], ..Default::default() };
1384
1385 if let Some(device) = store.get_device_from_curve_key(sender, sender_key).await? {
1392 let mut device_data = device.inner;
1393 device_data.olm_wedging_index.increment();
1394
1395 changes.devices =
1396 DeviceChanges { changed: vec![device_data], ..Default::default() };
1397 }
1398
1399 store.save_changes(changes).await?;
1400
1401 Ok((SessionType::New(result.session), result.plaintext))
1402 }
1403 }
1404 }
1405
1406 #[instrument(skip(self, store), fields(session, session_id))]
1409 async fn decrypt_and_parse_olm_message(
1410 &mut self,
1411 store: &Store,
1412 sender: &UserId,
1413 sender_key: Curve25519PublicKey,
1414 message: &OlmMessage,
1415 decryption_settings: &DecryptionSettings,
1416 ) -> OlmResult<(SessionType, DecryptionResult)> {
1417 let (session, plaintext) =
1418 self.decrypt_olm_message(store, sender, sender_key, message).await?;
1419
1420 trace!("Successfully decrypted an Olm message");
1421
1422 match self
1423 .parse_decrypted_to_device_event(
1424 store,
1425 sender,
1426 sender_key,
1427 plaintext,
1428 decryption_settings,
1429 )
1430 .await
1431 {
1432 Ok(result) => Ok((session, result)),
1433 Err(e) => {
1434 match session {
1438 SessionType::New(s) | SessionType::Existing(s) => {
1439 store.save_sessions(&[s]).await?;
1440 }
1441 }
1442
1443 warn!(
1444 error = ?e,
1445 "A to-device message was successfully decrypted but \
1446 parsing and checking the event fields failed"
1447 );
1448
1449 Err(e)
1450 }
1451 }
1452 }
1453
1454 async fn parse_decrypted_to_device_event(
1475 &self,
1476 store: &Store,
1477 sender: &UserId,
1478 sender_key: Curve25519PublicKey,
1479 plaintext: String,
1480 decryption_settings: &DecryptionSettings,
1481 ) -> OlmResult<DecryptionResult> {
1482 let event: Box<AnyDecryptedOlmEvent> = serde_json::from_str(&plaintext)?;
1483 let identity_keys = &self.static_data.identity_keys;
1484
1485 if event.recipient() != self.static_data.user_id {
1486 Err(EventError::MismatchedSender(
1487 event.recipient().to_owned(),
1488 self.static_data.user_id.clone(),
1489 )
1490 .into())
1491 }
1492 else if event.sender() != sender {
1495 Err(EventError::MismatchedSender(event.sender().to_owned(), sender.to_owned()).into())
1496 } else if identity_keys.ed25519 != event.recipient_keys().ed25519 {
1497 Err(EventError::MismatchedKeys(
1498 identity_keys.ed25519.into(),
1499 event.recipient_keys().ed25519.into(),
1500 )
1501 .into())
1502 } else {
1503 Self::check_sender_device_keys(event.as_ref(), sender_key)?;
1508 let mut sender_device: Option<Device> = None;
1509 if let AnyDecryptedOlmEvent::RoomKey(_) = event.as_ref() {
1510 } else if let AnyDecryptedOlmEvent::RoomKeyBundle(_) = event.as_ref() {
1515 event.sender_device_keys().ok_or(EventError::MissingSigningKey).inspect_err(
1522 |_| {
1523 warn!("The room key bundle was missing the sender device keys in the event")
1524 },
1525 )?;
1526 } else {
1527 let device = store
1528 .get_device_from_curve_key(event.sender(), sender_key)
1529 .await?
1530 .ok_or(EventError::MissingSigningKey)?;
1531
1532 let key = device.ed25519_key().ok_or(EventError::MissingSigningKey)?;
1533
1534 if key != event.keys().ed25519 {
1535 return Err(EventError::MismatchedKeys(
1536 key.into(),
1537 event.keys().ed25519.into(),
1538 )
1539 .into());
1540 }
1541
1542 sender_device = Some(device);
1547 }
1548
1549 let encryption_info = Self::get_olm_encryption_info(sender_key, sender, &sender_device);
1550
1551 let result = DecryptionResult {
1552 event,
1553 raw_event: Raw::from_json(RawJsonValue::from_string(plaintext)?),
1554 sender_key,
1555 encryption_info,
1556 };
1557
1558 if !self.is_from_verified_device_or_allowed_type(decryption_settings, &result) {
1560 Err(OlmError::UnverifiedSenderDevice)
1561 } else {
1562 Ok(result)
1564 }
1565 }
1566 }
1567
1568 fn is_from_verified_device_or_allowed_type(
1581 &self,
1582 decryption_settings: &DecryptionSettings,
1583 result: &DecryptionResult,
1584 ) -> bool {
1585 let event_type = result.event.event_type();
1586
1587 match event_type {
1599 "m.room_key"
1600 | "m.room_key.withheld"
1601 | "m.room_key_request"
1602 | "m.secret.request"
1603 | "m.key.verification.key"
1604 | "m.key.verification.mac"
1605 | "m.key.verification.done"
1606 | "m.key.verification.ready"
1607 | "m.key.verification.start"
1608 | "m.key.verification.accept"
1609 | "m.key.verification.cancel"
1610 | "m.key.verification.request" => {
1611 true
1614 }
1615 _ => {
1616 satisfies_sender_trust_requirement(
1619 &result.encryption_info,
1620 &decryption_settings.sender_device_trust_requirement,
1621 )
1622 }
1623 }
1624 }
1625
1626 fn get_olm_encryption_info(
1633 sender_key: Curve25519PublicKey,
1634 sender_id: &UserId,
1635 sender_device: &Option<Device>,
1636 ) -> EncryptionInfo {
1637 let verification_state = sender_device
1638 .as_ref()
1639 .map(|device| {
1640 if device.is_verified() {
1641 VerificationState::Verified
1643 } else if device.is_cross_signed_by_owner() {
1644 if device
1646 .device_owner_identity
1647 .as_ref()
1648 .expect("A device cross-signed by the owner must have an owner identity")
1649 .was_previously_verified()
1650 {
1651 VerificationState::Unverified(VerificationLevel::VerificationViolation)
1652 } else {
1653 VerificationState::Unverified(VerificationLevel::UnverifiedIdentity)
1654 }
1655 } else {
1656 VerificationState::Unverified(VerificationLevel::UnsignedDevice)
1658 }
1659 })
1660 .unwrap_or(VerificationState::Unverified(VerificationLevel::None(
1661 DeviceLinkProblem::MissingDevice,
1662 )));
1663
1664 let encryption_info = EncryptionInfo {
1665 sender: sender_id.to_owned(),
1666 sender_device: sender_device.as_ref().map(|d| d.device_id().to_owned()),
1667 algorithm_info: AlgorithmInfo::OlmV1Curve25519AesSha2 {
1668 curve25519_public_key_base64: sender_key.to_base64(),
1669 },
1670 verification_state,
1671 };
1672 encryption_info
1673 }
1674
1675 fn check_sender_device_keys(
1685 event: &AnyDecryptedOlmEvent,
1686 sender_key: Curve25519PublicKey,
1687 ) -> OlmResult<()> {
1688 let Some(sender_device_keys) = event.sender_device_keys() else {
1689 return Ok(());
1690 };
1691
1692 let sender_device_data = DeviceData::try_from(sender_device_keys).map_err(|err| {
1694 warn!(
1695 "Received a to-device message with sender_device_keys with \
1696 invalid signature: {err:?}",
1697 );
1698 OlmError::EventError(EventError::InvalidSenderDeviceKeys)
1699 })?;
1700
1701 if sender_device_data.ed25519_key() != Some(event.keys().ed25519) {
1704 warn!(
1705 "Received a to-device message with sender_device_keys with incorrect \
1706 ed25519 key: expected {:?}, got {:?}",
1707 event.keys().ed25519,
1708 sender_device_data.ed25519_key(),
1709 );
1710 return Err(OlmError::EventError(EventError::InvalidSenderDeviceKeys));
1711 }
1712
1713 if sender_device_data.curve25519_key() != Some(sender_key) {
1716 warn!(
1717 "Received a to-device message with sender_device_keys with incorrect \
1718 curve25519 key: expected {sender_key:?}, got {:?}",
1719 sender_device_data.curve25519_key(),
1720 );
1721 return Err(OlmError::EventError(EventError::InvalidSenderDeviceKeys));
1722 }
1723
1724 Ok(())
1725 }
1726
1727 #[doc(hidden)]
1732 pub fn deep_clone(&self) -> Self {
1733 Self::from_pickle(self.pickle()).unwrap()
1735 }
1736}
1737
1738impl PartialEq for Account {
1739 fn eq(&self, other: &Self) -> bool {
1740 self.identity_keys() == other.identity_keys() && self.shared() == other.shared()
1741 }
1742}
1743
1744pub(crate) fn shared_history_from_history_visibility(
1764 history_visibility: &HistoryVisibility,
1765) -> bool {
1766 match history_visibility {
1767 HistoryVisibility::Shared | HistoryVisibility::WorldReadable => true,
1768 HistoryVisibility::Invited | HistoryVisibility::Joined | _ => false,
1769 }
1770}
1771
1772fn expand_legacy_pickle_key(key: &[u8; 32], device_id: &DeviceId) -> Box<[u8; 32]> {
1791 let kdf: Hkdf<Sha256> = Hkdf::new(Some(device_id.as_bytes()), key);
1792 let mut key = Box::new([0u8; 32]);
1793
1794 kdf.expand(b"dehydrated-device-pickle-key", key.as_mut_slice())
1795 .expect("We should be able to expand the 32 byte pickle key");
1796
1797 key
1798}
1799
1800fn satisfies_sender_trust_requirement(
1803 encryption_info: &EncryptionInfo,
1804 trust_requirement: &TrustRequirement,
1805) -> bool {
1806 trace!(
1807 verification_state = ?encryption_info.verification_state,
1808 ?trust_requirement, "check_to_device_sender_trust_requirement",
1809 );
1810
1811 match (&encryption_info.verification_state, trust_requirement) {
1812 (_, TrustRequirement::Untrusted) => true,
1814
1815 (VerificationState::Verified, _) => true,
1817
1818 (
1821 VerificationState::Unverified(verification_level),
1822 TrustRequirement::CrossSignedOrLegacy | TrustRequirement::CrossSigned,
1823 ) => match verification_level {
1824 VerificationLevel::UnverifiedIdentity => true,
1826
1827 VerificationLevel::UnsignedDevice
1830 | VerificationLevel::None(_)
1831 | VerificationLevel::VerificationViolation
1832 | VerificationLevel::MismatchedSender => false,
1833 },
1834 }
1835}
1836
1837#[cfg(test)]
1838mod tests {
1839 use std::{
1840 collections::{BTreeMap, BTreeSet},
1841 ops::Deref,
1842 time::Duration,
1843 };
1844
1845 use anyhow::Result;
1846 use matrix_sdk_test::async_test;
1847 use ruma::{
1848 device_id, events::room::history_visibility::HistoryVisibility, room_id, user_id, DeviceId,
1849 MilliSecondsSinceUnixEpoch, OneTimeKeyAlgorithm, OneTimeKeyId, UserId,
1850 };
1851 use serde_json::json;
1852
1853 use super::Account;
1854 use crate::{
1855 olm::{account::shared_history_from_history_visibility, SignedJsonObject},
1856 types::{DeviceKeys, SignedKey},
1857 DeviceData, EncryptionSettings,
1858 };
1859
1860 fn user_id() -> &'static UserId {
1861 user_id!("@alice:localhost")
1862 }
1863
1864 fn device_id() -> &'static DeviceId {
1865 device_id!("DEVICEID")
1866 }
1867
1868 #[test]
1869 fn test_one_time_key_creation() -> Result<()> {
1870 let mut account = Account::with_device_id(user_id(), device_id());
1871
1872 let (_, one_time_keys, _) = account.keys_for_upload();
1873 assert!(!one_time_keys.is_empty());
1874
1875 let (_, second_one_time_keys, _) = account.keys_for_upload();
1876 assert!(!second_one_time_keys.is_empty());
1877
1878 let one_time_key_ids: BTreeSet<&OneTimeKeyId> =
1879 one_time_keys.keys().map(Deref::deref).collect();
1880 let second_one_time_key_ids: BTreeSet<&OneTimeKeyId> =
1881 second_one_time_keys.keys().map(Deref::deref).collect();
1882
1883 assert_eq!(one_time_key_ids, second_one_time_key_ids);
1884
1885 account.mark_keys_as_published();
1886 account.update_uploaded_key_count(50);
1887 account.generate_one_time_keys_if_needed();
1888
1889 let (_, third_one_time_keys, _) = account.keys_for_upload();
1890 assert!(third_one_time_keys.is_empty());
1891
1892 account.update_uploaded_key_count(0);
1893 account.generate_one_time_keys_if_needed();
1894
1895 let (_, fourth_one_time_keys, _) = account.keys_for_upload();
1896 assert!(!fourth_one_time_keys.is_empty());
1897
1898 let fourth_one_time_key_ids: BTreeSet<&OneTimeKeyId> =
1899 fourth_one_time_keys.keys().map(Deref::deref).collect();
1900
1901 assert_ne!(one_time_key_ids, fourth_one_time_key_ids);
1902 Ok(())
1903 }
1904
1905 #[test]
1906 fn test_fallback_key_creation() -> Result<()> {
1907 let mut account = Account::with_device_id(user_id(), device_id());
1908
1909 let (_, _, fallback_keys) = account.keys_for_upload();
1910
1911 assert!(
1915 fallback_keys.is_empty(),
1916 "We should not upload fallback keys until we know if the server supports them."
1917 );
1918
1919 let one_time_keys = BTreeMap::from([(OneTimeKeyAlgorithm::SignedCurve25519, 50u8.into())]);
1920
1921 account.update_key_counts(&one_time_keys, None);
1924 let (_, _, fallback_keys) = account.keys_for_upload();
1925 assert!(
1926 fallback_keys.is_empty(),
1927 "We should not upload a fallback key if we're certain that the server doesn't support \
1928 them."
1929 );
1930
1931 let unused_fallback_keys = &[];
1935 account.update_key_counts(&one_time_keys, Some(unused_fallback_keys.as_ref()));
1936 let (_, _, fallback_keys) = account.keys_for_upload();
1937 assert!(
1938 !fallback_keys.is_empty(),
1939 "We should upload the initial fallback key if the server supports them."
1940 );
1941 account.mark_keys_as_published();
1942
1943 let unused_fallback_keys = &[];
1946 account.update_key_counts(&one_time_keys, Some(unused_fallback_keys.as_ref()));
1947 let (_, _, fallback_keys) = account.keys_for_upload();
1948 assert!(
1949 fallback_keys.is_empty(),
1950 "We should not upload new fallback keys unless our current fallback key expires."
1951 );
1952
1953 let fallback_key_timestamp =
1954 account.fallback_creation_timestamp.unwrap().to_system_time().unwrap()
1955 - Duration::from_secs(3600 * 24 * 30);
1956
1957 account.fallback_creation_timestamp =
1958 Some(MilliSecondsSinceUnixEpoch::from_system_time(fallback_key_timestamp).unwrap());
1959
1960 account.update_key_counts(&one_time_keys, None);
1961 let (_, _, fallback_keys) = account.keys_for_upload();
1962 assert!(
1963 !fallback_keys.is_empty(),
1964 "Now that our fallback key has expired, we should try to upload a new one, even if the \
1965 server supposedly doesn't support fallback keys anymore"
1966 );
1967
1968 Ok(())
1969 }
1970
1971 #[test]
1972 fn test_fallback_key_signing() -> Result<()> {
1973 let key = vodozemac::Curve25519PublicKey::from_base64(
1974 "7PUPP6Ijt5R8qLwK2c8uK5hqCNF9tOzWYgGaAay5JBs",
1975 )?;
1976 let account = Account::with_device_id(user_id(), device_id());
1977
1978 let key = account.sign_key(key, true);
1979
1980 let canonical_key = key.to_canonical_json()?;
1981
1982 assert_eq!(
1983 canonical_key,
1984 "{\"fallback\":true,\"key\":\"7PUPP6Ijt5R8qLwK2c8uK5hqCNF9tOzWYgGaAay5JBs\"}"
1985 );
1986
1987 account
1988 .has_signed_raw(key.signatures(), &canonical_key)
1989 .expect("Couldn't verify signature");
1990
1991 let device = DeviceData::from_account(&account);
1992 device.verify_one_time_key(&key).expect("The device can verify its own signature");
1993
1994 Ok(())
1995 }
1996
1997 #[test]
1998 fn test_account_and_device_creation_timestamp() -> Result<()> {
1999 let now = MilliSecondsSinceUnixEpoch::now();
2000 let account = Account::with_device_id(user_id(), device_id());
2001 let then = MilliSecondsSinceUnixEpoch::now();
2002
2003 assert!(account.creation_local_time() >= now);
2004 assert!(account.creation_local_time() <= then);
2005
2006 let device = DeviceData::from_account(&account);
2007 assert_eq!(account.creation_local_time(), device.first_time_seen_ts());
2008
2009 Ok(())
2010 }
2011
2012 #[async_test]
2013 async fn test_fallback_key_signature_verification() -> Result<()> {
2014 let fallback_key = json!({
2015 "fallback": true,
2016 "key": "XPFqtLvBepBmW6jSAbBuJbhEpprBhQOX1IjUu+cnMF4",
2017 "signatures": {
2018 "@dkasak_c:matrix.org": {
2019 "ed25519:EXPDYDPWZH": "RJCBMJPL5hvjxgq8rmLmqkNOuPsaan7JeL1wsE+gW6R39G894lb2sBmzapHeKCn/KFjmkonPLkICApRDS+zyDw"
2020 }
2021 }
2022 });
2023
2024 let device_keys = json!({
2025 "algorithms": [
2026 "m.olm.v1.curve25519-aes-sha2",
2027 "m.megolm.v1.aes-sha2"
2028 ],
2029 "device_id": "EXPDYDPWZH",
2030 "keys": {
2031 "curve25519:EXPDYDPWZH": "k7f3igo0Vrdm88JSSA5d3OCuUfHYELChB2b57aOROB8",
2032 "ed25519:EXPDYDPWZH": "GdjYI8fxs175gSpYRJkyN6FRfvcyTsNOhJ2OR/Ggp+E"
2033 },
2034 "signatures": {
2035 "@dkasak_c:matrix.org": {
2036 "ed25519:EXPDYDPWZH": "kzrtfQMbJXWXQ1uzhybtwFnGk0JJBS4Mg8VPMusMu6U8MPJccwoHVZKo5+owuHTzIodI+GZYqLmMSzvfvsChAA"
2037 }
2038 },
2039 "user_id": "@dkasak_c:matrix.org",
2040 "unsigned": {}
2041 });
2042
2043 let device_keys: DeviceKeys = serde_json::from_value(device_keys).unwrap();
2044 let device = DeviceData::try_from(&device_keys).unwrap();
2045 let fallback_key: SignedKey = serde_json::from_value(fallback_key).unwrap();
2046
2047 device
2048 .verify_one_time_key(&fallback_key)
2049 .expect("The fallback key should pass the signature verification");
2050
2051 Ok(())
2052 }
2053
2054 #[test]
2055 fn test_shared_history_flag_from_history_visibility() {
2056 assert!(
2057 shared_history_from_history_visibility(&HistoryVisibility::WorldReadable),
2058 "The world readable visibility should set the shared history flag to true"
2059 );
2060
2061 assert!(
2062 shared_history_from_history_visibility(&HistoryVisibility::Shared),
2063 "The shared visibility should set the shared history flag to true"
2064 );
2065
2066 assert!(
2067 !shared_history_from_history_visibility(&HistoryVisibility::Joined),
2068 "The joined visibility should set the shared history flag to false"
2069 );
2070
2071 assert!(
2072 !shared_history_from_history_visibility(&HistoryVisibility::Invited),
2073 "The invited visibility should set the shared history flag to false"
2074 );
2075
2076 let visibility = HistoryVisibility::from("custom_visibility");
2077 assert!(
2078 !shared_history_from_history_visibility(&visibility),
2079 "A custom visibility should set the shared history flag to false"
2080 );
2081 }
2082
2083 #[async_test]
2084 async fn test_shared_history_set_when_creating_group_sessions() {
2085 let account = Account::new(user_id());
2086 let room_id = room_id!("!room:id");
2087 let settings = EncryptionSettings {
2088 history_visibility: HistoryVisibility::Shared,
2089 ..Default::default()
2090 };
2091
2092 let (_, session) = account
2093 .create_group_session_pair(room_id, settings, Default::default())
2094 .await
2095 .expect("We should be able to create a group session pair");
2096
2097 assert!(
2098 session.shared_history(),
2099 "The shared history flag should have been set when we created the new session"
2100 );
2101 }
2102}