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