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 uint,
40};
41use serde::{Deserialize, Serialize, de::Error};
42use serde_json::value::{RawValue as RawJsonValue, to_raw_value};
43use sha2::{Digest, Sha256};
44use tokio::sync::Mutex;
45use tracing::{Span, debug, field::debug, info, instrument, trace, warn};
46use vodozemac::{
47 Curve25519PublicKey, Ed25519Signature, KeyId, PickleError, base64_encode,
48 olm::{
49 Account as InnerAccount, AccountPickle, IdentityKeys, OlmMessage,
50 OneTimeKeyGenerationResult, PreKeyMessage, SessionConfig,
51 },
52};
53
54use super::{
55 EncryptionSettings, InboundGroupSession, OutboundGroupSession, PrivateCrossSigningIdentity,
56 Session, SessionCreationError as MegolmSessionCreationError, utility::SignJson,
57};
58#[cfg(feature = "experimental-algorithms")]
59use crate::types::events::room::encrypted::OlmV2Curve25519AesSha2Content;
60#[cfg(feature = "experimental-x509-identity-verification")]
61use crate::x509::{RawX509Signer, X509Signer};
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, 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(
548 &mut self,
549 one_time_key_counts: &BTreeMap<OneTimeKeyAlgorithm, UInt>,
550 unused_fallback_keys: Option<&[OneTimeKeyAlgorithm]>,
551 is_missing_count_zero: bool,
552 ) {
553 let count = if is_missing_count_zero {
554 Some(
555 one_time_key_counts
556 .get(&OneTimeKeyAlgorithm::SignedCurve25519)
557 .copied()
558 .unwrap_or(uint!(0)),
559 )
560 } else {
561 one_time_key_counts.get(&OneTimeKeyAlgorithm::SignedCurve25519).copied()
562 };
563
564 if let Some(count) = count.map(Into::into) {
565 let old_count = self.uploaded_key_count();
566
567 if count != old_count {
571 debug!(
572 "Updated uploaded one-time key count {} -> {count}.",
573 self.uploaded_key_count(),
574 );
575 }
576
577 self.update_uploaded_key_count(count);
578 self.generate_one_time_keys_if_needed();
579 }
580
581 if unused_fallback_keys.is_some() || self.fallback_creation_timestamp.is_some() {
585 self.generate_fallback_key_if_needed();
586 }
587 }
588
589 #[instrument(skip_all)]
598 pub fn generate_one_time_keys_if_needed(&mut self) -> Option<u64> {
599 if !self.one_time_keys().is_empty() {
603 return Some(0);
604 }
605
606 let count = self.uploaded_key_count();
607 let max_keys = self.max_one_time_keys();
608
609 if count >= max_keys as u64 {
610 return None;
611 }
612
613 let key_count = (max_keys as u64) - count;
614 let key_count: usize = key_count.try_into().unwrap_or(max_keys);
615
616 let result = self.generate_one_time_keys(key_count);
617
618 debug!(
619 count = key_count,
620 discarded_keys = ?result.removed,
621 created_keys = ?result.created,
622 "Generated new one-time keys"
623 );
624
625 Some(key_count as u64)
626 }
627
628 pub(crate) fn generate_fallback_key_if_needed(&mut self) {
635 if self.inner.fallback_key().is_empty() && self.fallback_key_expired() {
636 let removed_fallback_key = self.inner.generate_fallback_key();
637 self.fallback_creation_timestamp = Some(MilliSecondsSinceUnixEpoch::now());
638
639 debug!(
640 ?removed_fallback_key,
641 "The fallback key either expired or we didn't have one: generated a new fallback key.",
642 );
643 }
644 }
645
646 fn fallback_key_expired(&self) -> bool {
654 const FALLBACK_KEY_MAX_AGE: Duration = Duration::from_secs(3600 * 24 * 7);
655
656 if let Some(time) = self.fallback_creation_timestamp {
657 let Some(system_time) = time.to_system_time() else {
661 return true;
662 };
663
664 let Ok(elapsed) = system_time.elapsed() else {
668 return true;
669 };
670
671 elapsed > FALLBACK_KEY_MAX_AGE
676 } else {
677 true
680 }
681 }
682
683 fn fallback_key(&self) -> HashMap<KeyId, Curve25519PublicKey> {
684 self.inner.fallback_key()
685 }
686
687 pub fn keys_for_upload(&self) -> (Option<DeviceKeys>, OneTimeKeys, FallbackKeys) {
693 let device_keys = self.shared().not().then(|| self.device_keys());
694
695 let one_time_keys = self.signed_one_time_keys();
696 let fallback_keys = self.signed_fallback_keys();
697
698 (device_keys, one_time_keys, fallback_keys)
699 }
700
701 pub fn mark_keys_as_published(&mut self) {
703 self.inner.mark_keys_as_published();
704 }
705
706 pub fn sign(&self, string: &str) -> Ed25519Signature {
710 self.inner.sign(string)
711 }
712
713 pub fn pickle(&self) -> PickledAccount {
715 let pickle = self.inner.pickle();
716
717 PickledAccount {
718 user_id: self.user_id().to_owned(),
719 device_id: self.device_id().to_owned(),
720 pickle,
721 shared: self.shared(),
722 dehydrated: self.static_data.dehydrated,
723 uploaded_signed_key_count: self.uploaded_key_count(),
724 creation_local_time: self.static_data.creation_local_time,
725 fallback_key_creation_timestamp: self.fallback_creation_timestamp,
726 }
727 }
728
729 pub(crate) fn dehydrate(&self, pickle_key: &[u8; 32]) -> Raw<DehydratedDeviceData> {
730 let dehydration_result = self
731 .inner
732 .to_dehydrated_device(pickle_key)
733 .expect("We should be able to convert a freshly created Account into a libolm pickle");
734
735 let data = DehydratedDeviceData::V2(DehydratedDeviceV2::new(
736 dehydration_result.ciphertext,
737 dehydration_result.nonce,
738 ));
739 Raw::from_json(to_raw_value(&data).expect("Couldn't serialize our dehydrated device data"))
740 }
741
742 pub(crate) fn rehydrate(
743 pickle_key: &[u8; 32],
744 user_id: &UserId,
745 device_id: &DeviceId,
746 device_data: Raw<DehydratedDeviceData>,
747 ) -> Result<Self, DehydrationError> {
748 let data = device_data.deserialize()?;
749
750 match data {
751 DehydratedDeviceData::V1(d) => {
752 let pickle_key = expand_legacy_pickle_key(pickle_key, device_id);
753 let account =
754 InnerAccount::from_libolm_pickle(&d.device_pickle, pickle_key.as_ref())?;
755 Ok(Self::new_helper(account, user_id, device_id))
756 }
757 DehydratedDeviceData::V2(d) => {
758 let account =
759 InnerAccount::from_dehydrated_device(&d.device_pickle, &d.nonce, pickle_key)?;
760 Ok(Self::new_helper(account, user_id, device_id))
761 }
762 _ => Err(DehydrationError::Json(serde_json::Error::custom(format!(
763 "Unsupported dehydrated device algorithm {:?}",
764 data.algorithm()
765 )))),
766 }
767 }
768
769 #[cfg(test)]
772 pub(crate) fn legacy_dehydrate(&self, pickle_key: &[u8; 32]) -> Raw<DehydratedDeviceData> {
773 use ruma::api::client::dehydrated_device::DehydratedDeviceV1;
774
775 let pickle_key = expand_legacy_pickle_key(pickle_key, &self.device_id);
776 let device_pickle = self
777 .inner
778 .to_libolm_pickle(pickle_key.as_ref())
779 .expect("We should be able to convert a freshly created Account into a libolm pickle");
780
781 let data = DehydratedDeviceData::V1(DehydratedDeviceV1::new(device_pickle));
782 Raw::from_json(to_raw_value(&data).expect("Couldn't serialize our dehydrated device data"))
783 }
784
785 pub fn from_pickle(pickle: PickledAccount) -> Result<Self, PickleError> {
794 let account: vodozemac::olm::Account = pickle.pickle.into();
795 let identity_keys = account.identity_keys();
796
797 Ok(Self {
798 static_data: StaticAccountData {
799 user_id: pickle.user_id.clone(),
800 device_id: pickle.device_id.clone(),
801 identity_keys: Arc::new(identity_keys),
802 dehydrated: pickle.dehydrated,
803 creation_local_time: pickle.creation_local_time,
804 },
805 inner: Box::new(account),
806 shared: pickle.shared,
807 uploaded_signed_key_count: pickle.uploaded_signed_key_count,
808 fallback_creation_timestamp: pickle.fallback_key_creation_timestamp,
809 })
810 }
811
812 pub fn device_keys(&self) -> DeviceKeys {
815 let mut device_keys = self.unsigned_device_keys();
816
817 let json_device_keys =
820 to_canonical_value(&device_keys).expect("device key is always safe to serialize");
821 let signature = self
822 .sign_json(json_device_keys)
823 .expect("Newly created device keys can always be signed");
824
825 device_keys.signatures.add_signature(
826 self.user_id().to_owned(),
827 DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, &self.static_data.device_id),
828 signature,
829 );
830
831 device_keys
832 }
833
834 pub async fn bootstrap_cross_signing(
846 &self,
847 #[cfg(feature = "experimental-x509-identity-verification")] x509_signer: Option<
848 Arc<dyn RawX509Signer>,
849 >,
850 ) -> Result<
851 (PrivateCrossSigningIdentity, UploadSigningKeysRequest, SignatureUploadRequest),
852 SignatureError,
853 > {
854 #[cfg(feature = "experimental-x509-identity-verification")]
855 let x509_signer = x509_signer.map(X509Signer::new);
856
857 let identity = PrivateCrossSigningIdentity::for_account(
858 self,
859 #[cfg(feature = "experimental-x509-identity-verification")]
860 x509_signer.as_ref(),
861 )
862 .await?;
863
864 let signature_request = identity.sign_account(self.static_data()).await?;
865
866 let upload_request = identity.as_upload_request().await;
867
868 Ok((identity, upload_request, signature_request))
869 }
870
871 pub fn sign_cross_signing_key(
873 &self,
874 cross_signing_key: &mut CrossSigningKey,
875 ) -> Result<(), SignatureError> {
876 let signature = self.sign_json(to_canonical_value(&cross_signing_key)?)?;
877
878 cross_signing_key.signatures.add_signature(
879 self.user_id().to_owned(),
880 DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, self.device_id()),
881 signature,
882 );
883
884 Ok(())
885 }
886
887 pub fn sign_json(&self, json: CanonicalJsonValue) -> Result<Ed25519Signature, SignatureError> {
895 self.inner.sign_json(json)
896 }
897
898 pub fn signed_one_time_keys(&self) -> OneTimeKeys {
902 let one_time_keys = self.one_time_keys();
903
904 if one_time_keys.is_empty() {
905 BTreeMap::new()
906 } else {
907 self.signed_keys(one_time_keys, false)
908 }
909 }
910
911 pub fn signed_fallback_keys(&self) -> FallbackKeys {
915 let fallback_key = self.fallback_key();
916
917 if fallback_key.is_empty() { BTreeMap::new() } else { self.signed_keys(fallback_key, true) }
918 }
919
920 fn signed_keys(
921 &self,
922 keys: HashMap<KeyId, Curve25519PublicKey>,
923 fallback: bool,
924 ) -> OneTimeKeys {
925 let mut keys_map = BTreeMap::new();
926
927 for (key_id, key) in keys {
928 let signed_key = self.sign_key(key, fallback);
929
930 keys_map.insert(
931 OneTimeKeyId::from_parts(
932 OneTimeKeyAlgorithm::SignedCurve25519,
933 key_id.to_base64().as_str().into(),
934 ),
935 signed_key.into_raw(),
936 );
937 }
938
939 keys_map
940 }
941
942 fn sign_key(&self, key: Curve25519PublicKey, fallback: bool) -> SignedKey {
943 let mut key = if fallback {
944 SignedKey::new_fallback(key.to_owned())
945 } else {
946 SignedKey::new(key.to_owned())
947 };
948
949 let signature = self
950 .sign_json(to_canonical_value(&key).expect("Can't serialize a signed key"))
951 .expect("Newly created one-time keys can always be signed");
952
953 key.signatures_mut().add_signature(
954 self.user_id().to_owned(),
955 DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, self.device_id()),
956 signature,
957 );
958
959 key
960 }
961
962 pub fn create_outbound_session_helper(
982 &self,
983 config: SessionConfig,
984 identity_key: Curve25519PublicKey,
985 one_time_key: Curve25519PublicKey,
986 fallback_used: bool,
987 our_device_keys: DeviceKeys,
988 ) -> Result<Session, vodozemac::olm::SessionCreationError> {
989 let session = self.inner.create_outbound_session(config, identity_key, one_time_key)?;
990
991 let now = SecondsSinceUnixEpoch::now();
992 let session_id = session.session_id();
993
994 Ok(Session {
995 inner: Arc::new(Mutex::new(session)),
996 session_id: session_id.into(),
997 sender_key: identity_key,
998 our_device_keys,
999 created_using_fallback_key: fallback_used,
1000 creation_time: now,
1001 last_use_time: now,
1002 })
1003 }
1004
1005 #[instrument(
1006 skip_all,
1007 fields(
1008 user_id = ?device.user_id(),
1009 device_id = ?device.device_id(),
1010 algorithms = ?device.algorithms()
1011 )
1012 )]
1013 fn find_pre_key_bundle(
1014 device: &DeviceData,
1015 key_map: &OneTimeKeys,
1016 ) -> Result<PrekeyBundle, SessionCreationError> {
1017 let mut keys = key_map.iter();
1018
1019 let first_key = keys.next().ok_or_else(|| {
1020 SessionCreationError::OneTimeKeyMissing(
1021 device.user_id().to_owned(),
1022 device.device_id().into(),
1023 )
1024 })?;
1025
1026 let first_key_id = first_key.0.to_owned();
1027 let first_key = OneTimeKey::deserialize(first_key_id.algorithm(), first_key.1)?;
1028
1029 let result = match first_key {
1030 OneTimeKey::SignedKey(key) => Ok(PrekeyBundle::Olm3DH { key }),
1031 };
1032
1033 trace!(?result, "Finished searching for a valid pre-key bundle");
1034
1035 result
1036 }
1037
1038 #[allow(clippy::result_large_err)]
1053 pub fn create_outbound_session(
1054 &self,
1055 device: &DeviceData,
1056 key_map: &OneTimeKeys,
1057 our_device_keys: DeviceKeys,
1058 ) -> Result<Session, SessionCreationError> {
1059 let pre_key_bundle = Self::find_pre_key_bundle(device, key_map)?;
1060
1061 match pre_key_bundle {
1062 PrekeyBundle::Olm3DH { key } => {
1063 device.verify_one_time_key(&key).map_err(|error| {
1064 SessionCreationError::InvalidSignature {
1065 signing_key: device.ed25519_key().map(Box::new),
1066 one_time_key: key.clone().into(),
1067 error: error.into(),
1068 }
1069 })?;
1070
1071 let identity_key = device.curve25519_key().ok_or_else(|| {
1072 SessionCreationError::DeviceMissingCurveKey(
1073 device.user_id().to_owned(),
1074 device.device_id().into(),
1075 )
1076 })?;
1077
1078 let is_fallback = key.fallback();
1079 let one_time_key = key.key();
1080 let config = device.olm_session_config();
1081
1082 Ok(self.create_outbound_session_helper(
1083 config,
1084 identity_key,
1085 one_time_key,
1086 is_fallback,
1087 our_device_keys,
1088 )?)
1089 }
1090 }
1091 }
1092
1093 pub fn create_inbound_session(
1108 &mut self,
1109 their_identity_key: Curve25519PublicKey,
1110 our_device_keys: DeviceKeys,
1111 message: &PreKeyMessage,
1112 ) -> Result<InboundCreationResult, SessionCreationError> {
1113 Span::current().record("session_id", debug(message.session_id()));
1114 trace!("Creating a new Olm session from a pre-key message");
1115
1116 #[cfg(not(feature = "experimental-algorithms"))]
1117 let config = SessionConfig::version_1();
1118
1119 #[cfg(feature = "experimental-algorithms")]
1120 let config = SessionConfig::version_2();
1121
1122 let result = self.inner.create_inbound_session(config, their_identity_key, message)?;
1123 let now = SecondsSinceUnixEpoch::now();
1124 let session_id = result.session.session_id();
1125
1126 debug!(session=?result.session, "Decrypted an Olm message from a new Olm session");
1127
1128 let session = Session {
1129 inner: Arc::new(Mutex::new(result.session)),
1130 session_id: session_id.into(),
1131 sender_key: their_identity_key,
1132 our_device_keys,
1133 created_using_fallback_key: false,
1134 creation_time: now,
1135 last_use_time: now,
1136 };
1137
1138 let plaintext = String::from_utf8_lossy(&result.plaintext).to_string();
1139
1140 Ok(InboundCreationResult { session, plaintext })
1141 }
1142
1143 #[cfg(any(test, feature = "testing"))]
1144 #[allow(dead_code)]
1145 pub async fn create_session_for_test_helper(
1147 &mut self,
1148 other: &mut Account,
1149 ) -> (Session, Session) {
1150 use ruma::events::dummy::ToDeviceDummyEventContent;
1151
1152 other.generate_one_time_keys(1);
1153 let one_time_map = other.signed_one_time_keys();
1154 let device = DeviceData::from_account(other);
1155
1156 let mut our_session =
1157 self.create_outbound_session(&device, &one_time_map, self.device_keys()).unwrap();
1158
1159 other.mark_keys_as_published();
1160
1161 let message = our_session
1162 .encrypt(&device, "m.dummy", ToDeviceDummyEventContent::new(), None)
1163 .await
1164 .unwrap()
1165 .deserialize()
1166 .unwrap();
1167
1168 #[cfg(feature = "experimental-algorithms")]
1169 let content = if let ToDeviceEncryptedEventContent::OlmV2Curve25519AesSha2(c) = message {
1170 c
1171 } else {
1172 panic!("Invalid encrypted event algorithm {}", message.algorithm());
1173 };
1174
1175 #[cfg(not(feature = "experimental-algorithms"))]
1176 let ToDeviceEncryptedEventContent::OlmV1Curve25519AesSha2(content) = message else {
1177 panic!("Invalid encrypted event algorithm {}", message.algorithm());
1178 };
1179
1180 let OlmMessage::PreKey(prekey) = content.ciphertext else {
1181 panic!("Wrong Olm message type");
1182 };
1183
1184 let our_device = DeviceData::from_account(self);
1185 let other_session = other
1186 .create_inbound_session(
1187 our_device.curve25519_key().unwrap(),
1188 other.device_keys(),
1189 &prekey,
1190 )
1191 .unwrap();
1192
1193 (our_session, other_session.session)
1194 }
1195
1196 async fn decrypt_olm_helper(
1197 &mut self,
1198 store: &Store,
1199 sender: &UserId,
1200 sender_key: Curve25519PublicKey,
1201 ciphertext: &OlmMessage,
1202 decryption_settings: &DecryptionSettings,
1203 ) -> OlmResult<OlmDecryptionInfo> {
1204 let message_hash = OlmMessageHash::new(sender_key, ciphertext);
1205
1206 match self
1207 .decrypt_and_parse_olm_message(
1208 store,
1209 sender,
1210 sender_key,
1211 ciphertext,
1212 decryption_settings,
1213 )
1214 .await
1215 {
1216 Ok((session, result)) => {
1217 Ok(OlmDecryptionInfo { session, message_hash, result, inbound_group_session: None })
1218 }
1219 Err(OlmError::SessionWedged(user_id, sender_key)) => {
1220 if store.is_message_known(&message_hash).await? {
1221 info!(?sender_key, "An Olm message got replayed, decryption failed");
1222 Err(OlmError::ReplayedMessage(user_id, sender_key))
1223 } else {
1224 Err(OlmError::SessionWedged(user_id, sender_key))
1225 }
1226 }
1227 Err(e) => Err(e),
1228 }
1229 }
1230
1231 #[cfg(feature = "experimental-algorithms")]
1232 async fn decrypt_olm_v2(
1233 &mut self,
1234 store: &Store,
1235 sender: &UserId,
1236 content: &OlmV2Curve25519AesSha2Content,
1237 decryption_settings: &DecryptionSettings,
1238 ) -> OlmResult<OlmDecryptionInfo> {
1239 self.decrypt_olm_helper(
1240 store,
1241 sender,
1242 content.sender_key,
1243 &content.ciphertext,
1244 decryption_settings,
1245 )
1246 .await
1247 }
1248
1249 #[instrument(skip_all, fields(sender, sender_key = ?content.sender_key))]
1250 async fn decrypt_olm_v1(
1251 &mut self,
1252 store: &Store,
1253 sender: &UserId,
1254 content: &OlmV1Curve25519AesSha2Content,
1255 decryption_settings: &DecryptionSettings,
1256 ) -> OlmResult<OlmDecryptionInfo> {
1257 if content.recipient_key != self.static_data.identity_keys.curve25519 {
1258 warn!("Olm event doesn't contain a ciphertext for our key");
1259
1260 Err(EventError::MissingCiphertext.into())
1261 } else {
1262 Box::pin(self.decrypt_olm_helper(
1263 store,
1264 sender,
1265 content.sender_key,
1266 &content.ciphertext,
1267 decryption_settings,
1268 ))
1269 .await
1270 }
1271 }
1272
1273 #[instrument(skip_all, fields(algorithm = ?event.content.algorithm()))]
1274 pub(crate) async fn decrypt_to_device_event(
1275 &mut self,
1276 store: &Store,
1277 event: &EncryptedToDeviceEvent,
1278 decryption_settings: &DecryptionSettings,
1279 ) -> OlmResult<OlmDecryptionInfo> {
1280 trace!("Decrypting a to-device event");
1281
1282 match &event.content {
1283 ToDeviceEncryptedEventContent::OlmV1Curve25519AesSha2(c) => {
1284 self.decrypt_olm_v1(store, &event.sender, c, decryption_settings).await
1285 }
1286 #[cfg(feature = "experimental-algorithms")]
1287 ToDeviceEncryptedEventContent::OlmV2Curve25519AesSha2(c) => {
1288 self.decrypt_olm_v2(store, &event.sender, c, decryption_settings).await
1289 }
1290 ToDeviceEncryptedEventContent::Unknown(_) => {
1291 warn!(
1292 "Error decrypting an to-device event, unsupported \
1293 encryption algorithm"
1294 );
1295
1296 Err(EventError::UnsupportedAlgorithm.into())
1297 }
1298 }
1299 }
1300
1301 pub fn receive_keys_upload_response(
1303 &mut self,
1304 response: &upload_keys::v3::Response,
1305 ) -> OlmResult<()> {
1306 if !self.shared() {
1307 debug!("Marking account as shared");
1308 }
1309 self.mark_as_shared();
1310
1311 debug!("Marking one-time keys as published");
1312 self.mark_keys_as_published();
1315 self.update_key_counts(&response.one_time_key_counts, None, false);
1316
1317 Ok(())
1318 }
1319
1320 async fn decrypt_olm_message(
1322 &mut self,
1323 store: &Store,
1324 sender: &UserId,
1325 sender_key: Curve25519PublicKey,
1326 message: &OlmMessage,
1327 ) -> Result<(SessionType, String), OlmError> {
1328 let existing_sessions = store.get_sessions(&sender_key.to_base64()).await?;
1329
1330 match message {
1331 OlmMessage::Normal(_) => {
1332 let mut errors_by_olm_session = Vec::new();
1333
1334 if let Some(sessions) = existing_sessions {
1335 for session in sessions.lock().await.iter_mut() {
1338 match session.decrypt(message).await {
1339 Ok(p) => {
1340 return Ok((SessionType::Existing(session.clone()), p));
1342 }
1343
1344 Err(e) => {
1345 errors_by_olm_session.push((session.session_id().to_owned(), e));
1350 }
1351 }
1352 }
1353 }
1354
1355 warn!(
1356 ?errors_by_olm_session,
1357 "Failed to decrypt a non-pre-key message with all available sessions"
1358 );
1359 Err(OlmError::SessionWedged(sender.to_owned(), sender_key))
1360 }
1361
1362 OlmMessage::PreKey(prekey_message) => {
1363 if let Some(sessions) = existing_sessions {
1365 for session in sessions.lock().await.iter_mut() {
1366 if prekey_message.session_id() != session.session_id() {
1367 continue;
1369 }
1370
1371 if let Ok(p) = session.decrypt(message).await {
1372 return Ok((SessionType::Existing(session.clone()), p));
1374 }
1375
1376 warn!(
1390 session_id = session.session_id(),
1391 "Failed to decrypt a pre-key message with the corresponding session"
1392 );
1393
1394 return Err(OlmError::SessionWedged(
1395 session.our_device_keys.user_id.to_owned(),
1396 session.sender_key(),
1397 ));
1398 }
1399 }
1400
1401 let device_keys = store.get_own_device().await?.as_device_keys().clone();
1402 let result =
1403 match self.create_inbound_session(sender_key, device_keys, prekey_message) {
1404 Ok(r) => r,
1405 Err(e) => {
1406 warn!(
1407 "Failed to create a new Olm session from a pre-key message: {e:?}"
1408 );
1409 return Err(OlmError::SessionWedged(sender.to_owned(), sender_key));
1410 }
1411 };
1412
1413 let mut changes =
1418 Changes { sessions: vec![result.session.clone()], ..Default::default() };
1419
1420 if let Some(device) = store.get_device_from_curve_key(sender, sender_key).await? {
1427 let mut device_data = device.inner;
1428 device_data.olm_wedging_index.increment();
1429
1430 changes.devices =
1431 DeviceChanges { changed: vec![device_data], ..Default::default() };
1432 }
1433
1434 store.save_changes(changes).await?;
1435
1436 Ok((SessionType::New(result.session), result.plaintext))
1437 }
1438 }
1439 }
1440
1441 #[instrument(skip(self, store), fields(session, session_id))]
1444 async fn decrypt_and_parse_olm_message(
1445 &mut self,
1446 store: &Store,
1447 sender: &UserId,
1448 sender_key: Curve25519PublicKey,
1449 message: &OlmMessage,
1450 decryption_settings: &DecryptionSettings,
1451 ) -> OlmResult<(SessionType, DecryptionResult)> {
1452 let (session, plaintext) =
1453 self.decrypt_olm_message(store, sender, sender_key, message).await?;
1454
1455 trace!("Successfully decrypted an Olm message");
1456
1457 match self
1458 .parse_decrypted_to_device_event(
1459 store,
1460 sender,
1461 sender_key,
1462 plaintext,
1463 decryption_settings,
1464 )
1465 .await
1466 {
1467 Ok(result) => Ok((session, result)),
1468 Err(e) => {
1469 match session {
1473 SessionType::New(s) | SessionType::Existing(s) => {
1474 store.save_sessions(&[s]).await?;
1475 }
1476 }
1477
1478 warn!(
1479 error = ?e,
1480 "A to-device message was successfully decrypted but \
1481 parsing and checking the event fields failed"
1482 );
1483
1484 Err(e)
1485 }
1486 }
1487 }
1488
1489 async fn parse_decrypted_to_device_event(
1510 &self,
1511 store: &Store,
1512 sender: &UserId,
1513 sender_key: Curve25519PublicKey,
1514 plaintext: String,
1515 decryption_settings: &DecryptionSettings,
1516 ) -> OlmResult<DecryptionResult> {
1517 let event: Box<AnyDecryptedOlmEvent> = serde_json::from_str(&plaintext)?;
1518 let identity_keys = &self.static_data.identity_keys;
1519
1520 if event.recipient() != self.static_data.user_id {
1521 Err(EventError::MismatchedSender(
1522 event.recipient().to_owned(),
1523 self.static_data.user_id.clone(),
1524 )
1525 .into())
1526 }
1527 else if event.sender() != sender {
1530 Err(EventError::MismatchedSender(event.sender().to_owned(), sender.to_owned()).into())
1531 } else if identity_keys.ed25519 != event.recipient_keys().ed25519 {
1532 Err(EventError::MismatchedKeys(
1533 identity_keys.ed25519.into(),
1534 event.recipient_keys().ed25519.into(),
1535 )
1536 .into())
1537 } else {
1538 let sender_device = Self::get_event_sender_device(store, sender_key, &event).await?;
1539 let encryption_info = Self::get_olm_encryption_info(sender_key, sender, &sender_device);
1540
1541 let result = DecryptionResult {
1542 event,
1543 raw_event: Raw::from_json(RawJsonValue::from_string(plaintext)?),
1544 sender_key,
1545 encryption_info,
1546 };
1547
1548 if !self.is_from_verified_device_or_allowed_type(decryption_settings, &result) {
1550 Err(OlmError::UnverifiedSenderDevice)
1551 } else {
1552 Ok(result)
1554 }
1555 }
1556 }
1557
1558 async fn get_event_sender_device(
1572 store: &Store,
1573 sender_key: Curve25519PublicKey,
1574 event: &AnyDecryptedOlmEvent,
1575 ) -> OlmResult<Option<Device>> {
1576 let sender_device_keys = Self::check_sender_device_keys(event, sender_key)?;
1581 if let AnyDecryptedOlmEvent::RoomKey(_) = event {
1582 return Ok(None);
1587 }
1588
1589 if let AnyDecryptedOlmEvent::RoomKeyBundle(_) = event {
1592 sender_device_keys.ok_or(EventError::MissingSigningKey).inspect_err(|_| {
1593 warn!("The room key bundle was missing the sender device keys in the event")
1594 })?;
1595 }
1596
1597 let store_device = store.get_device_from_curve_key(event.sender(), sender_key).await?;
1602
1603 match (store_device, sender_device_keys) {
1604 (Some(device), _) => {
1607 let key = device.ed25519_key().ok_or(EventError::MissingSigningKey)?;
1608 if key != event.keys().ed25519 {
1609 return Err(EventError::MismatchedKeys(
1610 key.into(),
1611 event.keys().ed25519.into(),
1612 )
1613 .into());
1614 }
1615 Ok(Some(device))
1616 }
1617
1618 (None, Some(sender_device_keys)) => {
1619 let sender_device_data = sender_device_keys.try_into().expect("Conversion of DeviceKeys to DeviceData failed despite the signature already having been checked");
1622 Ok(Some(store.wrap_device_data(sender_device_data).await?))
1623 }
1624
1625 (None, None) => Err(OlmError::EventError(EventError::MissingSigningKey)),
1626 }
1627 }
1628
1629 fn is_from_verified_device_or_allowed_type(
1642 &self,
1643 decryption_settings: &DecryptionSettings,
1644 result: &DecryptionResult,
1645 ) -> bool {
1646 let event_type = result.event.event_type();
1647
1648 match event_type {
1660 "m.room_key"
1661 | "m.room_key.withheld"
1662 | "m.room_key_request"
1663 | "m.secret.request"
1664 | "m.key.verification.key"
1665 | "m.key.verification.mac"
1666 | "m.key.verification.done"
1667 | "m.key.verification.ready"
1668 | "m.key.verification.start"
1669 | "m.key.verification.accept"
1670 | "m.key.verification.cancel"
1671 | "m.key.verification.request" => {
1672 true
1675 }
1676 _ => {
1677 satisfies_sender_trust_requirement(
1680 &result.encryption_info,
1681 &decryption_settings.sender_device_trust_requirement,
1682 )
1683 }
1684 }
1685 }
1686
1687 fn get_olm_encryption_info(
1694 sender_key: Curve25519PublicKey,
1695 sender_id: &UserId,
1696 sender_device: &Option<Device>,
1697 ) -> EncryptionInfo {
1698 let verification_state = sender_device
1699 .as_ref()
1700 .map(|device| {
1701 if device.is_verified() {
1702 VerificationState::Verified
1704 } else if device.is_cross_signed_by_owner() {
1705 if device
1707 .device_owner_identity
1708 .as_ref()
1709 .expect("A device cross-signed by the owner must have an owner identity")
1710 .was_previously_verified()
1711 {
1712 VerificationState::Unverified(VerificationLevel::VerificationViolation)
1713 } else {
1714 VerificationState::Unverified(VerificationLevel::UnverifiedIdentity)
1715 }
1716 } else {
1717 VerificationState::Unverified(VerificationLevel::UnsignedDevice)
1719 }
1720 })
1721 .unwrap_or(VerificationState::Unverified(VerificationLevel::None(
1722 DeviceLinkProblem::MissingDevice,
1723 )));
1724
1725 EncryptionInfo {
1726 sender: sender_id.to_owned(),
1727 sender_device: sender_device.as_ref().map(|d| d.device_id().to_owned()),
1728 forwarder: None,
1729 algorithm_info: AlgorithmInfo::OlmV1Curve25519AesSha2 {
1730 curve25519_public_key_base64: sender_key.to_base64(),
1731 },
1732 verification_state,
1733 }
1734 }
1735
1736 fn check_sender_device_keys(
1784 event: &AnyDecryptedOlmEvent,
1785 sender_key: Curve25519PublicKey,
1786 ) -> OlmResult<Option<&DeviceKeys>> {
1787 let Some(sender_device_keys) = event.sender_device_keys() else {
1788 return Ok(None);
1789 };
1790
1791 if sender_device_keys.user_id != event.sender() {
1792 warn!(
1793 "Received a to-device message with sender_device_keys with incorrect user_id: expected {:?}, got {:?}",
1794 event.sender(),
1795 sender_device_keys.user_id
1796 );
1797 return Err(OlmError::EventError(EventError::InvalidSenderDeviceKeys));
1798 }
1799
1800 sender_device_keys.check_self_signature().map_err(|err| {
1802 warn!(
1803 "Received a to-device message with sender_device_keys with \
1804 invalid signature: {err:?}",
1805 );
1806 OlmError::EventError(EventError::InvalidSenderDeviceKeys)
1807 })?;
1808
1809 if sender_device_keys.ed25519_key() != Some(event.keys().ed25519) {
1812 warn!(
1813 "Received a to-device message with sender_device_keys with incorrect \
1814 ed25519 key: expected {:?}, got {:?}",
1815 event.keys().ed25519,
1816 sender_device_keys.ed25519_key(),
1817 );
1818 return Err(OlmError::EventError(EventError::InvalidSenderDeviceKeys));
1819 }
1820
1821 if sender_device_keys.curve25519_key() != Some(sender_key) {
1824 warn!(
1825 "Received a to-device message with sender_device_keys with incorrect \
1826 curve25519 key: expected {sender_key:?}, got {:?}",
1827 sender_device_keys.curve25519_key(),
1828 );
1829 return Err(OlmError::EventError(EventError::InvalidSenderDeviceKeys));
1830 }
1831
1832 Ok(Some(sender_device_keys))
1833 }
1834
1835 #[doc(hidden)]
1840 pub fn deep_clone(&self) -> Self {
1841 Self::from_pickle(self.pickle()).unwrap()
1843 }
1844}
1845
1846impl PartialEq for Account {
1847 fn eq(&self, other: &Self) -> bool {
1848 self.identity_keys() == other.identity_keys() && self.shared() == other.shared()
1849 }
1850}
1851
1852pub(crate) fn shared_history_from_history_visibility(
1872 history_visibility: &HistoryVisibility,
1873) -> bool {
1874 match history_visibility {
1875 HistoryVisibility::Shared | HistoryVisibility::WorldReadable => true,
1876 HistoryVisibility::Invited | HistoryVisibility::Joined | _ => false,
1877 }
1878}
1879
1880fn expand_legacy_pickle_key(key: &[u8; 32], device_id: &DeviceId) -> Box<[u8; 32]> {
1899 let kdf: Hkdf<Sha256> = Hkdf::new(Some(device_id.as_bytes()), key);
1900 let mut key = Box::new([0u8; 32]);
1901
1902 kdf.expand(b"dehydrated-device-pickle-key", key.as_mut_slice())
1903 .expect("We should be able to expand the 32 byte pickle key");
1904
1905 key
1906}
1907
1908fn satisfies_sender_trust_requirement(
1911 encryption_info: &EncryptionInfo,
1912 trust_requirement: &TrustRequirement,
1913) -> bool {
1914 trace!(
1915 verification_state = ?encryption_info.verification_state,
1916 ?trust_requirement, "check_to_device_sender_trust_requirement",
1917 );
1918
1919 match (&encryption_info.verification_state, trust_requirement) {
1920 (_, TrustRequirement::Untrusted) => true,
1922
1923 (VerificationState::Verified, _) => true,
1925
1926 (
1929 VerificationState::Unverified(verification_level),
1930 TrustRequirement::CrossSignedOrLegacy | TrustRequirement::CrossSigned,
1931 ) => match verification_level {
1932 VerificationLevel::UnverifiedIdentity => true,
1934
1935 VerificationLevel::UnsignedDevice
1938 | VerificationLevel::None(_)
1939 | VerificationLevel::VerificationViolation
1940 | VerificationLevel::MismatchedSender => false,
1941 },
1942 }
1943}
1944
1945#[cfg(test)]
1946mod tests {
1947 use std::{
1948 collections::{BTreeMap, BTreeSet},
1949 ops::Deref,
1950 time::Duration,
1951 };
1952
1953 use anyhow::Result;
1954 use matrix_sdk_test::async_test;
1955 use ruma::{
1956 DeviceId, MilliSecondsSinceUnixEpoch, OneTimeKeyAlgorithm, OneTimeKeyId, UserId, device_id,
1957 events::room::history_visibility::HistoryVisibility, room_id, user_id,
1958 };
1959 use serde_json::json;
1960
1961 use super::Account;
1962 use crate::{
1963 DeviceData, EncryptionSettings,
1964 olm::{SignedJsonObject, account::shared_history_from_history_visibility},
1965 types::{DeviceKeys, SignedKey},
1966 };
1967
1968 fn user_id() -> &'static UserId {
1969 user_id!("@alice:localhost")
1970 }
1971
1972 fn device_id() -> &'static DeviceId {
1973 device_id!("DEVICEID")
1974 }
1975
1976 #[test]
1977 fn test_one_time_key_creation() -> Result<()> {
1978 let mut account = Account::with_device_id(user_id(), device_id());
1979
1980 let (_, one_time_keys, _) = account.keys_for_upload();
1981 assert!(!one_time_keys.is_empty());
1982
1983 let (_, second_one_time_keys, _) = account.keys_for_upload();
1984 assert!(!second_one_time_keys.is_empty());
1985
1986 let one_time_key_ids: BTreeSet<&OneTimeKeyId> =
1987 one_time_keys.keys().map(Deref::deref).collect();
1988 let second_one_time_key_ids: BTreeSet<&OneTimeKeyId> =
1989 second_one_time_keys.keys().map(Deref::deref).collect();
1990
1991 assert_eq!(one_time_key_ids, second_one_time_key_ids);
1992
1993 account.mark_keys_as_published();
1994 account.update_uploaded_key_count(50);
1995 account.generate_one_time_keys_if_needed();
1996
1997 let (_, third_one_time_keys, _) = account.keys_for_upload();
1998 assert!(third_one_time_keys.is_empty());
1999
2000 account.update_uploaded_key_count(0);
2001 account.generate_one_time_keys_if_needed();
2002
2003 let (_, fourth_one_time_keys, _) = account.keys_for_upload();
2004 assert!(!fourth_one_time_keys.is_empty());
2005
2006 let fourth_one_time_key_ids: BTreeSet<&OneTimeKeyId> =
2007 fourth_one_time_keys.keys().map(Deref::deref).collect();
2008
2009 assert_ne!(one_time_key_ids, fourth_one_time_key_ids);
2010 Ok(())
2011 }
2012
2013 #[test]
2014 fn test_fallback_key_creation() -> Result<()> {
2015 let mut account = Account::with_device_id(user_id(), device_id());
2016
2017 let (_, _, fallback_keys) = account.keys_for_upload();
2018
2019 assert!(
2023 fallback_keys.is_empty(),
2024 "We should not upload fallback keys until we know if the server supports them."
2025 );
2026
2027 let one_time_keys = BTreeMap::from([(OneTimeKeyAlgorithm::SignedCurve25519, 50u8.into())]);
2028
2029 account.update_key_counts(&one_time_keys, None, false);
2032 let (_, _, fallback_keys) = account.keys_for_upload();
2033 assert!(
2034 fallback_keys.is_empty(),
2035 "We should not upload a fallback key if we're certain that the server doesn't support \
2036 them."
2037 );
2038
2039 let unused_fallback_keys = &[];
2043 account.update_key_counts(&one_time_keys, Some(unused_fallback_keys.as_ref()), false);
2044 let (_, _, fallback_keys) = account.keys_for_upload();
2045 assert!(
2046 !fallback_keys.is_empty(),
2047 "We should upload the initial fallback key if the server supports them."
2048 );
2049 account.mark_keys_as_published();
2050
2051 let unused_fallback_keys = &[];
2054 account.update_key_counts(&one_time_keys, Some(unused_fallback_keys.as_ref()), false);
2055 let (_, _, fallback_keys) = account.keys_for_upload();
2056 assert!(
2057 fallback_keys.is_empty(),
2058 "We should not upload new fallback keys unless our current fallback key expires."
2059 );
2060
2061 let fallback_key_timestamp =
2062 account.fallback_creation_timestamp.unwrap().to_system_time().unwrap()
2063 - Duration::from_secs(3600 * 24 * 30);
2064
2065 account.fallback_creation_timestamp =
2066 Some(MilliSecondsSinceUnixEpoch::from_system_time(fallback_key_timestamp).unwrap());
2067
2068 account.update_key_counts(&one_time_keys, None, false);
2069 let (_, _, fallback_keys) = account.keys_for_upload();
2070 assert!(
2071 !fallback_keys.is_empty(),
2072 "Now that our fallback key has expired, we should try to upload a new one, even if the \
2073 server supposedly doesn't support fallback keys anymore"
2074 );
2075
2076 Ok(())
2077 }
2078
2079 #[test]
2080 fn test_fallback_key_signing() -> Result<()> {
2081 let key = vodozemac::Curve25519PublicKey::from_base64(
2082 "7PUPP6Ijt5R8qLwK2c8uK5hqCNF9tOzWYgGaAay5JBs",
2083 )?;
2084 let account = Account::with_device_id(user_id(), device_id());
2085
2086 let key = account.sign_key(key, true);
2087
2088 let canonical_key = key.to_canonical_json()?;
2089
2090 assert_eq!(
2091 canonical_key,
2092 "{\"fallback\":true,\"key\":\"7PUPP6Ijt5R8qLwK2c8uK5hqCNF9tOzWYgGaAay5JBs\"}"
2093 );
2094
2095 account
2096 .has_signed_raw(key.signatures(), &canonical_key)
2097 .expect("Couldn't verify signature");
2098
2099 let device = DeviceData::from_account(&account);
2100 device.verify_one_time_key(&key).expect("The device can verify its own signature");
2101
2102 Ok(())
2103 }
2104
2105 #[test]
2106 fn test_account_and_device_creation_timestamp() -> Result<()> {
2107 let now = MilliSecondsSinceUnixEpoch::now();
2108 let account = Account::with_device_id(user_id(), device_id());
2109 let then = MilliSecondsSinceUnixEpoch::now();
2110
2111 assert!(account.creation_local_time() >= now);
2112 assert!(account.creation_local_time() <= then);
2113
2114 let device = DeviceData::from_account(&account);
2115 assert_eq!(account.creation_local_time(), device.first_time_seen_ts());
2116
2117 Ok(())
2118 }
2119
2120 #[async_test]
2121 async fn test_fallback_key_signature_verification() -> Result<()> {
2122 let fallback_key = json!({
2123 "fallback": true,
2124 "key": "XPFqtLvBepBmW6jSAbBuJbhEpprBhQOX1IjUu+cnMF4",
2125 "signatures": {
2126 "@dkasak_c:matrix.org": {
2127 "ed25519:EXPDYDPWZH": "RJCBMJPL5hvjxgq8rmLmqkNOuPsaan7JeL1wsE+gW6R39G894lb2sBmzapHeKCn/KFjmkonPLkICApRDS+zyDw"
2128 }
2129 }
2130 });
2131
2132 let device_keys = json!({
2133 "algorithms": [
2134 "m.olm.v1.curve25519-aes-sha2",
2135 "m.megolm.v1.aes-sha2"
2136 ],
2137 "device_id": "EXPDYDPWZH",
2138 "keys": {
2139 "curve25519:EXPDYDPWZH": "k7f3igo0Vrdm88JSSA5d3OCuUfHYELChB2b57aOROB8",
2140 "ed25519:EXPDYDPWZH": "GdjYI8fxs175gSpYRJkyN6FRfvcyTsNOhJ2OR/Ggp+E"
2141 },
2142 "signatures": {
2143 "@dkasak_c:matrix.org": {
2144 "ed25519:EXPDYDPWZH": "kzrtfQMbJXWXQ1uzhybtwFnGk0JJBS4Mg8VPMusMu6U8MPJccwoHVZKo5+owuHTzIodI+GZYqLmMSzvfvsChAA"
2145 }
2146 },
2147 "user_id": "@dkasak_c:matrix.org",
2148 "unsigned": {}
2149 });
2150
2151 let device_keys: DeviceKeys = serde_json::from_value(device_keys).unwrap();
2152 let device = DeviceData::try_from(&device_keys).unwrap();
2153 let fallback_key: SignedKey = serde_json::from_value(fallback_key).unwrap();
2154
2155 device
2156 .verify_one_time_key(&fallback_key)
2157 .expect("The fallback key should pass the signature verification");
2158
2159 Ok(())
2160 }
2161
2162 #[test]
2163 fn test_shared_history_flag_from_history_visibility() {
2164 assert!(
2165 shared_history_from_history_visibility(&HistoryVisibility::WorldReadable),
2166 "The world readable visibility should set the shared history flag to true"
2167 );
2168
2169 assert!(
2170 shared_history_from_history_visibility(&HistoryVisibility::Shared),
2171 "The shared visibility should set the shared history flag to true"
2172 );
2173
2174 assert!(
2175 !shared_history_from_history_visibility(&HistoryVisibility::Joined),
2176 "The joined visibility should set the shared history flag to false"
2177 );
2178
2179 assert!(
2180 !shared_history_from_history_visibility(&HistoryVisibility::Invited),
2181 "The invited visibility should set the shared history flag to false"
2182 );
2183
2184 let visibility = HistoryVisibility::from("custom_visibility");
2185 assert!(
2186 !shared_history_from_history_visibility(&visibility),
2187 "A custom visibility should set the shared history flag to false"
2188 );
2189 }
2190
2191 #[async_test]
2192 async fn test_shared_history_set_when_creating_group_sessions() {
2193 let account = Account::new(user_id());
2194 let room_id = room_id!("!room:id");
2195 let settings = EncryptionSettings {
2196 history_visibility: HistoryVisibility::Shared,
2197 ..Default::default()
2198 };
2199
2200 let (_, session) = account
2201 .create_group_session_pair(room_id, settings, Default::default())
2202 .await
2203 .expect("We should be able to create a group session pair");
2204
2205 assert!(
2206 session.shared_history(),
2207 "The shared history flag should have been set when we created the new session"
2208 );
2209 }
2210}