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