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};
28use ruma::{
29 CanonicalJsonValue, DeviceId, DeviceKeyAlgorithm, DeviceKeyId, MilliSecondsSinceUnixEpoch,
30 OneTimeKeyAlgorithm, OneTimeKeyId, OwnedDeviceId, OwnedDeviceKeyId, OwnedOneTimeKeyId,
31 OwnedUserId, RoomId, SecondsSinceUnixEpoch, UInt, UserId,
32 api::client::{
33 dehydrated_device::{DehydratedDeviceData, DehydratedDeviceV2},
34 keys::{upload_keys, upload_signatures::v3::Request as SignatureUploadRequest},
35 },
36 canonical_json::to_canonical_value,
37 events::{AnyToDeviceEvent, room::history_visibility::HistoryVisibility},
38 serde::Raw,
39};
40use serde::{Deserialize, Serialize, de::Error};
41use serde_json::value::{RawValue as RawJsonValue, to_raw_value};
42use sha2::{Digest, Sha256};
43use tokio::sync::Mutex;
44use tracing::{Span, debug, field::debug, info, instrument, trace, warn};
45use vodozemac::{
46 Curve25519PublicKey, Ed25519Signature, KeyId, PickleError, base64_encode,
47 olm::{
48 Account as InnerAccount, AccountPickle, IdentityKeys, OlmMessage,
49 OneTimeKeyGenerationResult, PreKeyMessage, SessionConfig,
50 },
51};
52
53use super::{
54 EncryptionSettings, InboundGroupSession, OutboundGroupSession, PrivateCrossSigningIdentity,
55 Session, SessionCreationError as MegolmSessionCreationError, utility::SignJson,
56};
57#[cfg(feature = "experimental-algorithms")]
58use crate::types::events::room::encrypted::OlmV2Curve25519AesSha2Content;
59use crate::{
60 DecryptionSettings, Device, OlmError, SignatureError, TrustRequirement,
61 dehydrated_devices::DehydrationError,
62 error::{EventError, OlmResult, SessionCreationError},
63 identities::DeviceData,
64 olm::SenderData,
65 store::{
66 Store,
67 types::{Changes, DeviceChanges},
68 },
69 types::{
70 CrossSigningKey, DeviceKeys, EventEncryptionAlgorithm, OneTimeKey, SignedKey,
71 events::{
72 olm_v1::AnyDecryptedOlmEvent,
73 room::encrypted::{
74 EncryptedToDeviceEvent, OlmV1Curve25519AesSha2Content,
75 ToDeviceEncryptedEventContent,
76 },
77 },
78 requests::UploadSigningKeysRequest,
79 },
80};
81
82#[derive(Debug)]
83enum PrekeyBundle {
84 Olm3DH { key: SignedKey },
85}
86
87#[derive(Debug, Clone)]
88pub(crate) enum SessionType {
89 New(Session),
90 Existing(Session),
91}
92
93#[derive(Debug)]
94pub struct InboundCreationResult {
95 pub session: Session,
96 pub plaintext: String,
97}
98
99impl SessionType {
100 #[cfg(test)]
101 pub fn session(self) -> Session {
102 match self {
103 SessionType::New(s) => s,
104 SessionType::Existing(s) => s,
105 }
106 }
107}
108
109#[derive(Debug)]
115pub(crate) struct OlmDecryptionInfo {
116 pub session: SessionType,
117 pub message_hash: OlmMessageHash,
118 pub inbound_group_session: Option<InboundGroupSession>,
119 pub result: DecryptionResult,
120}
121
122#[derive(Debug)]
123pub(crate) struct DecryptionResult {
124 pub event: Box<AnyDecryptedOlmEvent>,
126 pub raw_event: Raw<AnyToDeviceEvent>,
127 pub sender_key: Curve25519PublicKey,
128 pub encryption_info: EncryptionInfo,
129}
130
131#[derive(Debug, Clone, Serialize, Deserialize)]
135pub struct OlmMessageHash {
136 pub sender_key: String,
138 pub hash: String,
140}
141
142impl OlmMessageHash {
143 fn new(sender_key: Curve25519PublicKey, ciphertext: &OlmMessage) -> Self {
144 let (message_type, ciphertext) = ciphertext.clone().to_parts();
145 let sender_key = sender_key.to_base64();
146
147 let sha = Sha256::new()
148 .chain_update(sender_key.as_bytes())
149 .chain_update([message_type as u8])
150 .chain_update(ciphertext)
151 .finalize();
152
153 Self { sender_key, hash: base64_encode(sha.as_slice()) }
154 }
155}
156
157#[derive(Clone)]
162#[cfg_attr(not(tarpaulin_include), derive(Debug))]
163pub struct StaticAccountData {
164 pub user_id: OwnedUserId,
166 pub device_id: OwnedDeviceId,
168 pub identity_keys: Arc<IdentityKeys>,
170 pub dehydrated: bool,
172 creation_local_time: MilliSecondsSinceUnixEpoch,
174}
175
176impl StaticAccountData {
177 const ALGORITHMS: &'static [&'static EventEncryptionAlgorithm] = &[
178 &EventEncryptionAlgorithm::OlmV1Curve25519AesSha2,
179 #[cfg(feature = "experimental-algorithms")]
180 &EventEncryptionAlgorithm::OlmV2Curve25519AesSha2,
181 &EventEncryptionAlgorithm::MegolmV1AesSha2,
182 #[cfg(feature = "experimental-algorithms")]
183 &EventEncryptionAlgorithm::MegolmV2AesSha2,
184 ];
185
186 pub async fn create_group_session_pair(
201 &self,
202 room_id: &RoomId,
203 settings: EncryptionSettings,
204 own_sender_data: SenderData,
205 ) -> Result<(OutboundGroupSession, InboundGroupSession), MegolmSessionCreationError> {
206 trace!(?room_id, algorithm = settings.algorithm.as_str(), "Creating a new room key");
207
208 let visibility = settings.history_visibility.clone();
209 let algorithm = settings.algorithm.to_owned();
210
211 let outbound = OutboundGroupSession::new(
212 self.device_id.clone(),
213 self.identity_keys.clone(),
214 room_id,
215 settings,
216 )?;
217
218 let identity_keys = &self.identity_keys;
219
220 let sender_key = identity_keys.curve25519;
221 let signing_key = identity_keys.ed25519;
222 let shared_history = shared_history_from_history_visibility(&visibility);
223
224 let inbound = InboundGroupSession::new(
225 sender_key,
226 signing_key,
227 room_id,
228 &outbound.session_key().await,
229 own_sender_data,
230 None,
231 algorithm,
232 Some(visibility),
233 shared_history,
234 )?;
235
236 Ok((outbound, inbound))
237 }
238
239 #[cfg(any(test, feature = "testing"))]
240 #[allow(dead_code)]
241 pub async fn create_group_session_pair_with_defaults(
244 &self,
245 room_id: &RoomId,
246 ) -> (OutboundGroupSession, InboundGroupSession) {
247 self.create_group_session_pair(
248 room_id,
249 EncryptionSettings::default(),
250 SenderData::unknown(),
251 )
252 .await
253 .expect("Can't create default group session pair")
254 }
255
256 pub fn signing_key_id(&self) -> OwnedDeviceKeyId {
258 DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, self.device_id())
259 }
260
261 pub fn has_signed_raw(
271 &self,
272 signatures: &crate::types::Signatures,
273 canonical_json: &str,
274 ) -> Result<(), SignatureError> {
275 use crate::olm::utility::VerifyJson;
276
277 let signing_key = self.identity_keys.ed25519;
278
279 signing_key.verify_canonicalized_json(
280 &self.user_id,
281 &DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, self.device_id()),
282 signatures,
283 canonical_json,
284 )
285 }
286
287 pub fn unsigned_device_keys(&self) -> DeviceKeys {
289 let identity_keys = self.identity_keys();
290 let keys = BTreeMap::from([
291 (
292 DeviceKeyId::from_parts(DeviceKeyAlgorithm::Curve25519, &self.device_id),
293 identity_keys.curve25519.into(),
294 ),
295 (
296 DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, &self.device_id),
297 identity_keys.ed25519.into(),
298 ),
299 ]);
300
301 let mut ret = DeviceKeys::new(
302 self.user_id.clone(),
303 self.device_id.clone(),
304 Self::ALGORITHMS.iter().map(|a| (**a).clone()).collect(),
305 keys,
306 Default::default(),
307 );
308 if self.dehydrated {
309 ret.dehydrated = JsOption::Some(true);
310 }
311 ret
312 }
313
314 pub fn user_id(&self) -> &UserId {
316 &self.user_id
317 }
318
319 pub fn device_id(&self) -> &DeviceId {
321 &self.device_id
322 }
323
324 pub fn identity_keys(&self) -> IdentityKeys {
326 *self.identity_keys
327 }
328
329 pub fn creation_local_time(&self) -> MilliSecondsSinceUnixEpoch {
331 self.creation_local_time
332 }
333}
334
335pub struct Account {
340 pub(crate) static_data: StaticAccountData,
341 inner: Box<InnerAccount>,
343 shared: bool,
346 uploaded_signed_key_count: u64,
351 fallback_creation_timestamp: Option<MilliSecondsSinceUnixEpoch>,
359}
360
361impl Deref for Account {
362 type Target = StaticAccountData;
363
364 fn deref(&self) -> &Self::Target {
365 &self.static_data
366 }
367}
368
369#[derive(Serialize, Deserialize)]
374#[allow(missing_debug_implementations)]
375pub struct PickledAccount {
376 pub user_id: OwnedUserId,
378 pub device_id: OwnedDeviceId,
380 pub pickle: AccountPickle,
382 pub shared: bool,
384 #[serde(default)]
386 pub dehydrated: bool,
387 pub uploaded_signed_key_count: u64,
389 #[serde(default = "default_account_creation_time")]
392 pub creation_local_time: MilliSecondsSinceUnixEpoch,
393 #[serde(default)]
395 pub fallback_key_creation_timestamp: Option<MilliSecondsSinceUnixEpoch>,
396}
397
398fn default_account_creation_time() -> MilliSecondsSinceUnixEpoch {
399 MilliSecondsSinceUnixEpoch(UInt::default())
400}
401
402#[cfg(not(tarpaulin_include))]
403impl fmt::Debug for Account {
404 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
405 f.debug_struct("Account")
406 .field("identity_keys", &self.identity_keys())
407 .field("shared", &self.shared())
408 .finish()
409 }
410}
411
412pub type OneTimeKeys = BTreeMap<OwnedOneTimeKeyId, Raw<ruma::encryption::OneTimeKey>>;
413pub type FallbackKeys = OneTimeKeys;
414
415impl Account {
416 pub(crate) fn new_helper(
417 mut account: InnerAccount,
418 user_id: &UserId,
419 device_id: &DeviceId,
420 ) -> Self {
421 let identity_keys = account.identity_keys();
422
423 account.generate_one_time_keys(account.max_number_of_one_time_keys());
435
436 Self {
437 static_data: StaticAccountData {
438 user_id: user_id.into(),
439 device_id: device_id.into(),
440 identity_keys: Arc::new(identity_keys),
441 dehydrated: false,
442 creation_local_time: MilliSecondsSinceUnixEpoch::now(),
443 },
444 inner: Box::new(account),
445 shared: false,
446 uploaded_signed_key_count: 0,
447 fallback_creation_timestamp: None,
448 }
449 }
450
451 pub fn with_device_id(user_id: &UserId, device_id: &DeviceId) -> Self {
453 let account = InnerAccount::new();
454
455 Self::new_helper(account, user_id, device_id)
456 }
457
458 pub fn new(user_id: &UserId) -> Self {
461 let account = InnerAccount::new();
462 let device_id: OwnedDeviceId =
463 base64_encode(account.identity_keys().curve25519.as_bytes()).into();
464
465 Self::new_helper(account, user_id, &device_id)
466 }
467
468 pub fn new_dehydrated(user_id: &UserId) -> Self {
470 let account = InnerAccount::new();
471 let device_id: OwnedDeviceId =
472 base64_encode(account.identity_keys().curve25519.as_bytes()).into();
473
474 let mut ret = Self::new_helper(account, user_id, &device_id);
475 ret.static_data.dehydrated = true;
476 ret
477 }
478
479 pub fn static_data(&self) -> &StaticAccountData {
481 &self.static_data
482 }
483
484 pub fn update_uploaded_key_count(&mut self, new_count: u64) {
490 self.uploaded_signed_key_count = new_count;
491 }
492
493 pub fn uploaded_key_count(&self) -> u64 {
495 self.uploaded_signed_key_count
496 }
497
498 pub fn shared(&self) -> bool {
500 self.shared
501 }
502
503 pub fn mark_as_shared(&mut self) {
508 self.shared = true;
509 }
510
511 pub fn one_time_keys(&self) -> HashMap<KeyId, Curve25519PublicKey> {
515 self.inner.one_time_keys()
516 }
517
518 pub fn generate_one_time_keys(&mut self, count: usize) -> OneTimeKeyGenerationResult {
520 self.inner.generate_one_time_keys(count)
521 }
522
523 pub fn max_one_time_keys(&self) -> usize {
525 self.inner.max_number_of_one_time_keys()
526 }
527
528 pub(crate) fn update_key_counts(
529 &mut self,
530 one_time_key_counts: &BTreeMap<OneTimeKeyAlgorithm, UInt>,
531 unused_fallback_keys: Option<&[OneTimeKeyAlgorithm]>,
532 ) {
533 if let Some(count) = one_time_key_counts.get(&OneTimeKeyAlgorithm::SignedCurve25519) {
534 let count: u64 = (*count).into();
535 let old_count = self.uploaded_key_count();
536
537 if count != old_count {
541 debug!(
542 "Updated uploaded one-time key count {} -> {count}.",
543 self.uploaded_key_count(),
544 );
545 }
546
547 self.update_uploaded_key_count(count);
548 self.generate_one_time_keys_if_needed();
549 }
550
551 if unused_fallback_keys.is_some() || self.fallback_creation_timestamp.is_some() {
555 self.generate_fallback_key_if_needed();
556 }
557 }
558
559 #[instrument(skip_all)]
568 pub fn generate_one_time_keys_if_needed(&mut self) -> Option<u64> {
569 if !self.one_time_keys().is_empty() {
573 return Some(0);
574 }
575
576 let count = self.uploaded_key_count();
577 let max_keys = self.max_one_time_keys();
578
579 if count >= max_keys as u64 {
580 return None;
581 }
582
583 let key_count = (max_keys as u64) - count;
584 let key_count: usize = key_count.try_into().unwrap_or(max_keys);
585
586 let result = self.generate_one_time_keys(key_count);
587
588 debug!(
589 count = key_count,
590 discarded_keys = ?result.removed,
591 created_keys = ?result.created,
592 "Generated new one-time keys"
593 );
594
595 Some(key_count as u64)
596 }
597
598 pub(crate) fn generate_fallback_key_if_needed(&mut self) {
605 if self.inner.fallback_key().is_empty() && self.fallback_key_expired() {
606 let removed_fallback_key = self.inner.generate_fallback_key();
607 self.fallback_creation_timestamp = Some(MilliSecondsSinceUnixEpoch::now());
608
609 debug!(
610 ?removed_fallback_key,
611 "The fallback key either expired or we didn't have one: generated a new fallback key.",
612 );
613 }
614 }
615
616 fn fallback_key_expired(&self) -> bool {
624 const FALLBACK_KEY_MAX_AGE: Duration = Duration::from_secs(3600 * 24 * 7);
625
626 if let Some(time) = self.fallback_creation_timestamp {
627 let Some(system_time) = time.to_system_time() else {
631 return true;
632 };
633
634 let Ok(elapsed) = system_time.elapsed() else {
638 return true;
639 };
640
641 elapsed > FALLBACK_KEY_MAX_AGE
646 } else {
647 true
650 }
651 }
652
653 fn fallback_key(&self) -> HashMap<KeyId, Curve25519PublicKey> {
654 self.inner.fallback_key()
655 }
656
657 pub fn keys_for_upload(&self) -> (Option<DeviceKeys>, OneTimeKeys, FallbackKeys) {
663 let device_keys = self.shared().not().then(|| self.device_keys());
664
665 let one_time_keys = self.signed_one_time_keys();
666 let fallback_keys = self.signed_fallback_keys();
667
668 (device_keys, one_time_keys, fallback_keys)
669 }
670
671 pub fn mark_keys_as_published(&mut self) {
673 self.inner.mark_keys_as_published();
674 }
675
676 pub fn sign(&self, string: &str) -> Ed25519Signature {
680 self.inner.sign(string)
681 }
682
683 pub fn pickle(&self) -> PickledAccount {
685 let pickle = self.inner.pickle();
686
687 PickledAccount {
688 user_id: self.user_id().to_owned(),
689 device_id: self.device_id().to_owned(),
690 pickle,
691 shared: self.shared(),
692 dehydrated: self.static_data.dehydrated,
693 uploaded_signed_key_count: self.uploaded_key_count(),
694 creation_local_time: self.static_data.creation_local_time,
695 fallback_key_creation_timestamp: self.fallback_creation_timestamp,
696 }
697 }
698
699 pub(crate) fn dehydrate(&self, pickle_key: &[u8; 32]) -> Raw<DehydratedDeviceData> {
700 let dehydration_result = self
701 .inner
702 .to_dehydrated_device(pickle_key)
703 .expect("We should be able to convert a freshly created Account into a libolm pickle");
704
705 let data = DehydratedDeviceData::V2(DehydratedDeviceV2::new(
706 dehydration_result.ciphertext,
707 dehydration_result.nonce,
708 ));
709 Raw::from_json(to_raw_value(&data).expect("Couldn't serialize our dehydrated device data"))
710 }
711
712 pub(crate) fn rehydrate(
713 pickle_key: &[u8; 32],
714 user_id: &UserId,
715 device_id: &DeviceId,
716 device_data: Raw<DehydratedDeviceData>,
717 ) -> Result<Self, DehydrationError> {
718 let data = device_data.deserialize()?;
719
720 match data {
721 DehydratedDeviceData::V1(d) => {
722 let pickle_key = expand_legacy_pickle_key(pickle_key, device_id);
723 let account =
724 InnerAccount::from_libolm_pickle(&d.device_pickle, pickle_key.as_ref())?;
725 Ok(Self::new_helper(account, user_id, device_id))
726 }
727 DehydratedDeviceData::V2(d) => {
728 let account =
729 InnerAccount::from_dehydrated_device(&d.device_pickle, &d.nonce, pickle_key)?;
730 Ok(Self::new_helper(account, user_id, device_id))
731 }
732 _ => Err(DehydrationError::Json(serde_json::Error::custom(format!(
733 "Unsupported dehydrated device algorithm {:?}",
734 data.algorithm()
735 )))),
736 }
737 }
738
739 #[cfg(test)]
742 pub(crate) fn legacy_dehydrate(&self, pickle_key: &[u8; 32]) -> Raw<DehydratedDeviceData> {
743 use ruma::api::client::dehydrated_device::DehydratedDeviceV1;
744
745 let pickle_key = expand_legacy_pickle_key(pickle_key, &self.device_id);
746 let device_pickle = self
747 .inner
748 .to_libolm_pickle(pickle_key.as_ref())
749 .expect("We should be able to convert a freshly created Account into a libolm pickle");
750
751 let data = DehydratedDeviceData::V1(DehydratedDeviceV1::new(device_pickle));
752 Raw::from_json(to_raw_value(&data).expect("Couldn't serialize our dehydrated device data"))
753 }
754
755 pub fn from_pickle(pickle: PickledAccount) -> Result<Self, PickleError> {
764 let account: vodozemac::olm::Account = pickle.pickle.into();
765 let identity_keys = account.identity_keys();
766
767 Ok(Self {
768 static_data: StaticAccountData {
769 user_id: pickle.user_id.clone(),
770 device_id: pickle.device_id.clone(),
771 identity_keys: Arc::new(identity_keys),
772 dehydrated: pickle.dehydrated,
773 creation_local_time: pickle.creation_local_time,
774 },
775 inner: Box::new(account),
776 shared: pickle.shared,
777 uploaded_signed_key_count: pickle.uploaded_signed_key_count,
778 fallback_creation_timestamp: pickle.fallback_key_creation_timestamp,
779 })
780 }
781
782 pub fn device_keys(&self) -> DeviceKeys {
785 let mut device_keys = self.unsigned_device_keys();
786
787 let json_device_keys =
790 to_canonical_value(&device_keys).expect("device key is always safe to serialize");
791 let signature = self
792 .sign_json(json_device_keys)
793 .expect("Newly created device keys can always be signed");
794
795 device_keys.signatures.add_signature(
796 self.user_id().to_owned(),
797 DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, &self.static_data.device_id),
798 signature,
799 );
800
801 device_keys
802 }
803
804 pub async fn bootstrap_cross_signing(
816 &self,
817 ) -> Result<
818 (PrivateCrossSigningIdentity, UploadSigningKeysRequest, SignatureUploadRequest),
819 SignatureError,
820 > {
821 let identity = PrivateCrossSigningIdentity::for_account(self)?;
822
823 let signature_request = identity.sign_account(self.static_data()).await?;
824
825 let upload_request = identity.as_upload_request().await;
826
827 Ok((identity, upload_request, signature_request))
828 }
829
830 pub fn sign_cross_signing_key(
832 &self,
833 cross_signing_key: &mut CrossSigningKey,
834 ) -> Result<(), SignatureError> {
835 let signature = self.sign_json(to_canonical_value(&cross_signing_key)?)?;
836
837 cross_signing_key.signatures.add_signature(
838 self.user_id().to_owned(),
839 DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, self.device_id()),
840 signature,
841 );
842
843 Ok(())
844 }
845
846 pub fn sign_json(&self, json: CanonicalJsonValue) -> Result<Ed25519Signature, SignatureError> {
854 self.inner.sign_json(json)
855 }
856
857 pub fn signed_one_time_keys(&self) -> OneTimeKeys {
861 let one_time_keys = self.one_time_keys();
862
863 if one_time_keys.is_empty() {
864 BTreeMap::new()
865 } else {
866 self.signed_keys(one_time_keys, false)
867 }
868 }
869
870 pub fn signed_fallback_keys(&self) -> FallbackKeys {
874 let fallback_key = self.fallback_key();
875
876 if fallback_key.is_empty() { BTreeMap::new() } else { self.signed_keys(fallback_key, true) }
877 }
878
879 fn signed_keys(
880 &self,
881 keys: HashMap<KeyId, Curve25519PublicKey>,
882 fallback: bool,
883 ) -> OneTimeKeys {
884 let mut keys_map = BTreeMap::new();
885
886 for (key_id, key) in keys {
887 let signed_key = self.sign_key(key, fallback);
888
889 keys_map.insert(
890 OneTimeKeyId::from_parts(
891 OneTimeKeyAlgorithm::SignedCurve25519,
892 key_id.to_base64().as_str().into(),
893 ),
894 signed_key.into_raw(),
895 );
896 }
897
898 keys_map
899 }
900
901 fn sign_key(&self, key: Curve25519PublicKey, fallback: bool) -> SignedKey {
902 let mut key = if fallback {
903 SignedKey::new_fallback(key.to_owned())
904 } else {
905 SignedKey::new(key.to_owned())
906 };
907
908 let signature = self
909 .sign_json(to_canonical_value(&key).expect("Can't serialize a signed key"))
910 .expect("Newly created one-time keys can always be signed");
911
912 key.signatures_mut().add_signature(
913 self.user_id().to_owned(),
914 DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, self.device_id()),
915 signature,
916 );
917
918 key
919 }
920
921 pub fn create_outbound_session_helper(
941 &self,
942 config: SessionConfig,
943 identity_key: Curve25519PublicKey,
944 one_time_key: Curve25519PublicKey,
945 fallback_used: bool,
946 our_device_keys: DeviceKeys,
947 ) -> Result<Session, vodozemac::olm::SessionCreationError> {
948 let session = self.inner.create_outbound_session(config, identity_key, one_time_key)?;
949
950 let now = SecondsSinceUnixEpoch::now();
951 let session_id = session.session_id();
952
953 Ok(Session {
954 inner: Arc::new(Mutex::new(session)),
955 session_id: session_id.into(),
956 sender_key: identity_key,
957 our_device_keys,
958 created_using_fallback_key: fallback_used,
959 creation_time: now,
960 last_use_time: now,
961 })
962 }
963
964 #[instrument(
965 skip_all,
966 fields(
967 user_id = ?device.user_id(),
968 device_id = ?device.device_id(),
969 algorithms = ?device.algorithms()
970 )
971 )]
972 fn find_pre_key_bundle(
973 device: &DeviceData,
974 key_map: &OneTimeKeys,
975 ) -> Result<PrekeyBundle, SessionCreationError> {
976 let mut keys = key_map.iter();
977
978 let first_key = keys.next().ok_or_else(|| {
979 SessionCreationError::OneTimeKeyMissing(
980 device.user_id().to_owned(),
981 device.device_id().into(),
982 )
983 })?;
984
985 let first_key_id = first_key.0.to_owned();
986 let first_key = OneTimeKey::deserialize(first_key_id.algorithm(), first_key.1)?;
987
988 let result = match first_key {
989 OneTimeKey::SignedKey(key) => Ok(PrekeyBundle::Olm3DH { key }),
990 };
991
992 trace!(?result, "Finished searching for a valid pre-key bundle");
993
994 result
995 }
996
997 #[allow(clippy::result_large_err)]
1012 pub fn create_outbound_session(
1013 &self,
1014 device: &DeviceData,
1015 key_map: &OneTimeKeys,
1016 our_device_keys: DeviceKeys,
1017 ) -> Result<Session, SessionCreationError> {
1018 let pre_key_bundle = Self::find_pre_key_bundle(device, key_map)?;
1019
1020 match pre_key_bundle {
1021 PrekeyBundle::Olm3DH { key } => {
1022 device.verify_one_time_key(&key).map_err(|error| {
1023 SessionCreationError::InvalidSignature {
1024 signing_key: device.ed25519_key().map(Box::new),
1025 one_time_key: key.clone().into(),
1026 error: error.into(),
1027 }
1028 })?;
1029
1030 let identity_key = device.curve25519_key().ok_or_else(|| {
1031 SessionCreationError::DeviceMissingCurveKey(
1032 device.user_id().to_owned(),
1033 device.device_id().into(),
1034 )
1035 })?;
1036
1037 let is_fallback = key.fallback();
1038 let one_time_key = key.key();
1039 let config = device.olm_session_config();
1040
1041 Ok(self.create_outbound_session_helper(
1042 config,
1043 identity_key,
1044 one_time_key,
1045 is_fallback,
1046 our_device_keys,
1047 )?)
1048 }
1049 }
1050 }
1051
1052 pub fn create_inbound_session(
1067 &mut self,
1068 their_identity_key: Curve25519PublicKey,
1069 our_device_keys: DeviceKeys,
1070 message: &PreKeyMessage,
1071 ) -> Result<InboundCreationResult, SessionCreationError> {
1072 Span::current().record("session_id", debug(message.session_id()));
1073 trace!("Creating a new Olm session from a pre-key message");
1074
1075 #[cfg(not(feature = "experimental-algorithms"))]
1076 let config = SessionConfig::version_1();
1077
1078 #[cfg(feature = "experimental-algorithms")]
1079 let config = SessionConfig::version_2();
1080
1081 let result = self.inner.create_inbound_session(config, their_identity_key, message)?;
1082 let now = SecondsSinceUnixEpoch::now();
1083 let session_id = result.session.session_id();
1084
1085 debug!(session=?result.session, "Decrypted an Olm message from a new Olm session");
1086
1087 let session = Session {
1088 inner: Arc::new(Mutex::new(result.session)),
1089 session_id: session_id.into(),
1090 sender_key: their_identity_key,
1091 our_device_keys,
1092 created_using_fallback_key: false,
1093 creation_time: now,
1094 last_use_time: now,
1095 };
1096
1097 let plaintext = String::from_utf8_lossy(&result.plaintext).to_string();
1098
1099 Ok(InboundCreationResult { session, plaintext })
1100 }
1101
1102 #[cfg(any(test, feature = "testing"))]
1103 #[allow(dead_code)]
1104 pub async fn create_session_for_test_helper(
1106 &mut self,
1107 other: &mut Account,
1108 ) -> (Session, Session) {
1109 use ruma::events::dummy::ToDeviceDummyEventContent;
1110
1111 other.generate_one_time_keys(1);
1112 let one_time_map = other.signed_one_time_keys();
1113 let device = DeviceData::from_account(other);
1114
1115 let mut our_session =
1116 self.create_outbound_session(&device, &one_time_map, self.device_keys()).unwrap();
1117
1118 other.mark_keys_as_published();
1119
1120 let message = our_session
1121 .encrypt(&device, "m.dummy", ToDeviceDummyEventContent::new(), None)
1122 .await
1123 .unwrap()
1124 .deserialize()
1125 .unwrap();
1126
1127 #[cfg(feature = "experimental-algorithms")]
1128 let content = if let ToDeviceEncryptedEventContent::OlmV2Curve25519AesSha2(c) = message {
1129 c
1130 } else {
1131 panic!("Invalid encrypted event algorithm {}", message.algorithm());
1132 };
1133
1134 #[cfg(not(feature = "experimental-algorithms"))]
1135 let ToDeviceEncryptedEventContent::OlmV1Curve25519AesSha2(content) = message else {
1136 panic!("Invalid encrypted event algorithm {}", message.algorithm());
1137 };
1138
1139 let OlmMessage::PreKey(prekey) = content.ciphertext else {
1140 panic!("Wrong Olm message type");
1141 };
1142
1143 let our_device = DeviceData::from_account(self);
1144 let other_session = other
1145 .create_inbound_session(
1146 our_device.curve25519_key().unwrap(),
1147 other.device_keys(),
1148 &prekey,
1149 )
1150 .unwrap();
1151
1152 (our_session, other_session.session)
1153 }
1154
1155 async fn decrypt_olm_helper(
1156 &mut self,
1157 store: &Store,
1158 sender: &UserId,
1159 sender_key: Curve25519PublicKey,
1160 ciphertext: &OlmMessage,
1161 decryption_settings: &DecryptionSettings,
1162 ) -> OlmResult<OlmDecryptionInfo> {
1163 let message_hash = OlmMessageHash::new(sender_key, ciphertext);
1164
1165 match self
1166 .decrypt_and_parse_olm_message(
1167 store,
1168 sender,
1169 sender_key,
1170 ciphertext,
1171 decryption_settings,
1172 )
1173 .await
1174 {
1175 Ok((session, result)) => {
1176 Ok(OlmDecryptionInfo { session, message_hash, result, inbound_group_session: None })
1177 }
1178 Err(OlmError::SessionWedged(user_id, sender_key)) => {
1179 if store.is_message_known(&message_hash).await? {
1180 info!(?sender_key, "An Olm message got replayed, decryption failed");
1181 Err(OlmError::ReplayedMessage(user_id, sender_key))
1182 } else {
1183 Err(OlmError::SessionWedged(user_id, sender_key))
1184 }
1185 }
1186 Err(e) => Err(e),
1187 }
1188 }
1189
1190 #[cfg(feature = "experimental-algorithms")]
1191 async fn decrypt_olm_v2(
1192 &mut self,
1193 store: &Store,
1194 sender: &UserId,
1195 content: &OlmV2Curve25519AesSha2Content,
1196 decryption_settings: &DecryptionSettings,
1197 ) -> OlmResult<OlmDecryptionInfo> {
1198 self.decrypt_olm_helper(
1199 store,
1200 sender,
1201 content.sender_key,
1202 &content.ciphertext,
1203 decryption_settings,
1204 )
1205 .await
1206 }
1207
1208 #[instrument(skip_all, fields(sender, sender_key = ?content.sender_key))]
1209 async fn decrypt_olm_v1(
1210 &mut self,
1211 store: &Store,
1212 sender: &UserId,
1213 content: &OlmV1Curve25519AesSha2Content,
1214 decryption_settings: &DecryptionSettings,
1215 ) -> OlmResult<OlmDecryptionInfo> {
1216 if content.recipient_key != self.static_data.identity_keys.curve25519 {
1217 warn!("Olm event doesn't contain a ciphertext for our key");
1218
1219 Err(EventError::MissingCiphertext.into())
1220 } else {
1221 Box::pin(self.decrypt_olm_helper(
1222 store,
1223 sender,
1224 content.sender_key,
1225 &content.ciphertext,
1226 decryption_settings,
1227 ))
1228 .await
1229 }
1230 }
1231
1232 #[instrument(skip_all, fields(algorithm = ?event.content.algorithm()))]
1233 pub(crate) async fn decrypt_to_device_event(
1234 &mut self,
1235 store: &Store,
1236 event: &EncryptedToDeviceEvent,
1237 decryption_settings: &DecryptionSettings,
1238 ) -> OlmResult<OlmDecryptionInfo> {
1239 trace!("Decrypting a to-device event");
1240
1241 match &event.content {
1242 ToDeviceEncryptedEventContent::OlmV1Curve25519AesSha2(c) => {
1243 self.decrypt_olm_v1(store, &event.sender, c, decryption_settings).await
1244 }
1245 #[cfg(feature = "experimental-algorithms")]
1246 ToDeviceEncryptedEventContent::OlmV2Curve25519AesSha2(c) => {
1247 self.decrypt_olm_v2(store, &event.sender, c, decryption_settings).await
1248 }
1249 ToDeviceEncryptedEventContent::Unknown(_) => {
1250 warn!(
1251 "Error decrypting an to-device event, unsupported \
1252 encryption algorithm"
1253 );
1254
1255 Err(EventError::UnsupportedAlgorithm.into())
1256 }
1257 }
1258 }
1259
1260 pub fn receive_keys_upload_response(
1262 &mut self,
1263 response: &upload_keys::v3::Response,
1264 ) -> OlmResult<()> {
1265 if !self.shared() {
1266 debug!("Marking account as shared");
1267 }
1268 self.mark_as_shared();
1269
1270 debug!("Marking one-time keys as published");
1271 self.mark_keys_as_published();
1274 self.update_key_counts(&response.one_time_key_counts, None);
1275
1276 Ok(())
1277 }
1278
1279 async fn decrypt_olm_message(
1281 &mut self,
1282 store: &Store,
1283 sender: &UserId,
1284 sender_key: Curve25519PublicKey,
1285 message: &OlmMessage,
1286 ) -> Result<(SessionType, String), OlmError> {
1287 let existing_sessions = store.get_sessions(&sender_key.to_base64()).await?;
1288
1289 match message {
1290 OlmMessage::Normal(_) => {
1291 let mut errors_by_olm_session = Vec::new();
1292
1293 if let Some(sessions) = existing_sessions {
1294 for session in sessions.lock().await.iter_mut() {
1297 match session.decrypt(message).await {
1298 Ok(p) => {
1299 return Ok((SessionType::Existing(session.clone()), p));
1301 }
1302
1303 Err(e) => {
1304 errors_by_olm_session.push((session.session_id().to_owned(), e));
1309 }
1310 }
1311 }
1312 }
1313
1314 warn!(
1315 ?errors_by_olm_session,
1316 "Failed to decrypt a non-pre-key message with all available sessions"
1317 );
1318 Err(OlmError::SessionWedged(sender.to_owned(), sender_key))
1319 }
1320
1321 OlmMessage::PreKey(prekey_message) => {
1322 if let Some(sessions) = existing_sessions {
1324 for session in sessions.lock().await.iter_mut() {
1325 if prekey_message.session_id() != session.session_id() {
1326 continue;
1328 }
1329
1330 if let Ok(p) = session.decrypt(message).await {
1331 return Ok((SessionType::Existing(session.clone()), p));
1333 }
1334
1335 warn!(
1349 session_id = session.session_id(),
1350 "Failed to decrypt a pre-key message with the corresponding session"
1351 );
1352
1353 return Err(OlmError::SessionWedged(
1354 session.our_device_keys.user_id.to_owned(),
1355 session.sender_key(),
1356 ));
1357 }
1358 }
1359
1360 let device_keys = store.get_own_device().await?.as_device_keys().clone();
1361 let result =
1362 match self.create_inbound_session(sender_key, device_keys, prekey_message) {
1363 Ok(r) => r,
1364 Err(e) => {
1365 warn!(
1366 "Failed to create a new Olm session from a pre-key message: {e:?}"
1367 );
1368 return Err(OlmError::SessionWedged(sender.to_owned(), sender_key));
1369 }
1370 };
1371
1372 let mut changes =
1377 Changes { sessions: vec![result.session.clone()], ..Default::default() };
1378
1379 if let Some(device) = store.get_device_from_curve_key(sender, sender_key).await? {
1386 let mut device_data = device.inner;
1387 device_data.olm_wedging_index.increment();
1388
1389 changes.devices =
1390 DeviceChanges { changed: vec![device_data], ..Default::default() };
1391 }
1392
1393 store.save_changes(changes).await?;
1394
1395 Ok((SessionType::New(result.session), result.plaintext))
1396 }
1397 }
1398 }
1399
1400 #[instrument(skip(self, store), fields(session, session_id))]
1403 async fn decrypt_and_parse_olm_message(
1404 &mut self,
1405 store: &Store,
1406 sender: &UserId,
1407 sender_key: Curve25519PublicKey,
1408 message: &OlmMessage,
1409 decryption_settings: &DecryptionSettings,
1410 ) -> OlmResult<(SessionType, DecryptionResult)> {
1411 let (session, plaintext) =
1412 self.decrypt_olm_message(store, sender, sender_key, message).await?;
1413
1414 trace!("Successfully decrypted an Olm message");
1415
1416 match self
1417 .parse_decrypted_to_device_event(
1418 store,
1419 sender,
1420 sender_key,
1421 plaintext,
1422 decryption_settings,
1423 )
1424 .await
1425 {
1426 Ok(result) => Ok((session, result)),
1427 Err(e) => {
1428 match session {
1432 SessionType::New(s) | SessionType::Existing(s) => {
1433 store.save_sessions(&[s]).await?;
1434 }
1435 }
1436
1437 warn!(
1438 error = ?e,
1439 "A to-device message was successfully decrypted but \
1440 parsing and checking the event fields failed"
1441 );
1442
1443 Err(e)
1444 }
1445 }
1446 }
1447
1448 async fn parse_decrypted_to_device_event(
1469 &self,
1470 store: &Store,
1471 sender: &UserId,
1472 sender_key: Curve25519PublicKey,
1473 plaintext: String,
1474 decryption_settings: &DecryptionSettings,
1475 ) -> OlmResult<DecryptionResult> {
1476 let event: Box<AnyDecryptedOlmEvent> = serde_json::from_str(&plaintext)?;
1477 let identity_keys = &self.static_data.identity_keys;
1478
1479 if event.recipient() != self.static_data.user_id {
1480 Err(EventError::MismatchedSender(
1481 event.recipient().to_owned(),
1482 self.static_data.user_id.clone(),
1483 )
1484 .into())
1485 }
1486 else if event.sender() != sender {
1489 Err(EventError::MismatchedSender(event.sender().to_owned(), sender.to_owned()).into())
1490 } else if identity_keys.ed25519 != event.recipient_keys().ed25519 {
1491 Err(EventError::MismatchedKeys(
1492 identity_keys.ed25519.into(),
1493 event.recipient_keys().ed25519.into(),
1494 )
1495 .into())
1496 } else {
1497 let sender_device = Self::get_event_sender_device(store, sender_key, &event).await?;
1498 let encryption_info = Self::get_olm_encryption_info(sender_key, sender, &sender_device);
1499
1500 let result = DecryptionResult {
1501 event,
1502 raw_event: Raw::from_json(RawJsonValue::from_string(plaintext)?),
1503 sender_key,
1504 encryption_info,
1505 };
1506
1507 if !self.is_from_verified_device_or_allowed_type(decryption_settings, &result) {
1509 Err(OlmError::UnverifiedSenderDevice)
1510 } else {
1511 Ok(result)
1513 }
1514 }
1515 }
1516
1517 async fn get_event_sender_device(
1531 store: &Store,
1532 sender_key: Curve25519PublicKey,
1533 event: &AnyDecryptedOlmEvent,
1534 ) -> OlmResult<Option<Device>> {
1535 let sender_device_keys = Self::check_sender_device_keys(event, sender_key)?;
1540 if let AnyDecryptedOlmEvent::RoomKey(_) = event {
1541 return Ok(None);
1546 }
1547
1548 if let AnyDecryptedOlmEvent::RoomKeyBundle(_) = event {
1551 sender_device_keys.ok_or(EventError::MissingSigningKey).inspect_err(|_| {
1552 warn!("The room key bundle was missing the sender device keys in the event")
1553 })?;
1554 }
1555
1556 let store_device = store.get_device_from_curve_key(event.sender(), sender_key).await?;
1561
1562 match (store_device, sender_device_keys) {
1563 (Some(device), _) => {
1566 let key = device.ed25519_key().ok_or(EventError::MissingSigningKey)?;
1567 if key != event.keys().ed25519 {
1568 return Err(EventError::MismatchedKeys(
1569 key.into(),
1570 event.keys().ed25519.into(),
1571 )
1572 .into());
1573 }
1574 Ok(Some(device))
1575 }
1576
1577 (None, Some(sender_device_keys)) => {
1578 let sender_device_data = sender_device_keys.try_into().expect("Conversion of DeviceKeys to DeviceData failed despite the signature already having been checked");
1581 Ok(Some(store.wrap_device_data(sender_device_data).await?))
1582 }
1583
1584 (None, None) => Err(OlmError::EventError(EventError::MissingSigningKey)),
1585 }
1586 }
1587
1588 fn is_from_verified_device_or_allowed_type(
1601 &self,
1602 decryption_settings: &DecryptionSettings,
1603 result: &DecryptionResult,
1604 ) -> bool {
1605 let event_type = result.event.event_type();
1606
1607 match event_type {
1619 "m.room_key"
1620 | "m.room_key.withheld"
1621 | "m.room_key_request"
1622 | "m.secret.request"
1623 | "m.key.verification.key"
1624 | "m.key.verification.mac"
1625 | "m.key.verification.done"
1626 | "m.key.verification.ready"
1627 | "m.key.verification.start"
1628 | "m.key.verification.accept"
1629 | "m.key.verification.cancel"
1630 | "m.key.verification.request" => {
1631 true
1634 }
1635 _ => {
1636 satisfies_sender_trust_requirement(
1639 &result.encryption_info,
1640 &decryption_settings.sender_device_trust_requirement,
1641 )
1642 }
1643 }
1644 }
1645
1646 fn get_olm_encryption_info(
1653 sender_key: Curve25519PublicKey,
1654 sender_id: &UserId,
1655 sender_device: &Option<Device>,
1656 ) -> EncryptionInfo {
1657 let verification_state = sender_device
1658 .as_ref()
1659 .map(|device| {
1660 if device.is_verified() {
1661 VerificationState::Verified
1663 } else if device.is_cross_signed_by_owner() {
1664 if device
1666 .device_owner_identity
1667 .as_ref()
1668 .expect("A device cross-signed by the owner must have an owner identity")
1669 .was_previously_verified()
1670 {
1671 VerificationState::Unverified(VerificationLevel::VerificationViolation)
1672 } else {
1673 VerificationState::Unverified(VerificationLevel::UnverifiedIdentity)
1674 }
1675 } else {
1676 VerificationState::Unverified(VerificationLevel::UnsignedDevice)
1678 }
1679 })
1680 .unwrap_or(VerificationState::Unverified(VerificationLevel::None(
1681 DeviceLinkProblem::MissingDevice,
1682 )));
1683
1684 EncryptionInfo {
1685 sender: sender_id.to_owned(),
1686 sender_device: sender_device.as_ref().map(|d| d.device_id().to_owned()),
1687 forwarder: None,
1688 algorithm_info: AlgorithmInfo::OlmV1Curve25519AesSha2 {
1689 curve25519_public_key_base64: sender_key.to_base64(),
1690 },
1691 verification_state,
1692 }
1693 }
1694
1695 fn check_sender_device_keys(
1743 event: &AnyDecryptedOlmEvent,
1744 sender_key: Curve25519PublicKey,
1745 ) -> OlmResult<Option<&DeviceKeys>> {
1746 let Some(sender_device_keys) = event.sender_device_keys() else {
1747 return Ok(None);
1748 };
1749
1750 if sender_device_keys.user_id != event.sender() {
1751 warn!(
1752 "Received a to-device message with sender_device_keys with incorrect user_id: expected {:?}, got {:?}",
1753 event.sender(),
1754 sender_device_keys.user_id
1755 );
1756 return Err(OlmError::EventError(EventError::InvalidSenderDeviceKeys));
1757 }
1758
1759 sender_device_keys.check_self_signature().map_err(|err| {
1761 warn!(
1762 "Received a to-device message with sender_device_keys with \
1763 invalid signature: {err:?}",
1764 );
1765 OlmError::EventError(EventError::InvalidSenderDeviceKeys)
1766 })?;
1767
1768 if sender_device_keys.ed25519_key() != Some(event.keys().ed25519) {
1771 warn!(
1772 "Received a to-device message with sender_device_keys with incorrect \
1773 ed25519 key: expected {:?}, got {:?}",
1774 event.keys().ed25519,
1775 sender_device_keys.ed25519_key(),
1776 );
1777 return Err(OlmError::EventError(EventError::InvalidSenderDeviceKeys));
1778 }
1779
1780 if sender_device_keys.curve25519_key() != Some(sender_key) {
1783 warn!(
1784 "Received a to-device message with sender_device_keys with incorrect \
1785 curve25519 key: expected {sender_key:?}, got {:?}",
1786 sender_device_keys.curve25519_key(),
1787 );
1788 return Err(OlmError::EventError(EventError::InvalidSenderDeviceKeys));
1789 }
1790
1791 Ok(Some(sender_device_keys))
1792 }
1793
1794 #[doc(hidden)]
1799 pub fn deep_clone(&self) -> Self {
1800 Self::from_pickle(self.pickle()).unwrap()
1802 }
1803}
1804
1805impl PartialEq for Account {
1806 fn eq(&self, other: &Self) -> bool {
1807 self.identity_keys() == other.identity_keys() && self.shared() == other.shared()
1808 }
1809}
1810
1811pub(crate) fn shared_history_from_history_visibility(
1831 history_visibility: &HistoryVisibility,
1832) -> bool {
1833 match history_visibility {
1834 HistoryVisibility::Shared | HistoryVisibility::WorldReadable => true,
1835 HistoryVisibility::Invited | HistoryVisibility::Joined | _ => false,
1836 }
1837}
1838
1839fn expand_legacy_pickle_key(key: &[u8; 32], device_id: &DeviceId) -> Box<[u8; 32]> {
1858 let kdf: Hkdf<Sha256> = Hkdf::new(Some(device_id.as_bytes()), key);
1859 let mut key = Box::new([0u8; 32]);
1860
1861 kdf.expand(b"dehydrated-device-pickle-key", key.as_mut_slice())
1862 .expect("We should be able to expand the 32 byte pickle key");
1863
1864 key
1865}
1866
1867fn satisfies_sender_trust_requirement(
1870 encryption_info: &EncryptionInfo,
1871 trust_requirement: &TrustRequirement,
1872) -> bool {
1873 trace!(
1874 verification_state = ?encryption_info.verification_state,
1875 ?trust_requirement, "check_to_device_sender_trust_requirement",
1876 );
1877
1878 match (&encryption_info.verification_state, trust_requirement) {
1879 (_, TrustRequirement::Untrusted) => true,
1881
1882 (VerificationState::Verified, _) => true,
1884
1885 (
1888 VerificationState::Unverified(verification_level),
1889 TrustRequirement::CrossSignedOrLegacy | TrustRequirement::CrossSigned,
1890 ) => match verification_level {
1891 VerificationLevel::UnverifiedIdentity => true,
1893
1894 VerificationLevel::UnsignedDevice
1897 | VerificationLevel::None(_)
1898 | VerificationLevel::VerificationViolation
1899 | VerificationLevel::MismatchedSender => false,
1900 },
1901 }
1902}
1903
1904#[cfg(test)]
1905mod tests {
1906 use std::{
1907 collections::{BTreeMap, BTreeSet},
1908 ops::Deref,
1909 time::Duration,
1910 };
1911
1912 use anyhow::Result;
1913 use matrix_sdk_test::async_test;
1914 use ruma::{
1915 DeviceId, MilliSecondsSinceUnixEpoch, OneTimeKeyAlgorithm, OneTimeKeyId, UserId, device_id,
1916 events::room::history_visibility::HistoryVisibility, room_id, user_id,
1917 };
1918 use serde_json::json;
1919
1920 use super::Account;
1921 use crate::{
1922 DeviceData, EncryptionSettings,
1923 olm::{SignedJsonObject, account::shared_history_from_history_visibility},
1924 types::{DeviceKeys, SignedKey},
1925 };
1926
1927 fn user_id() -> &'static UserId {
1928 user_id!("@alice:localhost")
1929 }
1930
1931 fn device_id() -> &'static DeviceId {
1932 device_id!("DEVICEID")
1933 }
1934
1935 #[test]
1936 fn test_one_time_key_creation() -> Result<()> {
1937 let mut account = Account::with_device_id(user_id(), device_id());
1938
1939 let (_, one_time_keys, _) = account.keys_for_upload();
1940 assert!(!one_time_keys.is_empty());
1941
1942 let (_, second_one_time_keys, _) = account.keys_for_upload();
1943 assert!(!second_one_time_keys.is_empty());
1944
1945 let one_time_key_ids: BTreeSet<&OneTimeKeyId> =
1946 one_time_keys.keys().map(Deref::deref).collect();
1947 let second_one_time_key_ids: BTreeSet<&OneTimeKeyId> =
1948 second_one_time_keys.keys().map(Deref::deref).collect();
1949
1950 assert_eq!(one_time_key_ids, second_one_time_key_ids);
1951
1952 account.mark_keys_as_published();
1953 account.update_uploaded_key_count(50);
1954 account.generate_one_time_keys_if_needed();
1955
1956 let (_, third_one_time_keys, _) = account.keys_for_upload();
1957 assert!(third_one_time_keys.is_empty());
1958
1959 account.update_uploaded_key_count(0);
1960 account.generate_one_time_keys_if_needed();
1961
1962 let (_, fourth_one_time_keys, _) = account.keys_for_upload();
1963 assert!(!fourth_one_time_keys.is_empty());
1964
1965 let fourth_one_time_key_ids: BTreeSet<&OneTimeKeyId> =
1966 fourth_one_time_keys.keys().map(Deref::deref).collect();
1967
1968 assert_ne!(one_time_key_ids, fourth_one_time_key_ids);
1969 Ok(())
1970 }
1971
1972 #[test]
1973 fn test_fallback_key_creation() -> Result<()> {
1974 let mut account = Account::with_device_id(user_id(), device_id());
1975
1976 let (_, _, fallback_keys) = account.keys_for_upload();
1977
1978 assert!(
1982 fallback_keys.is_empty(),
1983 "We should not upload fallback keys until we know if the server supports them."
1984 );
1985
1986 let one_time_keys = BTreeMap::from([(OneTimeKeyAlgorithm::SignedCurve25519, 50u8.into())]);
1987
1988 account.update_key_counts(&one_time_keys, None);
1991 let (_, _, fallback_keys) = account.keys_for_upload();
1992 assert!(
1993 fallback_keys.is_empty(),
1994 "We should not upload a fallback key if we're certain that the server doesn't support \
1995 them."
1996 );
1997
1998 let unused_fallback_keys = &[];
2002 account.update_key_counts(&one_time_keys, Some(unused_fallback_keys.as_ref()));
2003 let (_, _, fallback_keys) = account.keys_for_upload();
2004 assert!(
2005 !fallback_keys.is_empty(),
2006 "We should upload the initial fallback key if the server supports them."
2007 );
2008 account.mark_keys_as_published();
2009
2010 let unused_fallback_keys = &[];
2013 account.update_key_counts(&one_time_keys, Some(unused_fallback_keys.as_ref()));
2014 let (_, _, fallback_keys) = account.keys_for_upload();
2015 assert!(
2016 fallback_keys.is_empty(),
2017 "We should not upload new fallback keys unless our current fallback key expires."
2018 );
2019
2020 let fallback_key_timestamp =
2021 account.fallback_creation_timestamp.unwrap().to_system_time().unwrap()
2022 - Duration::from_secs(3600 * 24 * 30);
2023
2024 account.fallback_creation_timestamp =
2025 Some(MilliSecondsSinceUnixEpoch::from_system_time(fallback_key_timestamp).unwrap());
2026
2027 account.update_key_counts(&one_time_keys, None);
2028 let (_, _, fallback_keys) = account.keys_for_upload();
2029 assert!(
2030 !fallback_keys.is_empty(),
2031 "Now that our fallback key has expired, we should try to upload a new one, even if the \
2032 server supposedly doesn't support fallback keys anymore"
2033 );
2034
2035 Ok(())
2036 }
2037
2038 #[test]
2039 fn test_fallback_key_signing() -> Result<()> {
2040 let key = vodozemac::Curve25519PublicKey::from_base64(
2041 "7PUPP6Ijt5R8qLwK2c8uK5hqCNF9tOzWYgGaAay5JBs",
2042 )?;
2043 let account = Account::with_device_id(user_id(), device_id());
2044
2045 let key = account.sign_key(key, true);
2046
2047 let canonical_key = key.to_canonical_json()?;
2048
2049 assert_eq!(
2050 canonical_key,
2051 "{\"fallback\":true,\"key\":\"7PUPP6Ijt5R8qLwK2c8uK5hqCNF9tOzWYgGaAay5JBs\"}"
2052 );
2053
2054 account
2055 .has_signed_raw(key.signatures(), &canonical_key)
2056 .expect("Couldn't verify signature");
2057
2058 let device = DeviceData::from_account(&account);
2059 device.verify_one_time_key(&key).expect("The device can verify its own signature");
2060
2061 Ok(())
2062 }
2063
2064 #[test]
2065 fn test_account_and_device_creation_timestamp() -> Result<()> {
2066 let now = MilliSecondsSinceUnixEpoch::now();
2067 let account = Account::with_device_id(user_id(), device_id());
2068 let then = MilliSecondsSinceUnixEpoch::now();
2069
2070 assert!(account.creation_local_time() >= now);
2071 assert!(account.creation_local_time() <= then);
2072
2073 let device = DeviceData::from_account(&account);
2074 assert_eq!(account.creation_local_time(), device.first_time_seen_ts());
2075
2076 Ok(())
2077 }
2078
2079 #[async_test]
2080 async fn test_fallback_key_signature_verification() -> Result<()> {
2081 let fallback_key = json!({
2082 "fallback": true,
2083 "key": "XPFqtLvBepBmW6jSAbBuJbhEpprBhQOX1IjUu+cnMF4",
2084 "signatures": {
2085 "@dkasak_c:matrix.org": {
2086 "ed25519:EXPDYDPWZH": "RJCBMJPL5hvjxgq8rmLmqkNOuPsaan7JeL1wsE+gW6R39G894lb2sBmzapHeKCn/KFjmkonPLkICApRDS+zyDw"
2087 }
2088 }
2089 });
2090
2091 let device_keys = json!({
2092 "algorithms": [
2093 "m.olm.v1.curve25519-aes-sha2",
2094 "m.megolm.v1.aes-sha2"
2095 ],
2096 "device_id": "EXPDYDPWZH",
2097 "keys": {
2098 "curve25519:EXPDYDPWZH": "k7f3igo0Vrdm88JSSA5d3OCuUfHYELChB2b57aOROB8",
2099 "ed25519:EXPDYDPWZH": "GdjYI8fxs175gSpYRJkyN6FRfvcyTsNOhJ2OR/Ggp+E"
2100 },
2101 "signatures": {
2102 "@dkasak_c:matrix.org": {
2103 "ed25519:EXPDYDPWZH": "kzrtfQMbJXWXQ1uzhybtwFnGk0JJBS4Mg8VPMusMu6U8MPJccwoHVZKo5+owuHTzIodI+GZYqLmMSzvfvsChAA"
2104 }
2105 },
2106 "user_id": "@dkasak_c:matrix.org",
2107 "unsigned": {}
2108 });
2109
2110 let device_keys: DeviceKeys = serde_json::from_value(device_keys).unwrap();
2111 let device = DeviceData::try_from(&device_keys).unwrap();
2112 let fallback_key: SignedKey = serde_json::from_value(fallback_key).unwrap();
2113
2114 device
2115 .verify_one_time_key(&fallback_key)
2116 .expect("The fallback key should pass the signature verification");
2117
2118 Ok(())
2119 }
2120
2121 #[test]
2122 fn test_shared_history_flag_from_history_visibility() {
2123 assert!(
2124 shared_history_from_history_visibility(&HistoryVisibility::WorldReadable),
2125 "The world readable visibility should set the shared history flag to true"
2126 );
2127
2128 assert!(
2129 shared_history_from_history_visibility(&HistoryVisibility::Shared),
2130 "The shared visibility should set the shared history flag to true"
2131 );
2132
2133 assert!(
2134 !shared_history_from_history_visibility(&HistoryVisibility::Joined),
2135 "The joined visibility should set the shared history flag to false"
2136 );
2137
2138 assert!(
2139 !shared_history_from_history_visibility(&HistoryVisibility::Invited),
2140 "The invited visibility should set the shared history flag to false"
2141 );
2142
2143 let visibility = HistoryVisibility::from("custom_visibility");
2144 assert!(
2145 !shared_history_from_history_visibility(&visibility),
2146 "A custom visibility should set the shared history flag to false"
2147 );
2148 }
2149
2150 #[async_test]
2151 async fn test_shared_history_set_when_creating_group_sessions() {
2152 let account = Account::new(user_id());
2153 let room_id = room_id!("!room:id");
2154 let settings = EncryptionSettings {
2155 history_visibility: HistoryVisibility::Shared,
2156 ..Default::default()
2157 };
2158
2159 let (_, session) = account
2160 .create_group_session_pair(room_id, settings, Default::default())
2161 .await
2162 .expect("We should be able to create a group session pair");
2163
2164 assert!(
2165 session.shared_history(),
2166 "The shared history flag should have been set when we created the new session"
2167 );
2168 }
2169}