1use std::{
16 collections::{BTreeMap, HashMap},
17 fmt,
18 ops::{Deref, Not as _},
19 sync::Arc,
20 time::Duration,
21};
22
23use hkdf::Hkdf;
24use js_option::JsOption;
25use matrix_sdk_common::deserialized_responses::{
26 AlgorithmInfo, DeviceLinkProblem, EncryptionInfo, VerificationLevel, VerificationState,
27};
28#[cfg(test)]
29use ruma::api::client::dehydrated_device::DehydratedDeviceV1;
30use ruma::{
31 api::client::{
32 dehydrated_device::{DehydratedDeviceData, DehydratedDeviceV2},
33 keys::{
34 upload_keys,
35 upload_signatures::v3::{Request as SignatureUploadRequest, SignedKeys},
36 },
37 },
38 events::{room::history_visibility::HistoryVisibility, AnyToDeviceEvent},
39 serde::Raw,
40 DeviceId, DeviceKeyAlgorithm, DeviceKeyId, MilliSecondsSinceUnixEpoch, OneTimeKeyAlgorithm,
41 OneTimeKeyId, OwnedDeviceId, OwnedDeviceKeyId, OwnedOneTimeKeyId, OwnedUserId, RoomId,
42 SecondsSinceUnixEpoch, UInt, UserId,
43};
44use serde::{de::Error, Deserialize, Serialize};
45use serde_json::{
46 value::{to_raw_value, RawValue as RawJsonValue},
47 Value,
48};
49use sha2::{Digest, Sha256};
50use tokio::sync::Mutex;
51use tracing::{debug, field::debug, info, instrument, trace, warn, Span};
52use vodozemac::{
53 base64_encode,
54 olm::{
55 Account as InnerAccount, AccountPickle, IdentityKeys, OlmMessage,
56 OneTimeKeyGenerationResult, PreKeyMessage, SessionConfig,
57 },
58 Curve25519PublicKey, Ed25519Signature, KeyId, PickleError,
59};
60
61use super::{
62 utility::SignJson, EncryptionSettings, InboundGroupSession, OutboundGroupSession,
63 PrivateCrossSigningIdentity, Session, SessionCreationError as MegolmSessionCreationError,
64};
65#[cfg(feature = "experimental-algorithms")]
66use crate::types::events::room::encrypted::OlmV2Curve25519AesSha2Content;
67use crate::{
68 dehydrated_devices::DehydrationError,
69 error::{EventError, OlmResult, SessionCreationError},
70 identities::DeviceData,
71 olm::SenderData,
72 store::{
73 types::{Changes, DeviceChanges},
74 Store,
75 },
76 types::{
77 events::{
78 olm_v1::AnyDecryptedOlmEvent,
79 room::encrypted::{
80 EncryptedToDeviceEvent, OlmV1Curve25519AesSha2Content,
81 ToDeviceEncryptedEventContent,
82 },
83 },
84 requests::UploadSigningKeysRequest,
85 CrossSigningKey, DeviceKeys, EventEncryptionAlgorithm, MasterPubkey, OneTimeKey, SignedKey,
86 },
87 DecryptionSettings, Device, OlmError, SignatureError, TrustRequirement,
88};
89
90#[derive(Debug)]
91enum PrekeyBundle {
92 Olm3DH { key: SignedKey },
93}
94
95#[derive(Debug, Clone)]
96pub(crate) enum SessionType {
97 New(Session),
98 Existing(Session),
99}
100
101#[derive(Debug)]
102pub struct InboundCreationResult {
103 pub session: Session,
104 pub plaintext: String,
105}
106
107impl SessionType {
108 #[cfg(test)]
109 pub fn session(self) -> Session {
110 match self {
111 SessionType::New(s) => s,
112 SessionType::Existing(s) => s,
113 }
114 }
115}
116
117#[derive(Debug)]
123pub(crate) struct OlmDecryptionInfo {
124 pub session: SessionType,
125 pub message_hash: OlmMessageHash,
126 pub inbound_group_session: Option<InboundGroupSession>,
127 pub result: DecryptionResult,
128}
129
130#[derive(Debug)]
131pub(crate) struct DecryptionResult {
132 pub event: Box<AnyDecryptedOlmEvent>,
134 pub raw_event: Raw<AnyToDeviceEvent>,
135 pub sender_key: Curve25519PublicKey,
136 pub encryption_info: EncryptionInfo,
137}
138
139#[derive(Debug, Clone, Serialize, Deserialize)]
143pub struct OlmMessageHash {
144 pub sender_key: String,
146 pub hash: String,
148}
149
150impl OlmMessageHash {
151 fn new(sender_key: Curve25519PublicKey, ciphertext: &OlmMessage) -> Self {
152 let (message_type, ciphertext) = ciphertext.clone().to_parts();
153 let sender_key = sender_key.to_base64();
154
155 let sha = Sha256::new()
156 .chain_update(sender_key.as_bytes())
157 .chain_update([message_type as u8])
158 .chain_update(ciphertext)
159 .finalize();
160
161 Self { sender_key, hash: base64_encode(sha.as_slice()) }
162 }
163}
164
165#[derive(Clone)]
170#[cfg_attr(not(tarpaulin_include), derive(Debug))]
171pub struct StaticAccountData {
172 pub user_id: OwnedUserId,
174 pub device_id: OwnedDeviceId,
176 pub identity_keys: Arc<IdentityKeys>,
178 pub dehydrated: bool,
180 creation_local_time: MilliSecondsSinceUnixEpoch,
182}
183
184impl StaticAccountData {
185 const ALGORITHMS: &'static [&'static EventEncryptionAlgorithm] = &[
186 &EventEncryptionAlgorithm::OlmV1Curve25519AesSha2,
187 #[cfg(feature = "experimental-algorithms")]
188 &EventEncryptionAlgorithm::OlmV2Curve25519AesSha2,
189 &EventEncryptionAlgorithm::MegolmV1AesSha2,
190 #[cfg(feature = "experimental-algorithms")]
191 &EventEncryptionAlgorithm::MegolmV2AesSha2,
192 ];
193
194 pub async fn create_group_session_pair(
209 &self,
210 room_id: &RoomId,
211 settings: EncryptionSettings,
212 own_sender_data: SenderData,
213 ) -> Result<(OutboundGroupSession, InboundGroupSession), MegolmSessionCreationError> {
214 trace!(?room_id, algorithm = settings.algorithm.as_str(), "Creating a new room key");
215
216 let visibility = settings.history_visibility.clone();
217 let algorithm = settings.algorithm.to_owned();
218
219 let outbound = OutboundGroupSession::new(
220 self.device_id.clone(),
221 self.identity_keys.clone(),
222 room_id,
223 settings,
224 )?;
225
226 let identity_keys = &self.identity_keys;
227
228 let sender_key = identity_keys.curve25519;
229 let signing_key = identity_keys.ed25519;
230 let shared_history = shared_history_from_history_visibility(&visibility);
231
232 let inbound = InboundGroupSession::new(
233 sender_key,
234 signing_key,
235 room_id,
236 &outbound.session_key().await,
237 own_sender_data,
238 algorithm,
239 Some(visibility),
240 shared_history,
241 )?;
242
243 Ok((outbound, inbound))
244 }
245
246 #[cfg(any(test, feature = "testing"))]
247 #[allow(dead_code)]
248 pub async fn create_group_session_pair_with_defaults(
251 &self,
252 room_id: &RoomId,
253 ) -> (OutboundGroupSession, InboundGroupSession) {
254 self.create_group_session_pair(
255 room_id,
256 EncryptionSettings::default(),
257 SenderData::unknown(),
258 )
259 .await
260 .expect("Can't create default group session pair")
261 }
262
263 pub fn signing_key_id(&self) -> OwnedDeviceKeyId {
265 DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, self.device_id())
266 }
267
268 pub fn has_signed_raw(
278 &self,
279 signatures: &crate::types::Signatures,
280 canonical_json: &str,
281 ) -> Result<(), SignatureError> {
282 use crate::olm::utility::VerifyJson;
283
284 let signing_key = self.identity_keys.ed25519;
285
286 signing_key.verify_canonicalized_json(
287 &self.user_id,
288 &DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, self.device_id()),
289 signatures,
290 canonical_json,
291 )
292 }
293
294 pub fn unsigned_device_keys(&self) -> DeviceKeys {
296 let identity_keys = self.identity_keys();
297 let keys = BTreeMap::from([
298 (
299 DeviceKeyId::from_parts(DeviceKeyAlgorithm::Curve25519, &self.device_id),
300 identity_keys.curve25519.into(),
301 ),
302 (
303 DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, &self.device_id),
304 identity_keys.ed25519.into(),
305 ),
306 ]);
307
308 let mut ret = DeviceKeys::new(
309 (*self.user_id).to_owned(),
310 (*self.device_id).to_owned(),
311 Self::ALGORITHMS.iter().map(|a| (**a).clone()).collect(),
312 keys,
313 Default::default(),
314 );
315 if self.dehydrated {
316 ret.dehydrated = JsOption::Some(true);
317 }
318 ret
319 }
320
321 pub fn user_id(&self) -> &UserId {
323 &self.user_id
324 }
325
326 pub fn device_id(&self) -> &DeviceId {
328 &self.device_id
329 }
330
331 pub fn identity_keys(&self) -> IdentityKeys {
333 *self.identity_keys
334 }
335
336 pub fn creation_local_time(&self) -> MilliSecondsSinceUnixEpoch {
338 self.creation_local_time
339 }
340}
341
342pub struct Account {
347 pub(crate) static_data: StaticAccountData,
348 inner: Box<InnerAccount>,
350 shared: bool,
353 uploaded_signed_key_count: u64,
358 fallback_creation_timestamp: Option<MilliSecondsSinceUnixEpoch>,
366}
367
368impl Deref for Account {
369 type Target = StaticAccountData;
370
371 fn deref(&self) -> &Self::Target {
372 &self.static_data
373 }
374}
375
376#[derive(Serialize, Deserialize)]
381#[allow(missing_debug_implementations)]
382pub struct PickledAccount {
383 pub user_id: OwnedUserId,
385 pub device_id: OwnedDeviceId,
387 pub pickle: AccountPickle,
389 pub shared: bool,
391 #[serde(default)]
393 pub dehydrated: bool,
394 pub uploaded_signed_key_count: u64,
396 #[serde(default = "default_account_creation_time")]
399 pub creation_local_time: MilliSecondsSinceUnixEpoch,
400 #[serde(default)]
402 pub fallback_key_creation_timestamp: Option<MilliSecondsSinceUnixEpoch>,
403}
404
405fn default_account_creation_time() -> MilliSecondsSinceUnixEpoch {
406 MilliSecondsSinceUnixEpoch(UInt::default())
407}
408
409#[cfg(not(tarpaulin_include))]
410impl fmt::Debug for Account {
411 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
412 f.debug_struct("Account")
413 .field("identity_keys", &self.identity_keys())
414 .field("shared", &self.shared())
415 .finish()
416 }
417}
418
419pub type OneTimeKeys = BTreeMap<OwnedOneTimeKeyId, Raw<ruma::encryption::OneTimeKey>>;
420pub type FallbackKeys = OneTimeKeys;
421
422impl Account {
423 pub(crate) fn new_helper(
424 mut account: InnerAccount,
425 user_id: &UserId,
426 device_id: &DeviceId,
427 ) -> Self {
428 let identity_keys = account.identity_keys();
429
430 account.generate_one_time_keys(account.max_number_of_one_time_keys());
442
443 Self {
444 static_data: StaticAccountData {
445 user_id: user_id.into(),
446 device_id: device_id.into(),
447 identity_keys: Arc::new(identity_keys),
448 dehydrated: false,
449 creation_local_time: MilliSecondsSinceUnixEpoch::now(),
450 },
451 inner: Box::new(account),
452 shared: false,
453 uploaded_signed_key_count: 0,
454 fallback_creation_timestamp: None,
455 }
456 }
457
458 pub fn with_device_id(user_id: &UserId, device_id: &DeviceId) -> Self {
460 let account = InnerAccount::new();
461
462 Self::new_helper(account, user_id, device_id)
463 }
464
465 pub fn new(user_id: &UserId) -> Self {
468 let account = InnerAccount::new();
469 let device_id: OwnedDeviceId =
470 base64_encode(account.identity_keys().curve25519.as_bytes()).into();
471
472 Self::new_helper(account, user_id, &device_id)
473 }
474
475 pub fn new_dehydrated(user_id: &UserId) -> Self {
477 let account = InnerAccount::new();
478 let device_id: OwnedDeviceId =
479 base64_encode(account.identity_keys().curve25519.as_bytes()).into();
480
481 let mut ret = Self::new_helper(account, user_id, &device_id);
482 ret.static_data.dehydrated = true;
483 ret
484 }
485
486 pub fn static_data(&self) -> &StaticAccountData {
488 &self.static_data
489 }
490
491 pub fn update_uploaded_key_count(&mut self, new_count: u64) {
497 self.uploaded_signed_key_count = new_count;
498 }
499
500 pub fn uploaded_key_count(&self) -> u64 {
502 self.uploaded_signed_key_count
503 }
504
505 pub fn shared(&self) -> bool {
507 self.shared
508 }
509
510 pub fn mark_as_shared(&mut self) {
515 self.shared = true;
516 }
517
518 pub fn one_time_keys(&self) -> HashMap<KeyId, Curve25519PublicKey> {
522 self.inner.one_time_keys()
523 }
524
525 pub fn generate_one_time_keys(&mut self, count: usize) -> OneTimeKeyGenerationResult {
527 self.inner.generate_one_time_keys(count)
528 }
529
530 pub fn max_one_time_keys(&self) -> usize {
532 self.inner.max_number_of_one_time_keys()
533 }
534
535 pub(crate) fn update_key_counts(
536 &mut self,
537 one_time_key_counts: &BTreeMap<OneTimeKeyAlgorithm, UInt>,
538 unused_fallback_keys: Option<&[OneTimeKeyAlgorithm]>,
539 ) {
540 if let Some(count) = one_time_key_counts.get(&OneTimeKeyAlgorithm::SignedCurve25519) {
541 let count: u64 = (*count).into();
542 let old_count = self.uploaded_key_count();
543
544 if count != old_count {
548 debug!(
549 "Updated uploaded one-time key count {} -> {count}.",
550 self.uploaded_key_count(),
551 );
552 }
553
554 self.update_uploaded_key_count(count);
555 self.generate_one_time_keys_if_needed();
556 }
557
558 if unused_fallback_keys.is_some() || self.fallback_creation_timestamp.is_some() {
562 self.generate_fallback_key_if_needed();
563 }
564 }
565
566 #[instrument(skip_all)]
575 pub fn generate_one_time_keys_if_needed(&mut self) -> Option<u64> {
576 if !self.one_time_keys().is_empty() {
580 return Some(0);
581 }
582
583 let count = self.uploaded_key_count();
584 let max_keys = self.max_one_time_keys();
585
586 if count >= max_keys as u64 {
587 return None;
588 }
589
590 let key_count = (max_keys as u64) - count;
591 let key_count: usize = key_count.try_into().unwrap_or(max_keys);
592
593 let result = self.generate_one_time_keys(key_count);
594
595 debug!(
596 count = key_count,
597 discarded_keys = ?result.removed,
598 created_keys = ?result.created,
599 "Generated new one-time keys"
600 );
601
602 Some(key_count as u64)
603 }
604
605 pub(crate) fn generate_fallback_key_if_needed(&mut self) {
612 if self.inner.fallback_key().is_empty() && self.fallback_key_expired() {
613 let removed_fallback_key = self.inner.generate_fallback_key();
614 self.fallback_creation_timestamp = Some(MilliSecondsSinceUnixEpoch::now());
615
616 debug!(
617 ?removed_fallback_key,
618 "The fallback key either expired or we didn't have one: generated a new fallback key.",
619 );
620 }
621 }
622
623 fn fallback_key_expired(&self) -> bool {
631 const FALLBACK_KEY_MAX_AGE: Duration = Duration::from_secs(3600 * 24 * 7);
632
633 if let Some(time) = self.fallback_creation_timestamp {
634 let Some(system_time) = time.to_system_time() else {
638 return true;
639 };
640
641 let Ok(elapsed) = system_time.elapsed() else {
645 return true;
646 };
647
648 elapsed > FALLBACK_KEY_MAX_AGE
653 } else {
654 true
657 }
658 }
659
660 fn fallback_key(&self) -> HashMap<KeyId, Curve25519PublicKey> {
661 self.inner.fallback_key()
662 }
663
664 pub fn keys_for_upload(&self) -> (Option<DeviceKeys>, OneTimeKeys, FallbackKeys) {
670 let device_keys = self.shared().not().then(|| self.device_keys());
671
672 let one_time_keys = self.signed_one_time_keys();
673 let fallback_keys = self.signed_fallback_keys();
674
675 (device_keys, one_time_keys, fallback_keys)
676 }
677
678 pub fn mark_keys_as_published(&mut self) {
680 self.inner.mark_keys_as_published();
681 }
682
683 pub fn sign(&self, string: &str) -> Ed25519Signature {
687 self.inner.sign(string)
688 }
689
690 pub fn pickle(&self) -> PickledAccount {
692 let pickle = self.inner.pickle();
693
694 PickledAccount {
695 user_id: self.user_id().to_owned(),
696 device_id: self.device_id().to_owned(),
697 pickle,
698 shared: self.shared(),
699 dehydrated: self.static_data.dehydrated,
700 uploaded_signed_key_count: self.uploaded_key_count(),
701 creation_local_time: self.static_data.creation_local_time,
702 fallback_key_creation_timestamp: self.fallback_creation_timestamp,
703 }
704 }
705
706 pub(crate) fn dehydrate(&self, pickle_key: &[u8; 32]) -> Raw<DehydratedDeviceData> {
707 let dehydration_result = self
708 .inner
709 .to_dehydrated_device(pickle_key)
710 .expect("We should be able to convert a freshly created Account into a libolm pickle");
711
712 let data = DehydratedDeviceData::V2(DehydratedDeviceV2::new(
713 dehydration_result.ciphertext,
714 dehydration_result.nonce,
715 ));
716 Raw::from_json(to_raw_value(&data).expect("Couldn't serialize our dehydrated device data"))
717 }
718
719 pub(crate) fn rehydrate(
720 pickle_key: &[u8; 32],
721 user_id: &UserId,
722 device_id: &DeviceId,
723 device_data: Raw<DehydratedDeviceData>,
724 ) -> Result<Self, DehydrationError> {
725 let data = device_data.deserialize()?;
726
727 match data {
728 DehydratedDeviceData::V1(d) => {
729 let pickle_key = expand_legacy_pickle_key(pickle_key, device_id);
730 let account =
731 InnerAccount::from_libolm_pickle(&d.device_pickle, pickle_key.as_ref())?;
732 Ok(Self::new_helper(account, user_id, device_id))
733 }
734 DehydratedDeviceData::V2(d) => {
735 let account =
736 InnerAccount::from_dehydrated_device(&d.device_pickle, &d.nonce, pickle_key)?;
737 Ok(Self::new_helper(account, user_id, device_id))
738 }
739 _ => Err(DehydrationError::Json(serde_json::Error::custom(format!(
740 "Unsupported dehydrated device algorithm {:?}",
741 data.algorithm()
742 )))),
743 }
744 }
745
746 #[cfg(test)]
749 pub(crate) fn legacy_dehydrate(&self, pickle_key: &[u8; 32]) -> Raw<DehydratedDeviceData> {
750 let pickle_key = expand_legacy_pickle_key(pickle_key, &self.device_id);
751 let device_pickle = self
752 .inner
753 .to_libolm_pickle(pickle_key.as_ref())
754 .expect("We should be able to convert a freshly created Account into a libolm pickle");
755
756 let data = DehydratedDeviceData::V1(DehydratedDeviceV1::new(device_pickle));
757 Raw::from_json(to_raw_value(&data).expect("Couldn't serialize our dehydrated device data"))
758 }
759
760 pub fn from_pickle(pickle: PickledAccount) -> Result<Self, PickleError> {
769 let account: vodozemac::olm::Account = pickle.pickle.into();
770 let identity_keys = account.identity_keys();
771
772 Ok(Self {
773 static_data: StaticAccountData {
774 user_id: (*pickle.user_id).into(),
775 device_id: (*pickle.device_id).into(),
776 identity_keys: Arc::new(identity_keys),
777 dehydrated: pickle.dehydrated,
778 creation_local_time: pickle.creation_local_time,
779 },
780 inner: Box::new(account),
781 shared: pickle.shared,
782 uploaded_signed_key_count: pickle.uploaded_signed_key_count,
783 fallback_creation_timestamp: pickle.fallback_key_creation_timestamp,
784 })
785 }
786
787 pub fn device_keys(&self) -> DeviceKeys {
790 let mut device_keys = self.unsigned_device_keys();
791
792 let json_device_keys =
795 serde_json::to_value(&device_keys).expect("device key is always safe to serialize");
796 let signature = self
797 .sign_json(json_device_keys)
798 .expect("Newly created device keys can always be signed");
799
800 device_keys.signatures.add_signature(
801 self.user_id().to_owned(),
802 DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, &self.static_data.device_id),
803 signature,
804 );
805
806 device_keys
807 }
808
809 pub async fn bootstrap_cross_signing(
821 &self,
822 ) -> (PrivateCrossSigningIdentity, UploadSigningKeysRequest, SignatureUploadRequest) {
823 let identity = PrivateCrossSigningIdentity::for_account(self);
824
825 let signature_request = identity
826 .sign_account(self.static_data())
827 .await
828 .expect("Can't sign own device with new cross signing keys");
829
830 let upload_request = identity.as_upload_request().await;
831
832 (identity, upload_request, signature_request)
833 }
834
835 pub fn sign_cross_signing_key(
837 &self,
838 cross_signing_key: &mut CrossSigningKey,
839 ) -> Result<(), SignatureError> {
840 #[allow(clippy::needless_borrows_for_generic_args)]
841 let signature = self.sign_json(serde_json::to_value(&cross_signing_key)?)?;
843
844 cross_signing_key.signatures.add_signature(
845 self.user_id().to_owned(),
846 DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, self.device_id()),
847 signature,
848 );
849
850 Ok(())
851 }
852
853 pub fn sign_master_key(
855 &self,
856 master_key: &MasterPubkey,
857 ) -> Result<SignatureUploadRequest, SignatureError> {
858 let public_key =
859 master_key.get_first_key().ok_or(SignatureError::MissingSigningKey)?.to_base64().into();
860
861 let mut cross_signing_key: CrossSigningKey = master_key.as_ref().clone();
862 cross_signing_key.signatures.clear();
863 self.sign_cross_signing_key(&mut cross_signing_key)?;
864
865 let mut user_signed_keys = SignedKeys::new();
866 user_signed_keys.add_cross_signing_keys(public_key, cross_signing_key.to_raw());
867
868 let signed_keys = [(self.user_id().to_owned(), user_signed_keys)].into();
869 Ok(SignatureUploadRequest::new(signed_keys))
870 }
871
872 pub fn sign_json(&self, json: Value) -> Result<Ed25519Signature, SignatureError> {
880 self.inner.sign_json(json)
881 }
882
883 pub fn signed_one_time_keys(&self) -> OneTimeKeys {
887 let one_time_keys = self.one_time_keys();
888
889 if one_time_keys.is_empty() {
890 BTreeMap::new()
891 } else {
892 self.signed_keys(one_time_keys, false)
893 }
894 }
895
896 pub fn signed_fallback_keys(&self) -> FallbackKeys {
900 let fallback_key = self.fallback_key();
901
902 if fallback_key.is_empty() {
903 BTreeMap::new()
904 } else {
905 self.signed_keys(fallback_key, true)
906 }
907 }
908
909 fn signed_keys(
910 &self,
911 keys: HashMap<KeyId, Curve25519PublicKey>,
912 fallback: bool,
913 ) -> OneTimeKeys {
914 let mut keys_map = BTreeMap::new();
915
916 for (key_id, key) in keys {
917 let signed_key = self.sign_key(key, fallback);
918
919 keys_map.insert(
920 OneTimeKeyId::from_parts(
921 OneTimeKeyAlgorithm::SignedCurve25519,
922 key_id.to_base64().as_str().into(),
923 ),
924 signed_key.into_raw(),
925 );
926 }
927
928 keys_map
929 }
930
931 fn sign_key(&self, key: Curve25519PublicKey, fallback: bool) -> SignedKey {
932 let mut key = if fallback {
933 SignedKey::new_fallback(key.to_owned())
934 } else {
935 SignedKey::new(key.to_owned())
936 };
937
938 let signature = self
939 .sign_json(serde_json::to_value(&key).expect("Can't serialize a signed key"))
940 .expect("Newly created one-time keys can always be signed");
941
942 key.signatures_mut().add_signature(
943 self.user_id().to_owned(),
944 DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, self.device_id()),
945 signature,
946 );
947
948 key
949 }
950
951 pub fn create_outbound_session_helper(
971 &self,
972 config: SessionConfig,
973 identity_key: Curve25519PublicKey,
974 one_time_key: Curve25519PublicKey,
975 fallback_used: bool,
976 our_device_keys: DeviceKeys,
977 ) -> Session {
978 let session = self.inner.create_outbound_session(config, identity_key, one_time_key);
979
980 let now = SecondsSinceUnixEpoch::now();
981 let session_id = session.session_id();
982
983 Session {
984 inner: Arc::new(Mutex::new(session)),
985 session_id: session_id.into(),
986 sender_key: identity_key,
987 our_device_keys,
988 created_using_fallback_key: fallback_used,
989 creation_time: now,
990 last_use_time: now,
991 }
992 }
993
994 #[instrument(
995 skip_all,
996 fields(
997 user_id = ?device.user_id(),
998 device_id = ?device.device_id(),
999 algorithms = ?device.algorithms()
1000 )
1001 )]
1002 fn find_pre_key_bundle(
1003 device: &DeviceData,
1004 key_map: &OneTimeKeys,
1005 ) -> Result<PrekeyBundle, SessionCreationError> {
1006 let mut keys = key_map.iter();
1007
1008 let first_key = keys.next().ok_or_else(|| {
1009 SessionCreationError::OneTimeKeyMissing(
1010 device.user_id().to_owned(),
1011 device.device_id().into(),
1012 )
1013 })?;
1014
1015 let first_key_id = first_key.0.to_owned();
1016 let first_key = OneTimeKey::deserialize(first_key_id.algorithm(), first_key.1)?;
1017
1018 let result = match first_key {
1019 OneTimeKey::SignedKey(key) => Ok(PrekeyBundle::Olm3DH { key }),
1020 };
1021
1022 trace!(?result, "Finished searching for a valid pre-key bundle");
1023
1024 result
1025 }
1026
1027 #[allow(clippy::result_large_err)]
1042 pub fn create_outbound_session(
1043 &self,
1044 device: &DeviceData,
1045 key_map: &OneTimeKeys,
1046 our_device_keys: DeviceKeys,
1047 ) -> Result<Session, SessionCreationError> {
1048 let pre_key_bundle = Self::find_pre_key_bundle(device, key_map)?;
1049
1050 match pre_key_bundle {
1051 PrekeyBundle::Olm3DH { key } => {
1052 device.verify_one_time_key(&key).map_err(|error| {
1053 SessionCreationError::InvalidSignature {
1054 signing_key: device.ed25519_key().map(Box::new),
1055 one_time_key: key.clone().into(),
1056 error: error.into(),
1057 }
1058 })?;
1059
1060 let identity_key = device.curve25519_key().ok_or_else(|| {
1061 SessionCreationError::DeviceMissingCurveKey(
1062 device.user_id().to_owned(),
1063 device.device_id().into(),
1064 )
1065 })?;
1066
1067 let is_fallback = key.fallback();
1068 let one_time_key = key.key();
1069 let config = device.olm_session_config();
1070
1071 Ok(self.create_outbound_session_helper(
1072 config,
1073 identity_key,
1074 one_time_key,
1075 is_fallback,
1076 our_device_keys,
1077 ))
1078 }
1079 }
1080 }
1081
1082 pub fn create_inbound_session(
1097 &mut self,
1098 their_identity_key: Curve25519PublicKey,
1099 our_device_keys: DeviceKeys,
1100 message: &PreKeyMessage,
1101 ) -> Result<InboundCreationResult, SessionCreationError> {
1102 Span::current().record("session_id", debug(message.session_id()));
1103 trace!("Creating a new Olm session from a pre-key message");
1104
1105 let result = self.inner.create_inbound_session(their_identity_key, message)?;
1106 let now = SecondsSinceUnixEpoch::now();
1107 let session_id = result.session.session_id();
1108
1109 debug!(session=?result.session, "Decrypted an Olm message from a new Olm session");
1110
1111 let session = Session {
1112 inner: Arc::new(Mutex::new(result.session)),
1113 session_id: session_id.into(),
1114 sender_key: their_identity_key,
1115 our_device_keys,
1116 created_using_fallback_key: false,
1117 creation_time: now,
1118 last_use_time: now,
1119 };
1120
1121 let plaintext = String::from_utf8_lossy(&result.plaintext).to_string();
1122
1123 Ok(InboundCreationResult { session, plaintext })
1124 }
1125
1126 #[cfg(any(test, feature = "testing"))]
1127 #[allow(dead_code)]
1128 pub async fn create_session_for_test_helper(
1130 &mut self,
1131 other: &mut Account,
1132 ) -> (Session, Session) {
1133 use ruma::events::dummy::ToDeviceDummyEventContent;
1134
1135 other.generate_one_time_keys(1);
1136 let one_time_map = other.signed_one_time_keys();
1137 let device = DeviceData::from_account(other);
1138
1139 let mut our_session =
1140 self.create_outbound_session(&device, &one_time_map, self.device_keys()).unwrap();
1141
1142 other.mark_keys_as_published();
1143
1144 let message = our_session
1145 .encrypt(&device, "m.dummy", ToDeviceDummyEventContent::new(), None)
1146 .await
1147 .unwrap()
1148 .deserialize()
1149 .unwrap();
1150
1151 #[cfg(feature = "experimental-algorithms")]
1152 let content = if let ToDeviceEncryptedEventContent::OlmV2Curve25519AesSha2(c) = message {
1153 c
1154 } else {
1155 panic!("Invalid encrypted event algorithm {}", message.algorithm());
1156 };
1157
1158 #[cfg(not(feature = "experimental-algorithms"))]
1159 let ToDeviceEncryptedEventContent::OlmV1Curve25519AesSha2(content) = message
1160 else {
1161 panic!("Invalid encrypted event algorithm {}", message.algorithm());
1162 };
1163
1164 let OlmMessage::PreKey(prekey) = content.ciphertext else {
1165 panic!("Wrong Olm message type");
1166 };
1167
1168 let our_device = DeviceData::from_account(self);
1169 let other_session = other
1170 .create_inbound_session(
1171 our_device.curve25519_key().unwrap(),
1172 other.device_keys(),
1173 &prekey,
1174 )
1175 .unwrap();
1176
1177 (our_session, other_session.session)
1178 }
1179
1180 async fn decrypt_olm_helper(
1181 &mut self,
1182 store: &Store,
1183 sender: &UserId,
1184 sender_key: Curve25519PublicKey,
1185 ciphertext: &OlmMessage,
1186 decryption_settings: &DecryptionSettings,
1187 ) -> OlmResult<OlmDecryptionInfo> {
1188 let message_hash = OlmMessageHash::new(sender_key, ciphertext);
1189
1190 match self
1191 .decrypt_and_parse_olm_message(
1192 store,
1193 sender,
1194 sender_key,
1195 ciphertext,
1196 decryption_settings,
1197 )
1198 .await
1199 {
1200 Ok((session, result)) => {
1201 Ok(OlmDecryptionInfo { session, message_hash, result, inbound_group_session: None })
1202 }
1203 Err(OlmError::SessionWedged(user_id, sender_key)) => {
1204 if store.is_message_known(&message_hash).await? {
1205 info!(?sender_key, "An Olm message got replayed, decryption failed");
1206 Err(OlmError::ReplayedMessage(user_id, sender_key))
1207 } else {
1208 Err(OlmError::SessionWedged(user_id, sender_key))
1209 }
1210 }
1211 Err(e) => Err(e),
1212 }
1213 }
1214
1215 #[cfg(feature = "experimental-algorithms")]
1216 async fn decrypt_olm_v2(
1217 &mut self,
1218 store: &Store,
1219 sender: &UserId,
1220 content: &OlmV2Curve25519AesSha2Content,
1221 decryption_settings: &DecryptionSettings,
1222 ) -> OlmResult<OlmDecryptionInfo> {
1223 self.decrypt_olm_helper(
1224 store,
1225 sender,
1226 content.sender_key,
1227 &content.ciphertext,
1228 decryption_settings,
1229 )
1230 .await
1231 }
1232
1233 #[instrument(skip_all, fields(sender, sender_key = ?content.sender_key))]
1234 async fn decrypt_olm_v1(
1235 &mut self,
1236 store: &Store,
1237 sender: &UserId,
1238 content: &OlmV1Curve25519AesSha2Content,
1239 decryption_settings: &DecryptionSettings,
1240 ) -> OlmResult<OlmDecryptionInfo> {
1241 if content.recipient_key != self.static_data.identity_keys.curve25519 {
1242 warn!("Olm event doesn't contain a ciphertext for our key");
1243
1244 Err(EventError::MissingCiphertext.into())
1245 } else {
1246 Box::pin(self.decrypt_olm_helper(
1247 store,
1248 sender,
1249 content.sender_key,
1250 &content.ciphertext,
1251 decryption_settings,
1252 ))
1253 .await
1254 }
1255 }
1256
1257 #[instrument(skip_all, fields(algorithm = ?event.content.algorithm()))]
1258 pub(crate) async fn decrypt_to_device_event(
1259 &mut self,
1260 store: &Store,
1261 event: &EncryptedToDeviceEvent,
1262 decryption_settings: &DecryptionSettings,
1263 ) -> OlmResult<OlmDecryptionInfo> {
1264 trace!("Decrypting a to-device event");
1265
1266 match &event.content {
1267 ToDeviceEncryptedEventContent::OlmV1Curve25519AesSha2(c) => {
1268 self.decrypt_olm_v1(store, &event.sender, c, decryption_settings).await
1269 }
1270 #[cfg(feature = "experimental-algorithms")]
1271 ToDeviceEncryptedEventContent::OlmV2Curve25519AesSha2(c) => {
1272 self.decrypt_olm_v2(store, &event.sender, c, decryption_settings).await
1273 }
1274 ToDeviceEncryptedEventContent::Unknown(_) => {
1275 warn!(
1276 "Error decrypting an to-device event, unsupported \
1277 encryption algorithm"
1278 );
1279
1280 Err(EventError::UnsupportedAlgorithm.into())
1281 }
1282 }
1283 }
1284
1285 pub fn receive_keys_upload_response(
1287 &mut self,
1288 response: &upload_keys::v3::Response,
1289 ) -> OlmResult<()> {
1290 if !self.shared() {
1291 debug!("Marking account as shared");
1292 }
1293 self.mark_as_shared();
1294
1295 debug!("Marking one-time keys as published");
1296 self.mark_keys_as_published();
1299 self.update_key_counts(&response.one_time_key_counts, None);
1300
1301 Ok(())
1302 }
1303
1304 async fn decrypt_olm_message(
1306 &mut self,
1307 store: &Store,
1308 sender: &UserId,
1309 sender_key: Curve25519PublicKey,
1310 message: &OlmMessage,
1311 ) -> Result<(SessionType, String), OlmError> {
1312 let existing_sessions = store.get_sessions(&sender_key.to_base64()).await?;
1313
1314 match message {
1315 OlmMessage::Normal(_) => {
1316 let mut errors_by_olm_session = Vec::new();
1317
1318 if let Some(sessions) = existing_sessions {
1319 for session in sessions.lock().await.iter_mut() {
1322 match session.decrypt(message).await {
1323 Ok(p) => {
1324 return Ok((SessionType::Existing(session.clone()), p));
1326 }
1327
1328 Err(e) => {
1329 errors_by_olm_session.push((session.session_id().to_owned(), e));
1334 }
1335 }
1336 }
1337 }
1338
1339 warn!(
1340 ?errors_by_olm_session,
1341 "Failed to decrypt a non-pre-key message with all available sessions"
1342 );
1343 Err(OlmError::SessionWedged(sender.to_owned(), sender_key))
1344 }
1345
1346 OlmMessage::PreKey(prekey_message) => {
1347 if let Some(sessions) = existing_sessions {
1349 for session in sessions.lock().await.iter_mut() {
1350 if prekey_message.session_id() != session.session_id() {
1351 continue;
1353 }
1354
1355 if let Ok(p) = session.decrypt(message).await {
1356 return Ok((SessionType::Existing(session.clone()), p));
1358 }
1359
1360 warn!(
1374 session_id = session.session_id(),
1375 "Failed to decrypt a pre-key message with the corresponding session"
1376 );
1377
1378 return Err(OlmError::SessionWedged(
1379 session.our_device_keys.user_id.to_owned(),
1380 session.sender_key(),
1381 ));
1382 }
1383 }
1384
1385 let device_keys = store.get_own_device().await?.as_device_keys().clone();
1386 let result =
1387 match self.create_inbound_session(sender_key, device_keys, prekey_message) {
1388 Ok(r) => r,
1389 Err(e) => {
1390 warn!(
1391 "Failed to create a new Olm session from a pre-key message: {e:?}"
1392 );
1393 return Err(OlmError::SessionWedged(sender.to_owned(), sender_key));
1394 }
1395 };
1396
1397 let mut changes =
1402 Changes { sessions: vec![result.session.clone()], ..Default::default() };
1403
1404 if let Some(device) = store.get_device_from_curve_key(sender, sender_key).await? {
1411 let mut device_data = device.inner;
1412 device_data.olm_wedging_index.increment();
1413
1414 changes.devices =
1415 DeviceChanges { changed: vec![device_data], ..Default::default() };
1416 }
1417
1418 store.save_changes(changes).await?;
1419
1420 Ok((SessionType::New(result.session), result.plaintext))
1421 }
1422 }
1423 }
1424
1425 #[instrument(skip(self, store), fields(session, session_id))]
1428 async fn decrypt_and_parse_olm_message(
1429 &mut self,
1430 store: &Store,
1431 sender: &UserId,
1432 sender_key: Curve25519PublicKey,
1433 message: &OlmMessage,
1434 decryption_settings: &DecryptionSettings,
1435 ) -> OlmResult<(SessionType, DecryptionResult)> {
1436 let (session, plaintext) =
1437 self.decrypt_olm_message(store, sender, sender_key, message).await?;
1438
1439 trace!("Successfully decrypted an Olm message");
1440
1441 match self
1442 .parse_decrypted_to_device_event(
1443 store,
1444 sender,
1445 sender_key,
1446 plaintext,
1447 decryption_settings,
1448 )
1449 .await
1450 {
1451 Ok(result) => Ok((session, result)),
1452 Err(e) => {
1453 match session {
1457 SessionType::New(s) | SessionType::Existing(s) => {
1458 store.save_sessions(&[s]).await?;
1459 }
1460 }
1461
1462 warn!(
1463 error = ?e,
1464 "A to-device message was successfully decrypted but \
1465 parsing and checking the event fields failed"
1466 );
1467
1468 Err(e)
1469 }
1470 }
1471 }
1472
1473 async fn parse_decrypted_to_device_event(
1494 &self,
1495 store: &Store,
1496 sender: &UserId,
1497 sender_key: Curve25519PublicKey,
1498 plaintext: String,
1499 decryption_settings: &DecryptionSettings,
1500 ) -> OlmResult<DecryptionResult> {
1501 let event: Box<AnyDecryptedOlmEvent> = serde_json::from_str(&plaintext)?;
1502 let identity_keys = &self.static_data.identity_keys;
1503
1504 if event.recipient() != self.static_data.user_id {
1505 Err(EventError::MismatchedSender(
1506 event.recipient().to_owned(),
1507 self.static_data.user_id.clone(),
1508 )
1509 .into())
1510 }
1511 else if event.sender() != sender {
1514 Err(EventError::MismatchedSender(event.sender().to_owned(), sender.to_owned()).into())
1515 } else if identity_keys.ed25519 != event.recipient_keys().ed25519 {
1516 Err(EventError::MismatchedKeys(
1517 identity_keys.ed25519.into(),
1518 event.recipient_keys().ed25519.into(),
1519 )
1520 .into())
1521 } else {
1522 Self::check_sender_device_keys(event.as_ref(), sender_key)?;
1527 let mut sender_device: Option<Device> = None;
1528 if let AnyDecryptedOlmEvent::RoomKey(_) = event.as_ref() {
1529 } else if let AnyDecryptedOlmEvent::RoomKeyBundle(_) = event.as_ref() {
1534 event.sender_device_keys().ok_or(EventError::MissingSigningKey).inspect_err(
1541 |_| {
1542 warn!("The room key bundle was missing the sender device keys in the event")
1543 },
1544 )?;
1545 } else {
1546 let device = store
1547 .get_device_from_curve_key(event.sender(), sender_key)
1548 .await?
1549 .ok_or(EventError::MissingSigningKey)?;
1550
1551 let key = device.ed25519_key().ok_or(EventError::MissingSigningKey)?;
1552
1553 if key != event.keys().ed25519 {
1554 return Err(EventError::MismatchedKeys(
1555 key.into(),
1556 event.keys().ed25519.into(),
1557 )
1558 .into());
1559 }
1560
1561 sender_device = Some(device);
1566 }
1567
1568 let encryption_info = Self::get_olm_encryption_info(sender_key, sender, &sender_device);
1569
1570 let result = DecryptionResult {
1571 event,
1572 raw_event: Raw::from_json(RawJsonValue::from_string(plaintext)?),
1573 sender_key,
1574 encryption_info,
1575 };
1576
1577 if !self.is_from_verified_device_or_allowed_type(decryption_settings, &result) {
1579 Err(OlmError::UnverifiedSenderDevice)
1580 } else {
1581 Ok(result)
1583 }
1584 }
1585 }
1586
1587 fn is_from_verified_device_or_allowed_type(
1600 &self,
1601 decryption_settings: &DecryptionSettings,
1602 result: &DecryptionResult,
1603 ) -> bool {
1604 let event_type = result.event.event_type();
1605
1606 match event_type {
1618 "m.room_key"
1619 | "m.room_key.withheld"
1620 | "m.room_key_request"
1621 | "m.secret.request"
1622 | "m.key.verification.key"
1623 | "m.key.verification.mac"
1624 | "m.key.verification.done"
1625 | "m.key.verification.ready"
1626 | "m.key.verification.start"
1627 | "m.key.verification.accept"
1628 | "m.key.verification.cancel"
1629 | "m.key.verification.request" => {
1630 true
1633 }
1634 _ => {
1635 satisfies_sender_trust_requirement(
1638 &result.encryption_info,
1639 &decryption_settings.sender_device_trust_requirement,
1640 )
1641 }
1642 }
1643 }
1644
1645 fn get_olm_encryption_info(
1652 sender_key: Curve25519PublicKey,
1653 sender_id: &UserId,
1654 sender_device: &Option<Device>,
1655 ) -> EncryptionInfo {
1656 let verification_state = sender_device
1657 .as_ref()
1658 .map(|device| {
1659 if device.is_verified() {
1660 VerificationState::Verified
1662 } else if device.is_cross_signed_by_owner() {
1663 if device
1665 .device_owner_identity
1666 .as_ref()
1667 .expect("A device cross-signed by the owner must have an owner identity")
1668 .was_previously_verified()
1669 {
1670 VerificationState::Unverified(VerificationLevel::VerificationViolation)
1671 } else {
1672 VerificationState::Unverified(VerificationLevel::UnverifiedIdentity)
1673 }
1674 } else {
1675 VerificationState::Unverified(VerificationLevel::UnsignedDevice)
1677 }
1678 })
1679 .unwrap_or(VerificationState::Unverified(VerificationLevel::None(
1680 DeviceLinkProblem::MissingDevice,
1681 )));
1682
1683 let encryption_info = EncryptionInfo {
1684 sender: sender_id.to_owned(),
1685 sender_device: sender_device.as_ref().map(|d| d.device_id().to_owned()),
1686 algorithm_info: AlgorithmInfo::OlmV1Curve25519AesSha2 {
1687 curve25519_public_key_base64: sender_key.to_base64(),
1688 },
1689 verification_state,
1690 };
1691 encryption_info
1692 }
1693
1694 fn check_sender_device_keys(
1704 event: &AnyDecryptedOlmEvent,
1705 sender_key: Curve25519PublicKey,
1706 ) -> OlmResult<()> {
1707 let Some(sender_device_keys) = event.sender_device_keys() else {
1708 return Ok(());
1709 };
1710
1711 let sender_device_data = DeviceData::try_from(sender_device_keys).map_err(|err| {
1713 warn!(
1714 "Received a to-device message with sender_device_keys with \
1715 invalid signature: {err:?}",
1716 );
1717 OlmError::EventError(EventError::InvalidSenderDeviceKeys)
1718 })?;
1719
1720 if sender_device_data.ed25519_key() != Some(event.keys().ed25519) {
1723 warn!(
1724 "Received a to-device message with sender_device_keys with incorrect \
1725 ed25519 key: expected {:?}, got {:?}",
1726 event.keys().ed25519,
1727 sender_device_data.ed25519_key(),
1728 );
1729 return Err(OlmError::EventError(EventError::InvalidSenderDeviceKeys));
1730 }
1731
1732 if sender_device_data.curve25519_key() != Some(sender_key) {
1735 warn!(
1736 "Received a to-device message with sender_device_keys with incorrect \
1737 curve25519 key: expected {sender_key:?}, got {:?}",
1738 sender_device_data.curve25519_key(),
1739 );
1740 return Err(OlmError::EventError(EventError::InvalidSenderDeviceKeys));
1741 }
1742
1743 Ok(())
1744 }
1745
1746 #[doc(hidden)]
1751 pub fn deep_clone(&self) -> Self {
1752 Self::from_pickle(self.pickle()).unwrap()
1754 }
1755}
1756
1757impl PartialEq for Account {
1758 fn eq(&self, other: &Self) -> bool {
1759 self.identity_keys() == other.identity_keys() && self.shared() == other.shared()
1760 }
1761}
1762
1763pub(crate) fn shared_history_from_history_visibility(
1783 history_visibility: &HistoryVisibility,
1784) -> bool {
1785 match history_visibility {
1786 HistoryVisibility::Shared | HistoryVisibility::WorldReadable => true,
1787 HistoryVisibility::Invited | HistoryVisibility::Joined | _ => false,
1788 }
1789}
1790
1791fn expand_legacy_pickle_key(key: &[u8; 32], device_id: &DeviceId) -> Box<[u8; 32]> {
1810 let kdf: Hkdf<Sha256> = Hkdf::new(Some(device_id.as_bytes()), key);
1811 let mut key = Box::new([0u8; 32]);
1812
1813 kdf.expand(b"dehydrated-device-pickle-key", key.as_mut_slice())
1814 .expect("We should be able to expand the 32 byte pickle key");
1815
1816 key
1817}
1818
1819fn satisfies_sender_trust_requirement(
1822 encryption_info: &EncryptionInfo,
1823 trust_requirement: &TrustRequirement,
1824) -> bool {
1825 trace!(
1826 verification_state = ?encryption_info.verification_state,
1827 ?trust_requirement, "check_to_device_sender_trust_requirement",
1828 );
1829
1830 match (&encryption_info.verification_state, trust_requirement) {
1831 (_, TrustRequirement::Untrusted) => true,
1833
1834 (VerificationState::Verified, _) => true,
1836
1837 (
1840 VerificationState::Unverified(verification_level),
1841 TrustRequirement::CrossSignedOrLegacy | TrustRequirement::CrossSigned,
1842 ) => match verification_level {
1843 VerificationLevel::UnverifiedIdentity => true,
1845
1846 VerificationLevel::UnsignedDevice
1849 | VerificationLevel::None(_)
1850 | VerificationLevel::VerificationViolation
1851 | VerificationLevel::MismatchedSender => false,
1852 },
1853 }
1854}
1855
1856#[cfg(test)]
1857mod tests {
1858 use std::{
1859 collections::{BTreeMap, BTreeSet},
1860 ops::Deref,
1861 time::Duration,
1862 };
1863
1864 use anyhow::Result;
1865 use matrix_sdk_test::async_test;
1866 use ruma::{
1867 device_id, events::room::history_visibility::HistoryVisibility, room_id, user_id, DeviceId,
1868 MilliSecondsSinceUnixEpoch, OneTimeKeyAlgorithm, OneTimeKeyId, UserId,
1869 };
1870 use serde_json::json;
1871
1872 use super::Account;
1873 use crate::{
1874 olm::{account::shared_history_from_history_visibility, SignedJsonObject},
1875 types::{DeviceKeys, SignedKey},
1876 DeviceData, EncryptionSettings,
1877 };
1878
1879 fn user_id() -> &'static UserId {
1880 user_id!("@alice:localhost")
1881 }
1882
1883 fn device_id() -> &'static DeviceId {
1884 device_id!("DEVICEID")
1885 }
1886
1887 #[test]
1888 fn test_one_time_key_creation() -> Result<()> {
1889 let mut account = Account::with_device_id(user_id(), device_id());
1890
1891 let (_, one_time_keys, _) = account.keys_for_upload();
1892 assert!(!one_time_keys.is_empty());
1893
1894 let (_, second_one_time_keys, _) = account.keys_for_upload();
1895 assert!(!second_one_time_keys.is_empty());
1896
1897 let one_time_key_ids: BTreeSet<&OneTimeKeyId> =
1898 one_time_keys.keys().map(Deref::deref).collect();
1899 let second_one_time_key_ids: BTreeSet<&OneTimeKeyId> =
1900 second_one_time_keys.keys().map(Deref::deref).collect();
1901
1902 assert_eq!(one_time_key_ids, second_one_time_key_ids);
1903
1904 account.mark_keys_as_published();
1905 account.update_uploaded_key_count(50);
1906 account.generate_one_time_keys_if_needed();
1907
1908 let (_, third_one_time_keys, _) = account.keys_for_upload();
1909 assert!(third_one_time_keys.is_empty());
1910
1911 account.update_uploaded_key_count(0);
1912 account.generate_one_time_keys_if_needed();
1913
1914 let (_, fourth_one_time_keys, _) = account.keys_for_upload();
1915 assert!(!fourth_one_time_keys.is_empty());
1916
1917 let fourth_one_time_key_ids: BTreeSet<&OneTimeKeyId> =
1918 fourth_one_time_keys.keys().map(Deref::deref).collect();
1919
1920 assert_ne!(one_time_key_ids, fourth_one_time_key_ids);
1921 Ok(())
1922 }
1923
1924 #[test]
1925 fn test_fallback_key_creation() -> Result<()> {
1926 let mut account = Account::with_device_id(user_id(), device_id());
1927
1928 let (_, _, fallback_keys) = account.keys_for_upload();
1929
1930 assert!(
1934 fallback_keys.is_empty(),
1935 "We should not upload fallback keys until we know if the server supports them."
1936 );
1937
1938 let one_time_keys = BTreeMap::from([(OneTimeKeyAlgorithm::SignedCurve25519, 50u8.into())]);
1939
1940 account.update_key_counts(&one_time_keys, None);
1943 let (_, _, fallback_keys) = account.keys_for_upload();
1944 assert!(
1945 fallback_keys.is_empty(),
1946 "We should not upload a fallback key if we're certain that the server doesn't support \
1947 them."
1948 );
1949
1950 let unused_fallback_keys = &[];
1954 account.update_key_counts(&one_time_keys, Some(unused_fallback_keys.as_ref()));
1955 let (_, _, fallback_keys) = account.keys_for_upload();
1956 assert!(
1957 !fallback_keys.is_empty(),
1958 "We should upload the initial fallback key if the server supports them."
1959 );
1960 account.mark_keys_as_published();
1961
1962 let unused_fallback_keys = &[];
1965 account.update_key_counts(&one_time_keys, Some(unused_fallback_keys.as_ref()));
1966 let (_, _, fallback_keys) = account.keys_for_upload();
1967 assert!(
1968 fallback_keys.is_empty(),
1969 "We should not upload new fallback keys unless our current fallback key expires."
1970 );
1971
1972 let fallback_key_timestamp =
1973 account.fallback_creation_timestamp.unwrap().to_system_time().unwrap()
1974 - Duration::from_secs(3600 * 24 * 30);
1975
1976 account.fallback_creation_timestamp =
1977 Some(MilliSecondsSinceUnixEpoch::from_system_time(fallback_key_timestamp).unwrap());
1978
1979 account.update_key_counts(&one_time_keys, None);
1980 let (_, _, fallback_keys) = account.keys_for_upload();
1981 assert!(
1982 !fallback_keys.is_empty(),
1983 "Now that our fallback key has expired, we should try to upload a new one, even if the \
1984 server supposedly doesn't support fallback keys anymore"
1985 );
1986
1987 Ok(())
1988 }
1989
1990 #[test]
1991 fn test_fallback_key_signing() -> Result<()> {
1992 let key = vodozemac::Curve25519PublicKey::from_base64(
1993 "7PUPP6Ijt5R8qLwK2c8uK5hqCNF9tOzWYgGaAay5JBs",
1994 )?;
1995 let account = Account::with_device_id(user_id(), device_id());
1996
1997 let key = account.sign_key(key, true);
1998
1999 let canonical_key = key.to_canonical_json()?;
2000
2001 assert_eq!(
2002 canonical_key,
2003 "{\"fallback\":true,\"key\":\"7PUPP6Ijt5R8qLwK2c8uK5hqCNF9tOzWYgGaAay5JBs\"}"
2004 );
2005
2006 account
2007 .has_signed_raw(key.signatures(), &canonical_key)
2008 .expect("Couldn't verify signature");
2009
2010 let device = DeviceData::from_account(&account);
2011 device.verify_one_time_key(&key).expect("The device can verify its own signature");
2012
2013 Ok(())
2014 }
2015
2016 #[test]
2017 fn test_account_and_device_creation_timestamp() -> Result<()> {
2018 let now = MilliSecondsSinceUnixEpoch::now();
2019 let account = Account::with_device_id(user_id(), device_id());
2020 let then = MilliSecondsSinceUnixEpoch::now();
2021
2022 assert!(account.creation_local_time() >= now);
2023 assert!(account.creation_local_time() <= then);
2024
2025 let device = DeviceData::from_account(&account);
2026 assert_eq!(account.creation_local_time(), device.first_time_seen_ts());
2027
2028 Ok(())
2029 }
2030
2031 #[async_test]
2032 async fn test_fallback_key_signature_verification() -> Result<()> {
2033 let fallback_key = json!({
2034 "fallback": true,
2035 "key": "XPFqtLvBepBmW6jSAbBuJbhEpprBhQOX1IjUu+cnMF4",
2036 "signatures": {
2037 "@dkasak_c:matrix.org": {
2038 "ed25519:EXPDYDPWZH": "RJCBMJPL5hvjxgq8rmLmqkNOuPsaan7JeL1wsE+gW6R39G894lb2sBmzapHeKCn/KFjmkonPLkICApRDS+zyDw"
2039 }
2040 }
2041 });
2042
2043 let device_keys = json!({
2044 "algorithms": [
2045 "m.olm.v1.curve25519-aes-sha2",
2046 "m.megolm.v1.aes-sha2"
2047 ],
2048 "device_id": "EXPDYDPWZH",
2049 "keys": {
2050 "curve25519:EXPDYDPWZH": "k7f3igo0Vrdm88JSSA5d3OCuUfHYELChB2b57aOROB8",
2051 "ed25519:EXPDYDPWZH": "GdjYI8fxs175gSpYRJkyN6FRfvcyTsNOhJ2OR/Ggp+E"
2052 },
2053 "signatures": {
2054 "@dkasak_c:matrix.org": {
2055 "ed25519:EXPDYDPWZH": "kzrtfQMbJXWXQ1uzhybtwFnGk0JJBS4Mg8VPMusMu6U8MPJccwoHVZKo5+owuHTzIodI+GZYqLmMSzvfvsChAA"
2056 }
2057 },
2058 "user_id": "@dkasak_c:matrix.org",
2059 "unsigned": {}
2060 });
2061
2062 let device_keys: DeviceKeys = serde_json::from_value(device_keys).unwrap();
2063 let device = DeviceData::try_from(&device_keys).unwrap();
2064 let fallback_key: SignedKey = serde_json::from_value(fallback_key).unwrap();
2065
2066 device
2067 .verify_one_time_key(&fallback_key)
2068 .expect("The fallback key should pass the signature verification");
2069
2070 Ok(())
2071 }
2072
2073 #[test]
2074 fn test_shared_history_flag_from_history_visibility() {
2075 assert!(
2076 shared_history_from_history_visibility(&HistoryVisibility::WorldReadable),
2077 "The world readable visibility should set the shared history flag to true"
2078 );
2079
2080 assert!(
2081 shared_history_from_history_visibility(&HistoryVisibility::Shared),
2082 "The shared visibility should set the shared history flag to true"
2083 );
2084
2085 assert!(
2086 !shared_history_from_history_visibility(&HistoryVisibility::Joined),
2087 "The joined visibility should set the shared history flag to false"
2088 );
2089
2090 assert!(
2091 !shared_history_from_history_visibility(&HistoryVisibility::Invited),
2092 "The invited visibility should set the shared history flag to false"
2093 );
2094
2095 let visibility = HistoryVisibility::from("custom_visibility");
2096 assert!(
2097 !shared_history_from_history_visibility(&visibility),
2098 "A custom visibility should set the shared history flag to false"
2099 );
2100 }
2101
2102 #[async_test]
2103 async fn test_shared_history_set_when_creating_group_sessions() {
2104 let account = Account::new(user_id());
2105 let room_id = room_id!("!room:id");
2106 let settings = EncryptionSettings {
2107 history_visibility: HistoryVisibility::Shared,
2108 ..Default::default()
2109 };
2110
2111 let (_, session) = account
2112 .create_group_session_pair(room_id, settings, Default::default())
2113 .await
2114 .expect("We should be able to create a group session pair");
2115
2116 assert!(
2117 session.shared_history(),
2118 "The shared history flag should have been set when we created the new session"
2119 );
2120 }
2121}