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;
25#[cfg(test)]
26use ruma::api::client::dehydrated_device::DehydratedDeviceV1;
27use ruma::{
28 api::client::{
29 dehydrated_device::{DehydratedDeviceData, DehydratedDeviceV2},
30 keys::{
31 upload_keys,
32 upload_signatures::v3::{Request as SignatureUploadRequest, SignedKeys},
33 },
34 },
35 events::{room::history_visibility::HistoryVisibility, AnyToDeviceEvent},
36 serde::Raw,
37 DeviceId, DeviceKeyAlgorithm, DeviceKeyId, MilliSecondsSinceUnixEpoch, OneTimeKeyAlgorithm,
38 OneTimeKeyId, OwnedDeviceId, OwnedDeviceKeyId, OwnedOneTimeKeyId, OwnedUserId, RoomId,
39 SecondsSinceUnixEpoch, UInt, UserId,
40};
41use serde::{de::Error, Deserialize, Serialize};
42use serde_json::{
43 value::{to_raw_value, RawValue as RawJsonValue},
44 Value,
45};
46use sha2::{Digest, Sha256};
47use tokio::sync::Mutex;
48use tracing::{debug, field::debug, info, instrument, trace, warn, Span};
49use vodozemac::{
50 base64_encode,
51 olm::{
52 Account as InnerAccount, AccountPickle, IdentityKeys, OlmMessage,
53 OneTimeKeyGenerationResult, PreKeyMessage, SessionConfig,
54 },
55 Curve25519PublicKey, Ed25519Signature, KeyId, PickleError,
56};
57
58use super::{
59 utility::SignJson, EncryptionSettings, InboundGroupSession, OutboundGroupSession,
60 PrivateCrossSigningIdentity, Session, SessionCreationError as MegolmSessionCreationError,
61};
62#[cfg(feature = "experimental-algorithms")]
63use crate::types::events::room::encrypted::OlmV2Curve25519AesSha2Content;
64use crate::{
65 dehydrated_devices::DehydrationError,
66 error::{EventError, OlmResult, SessionCreationError},
67 identities::DeviceData,
68 olm::SenderData,
69 store::{Changes, DeviceChanges, Store},
70 types::{
71 events::{
72 olm_v1::AnyDecryptedOlmEvent,
73 room::encrypted::{
74 EncryptedToDeviceEvent, OlmV1Curve25519AesSha2Content,
75 ToDeviceEncryptedEventContent,
76 },
77 },
78 requests::UploadSigningKeysRequest,
79 CrossSigningKey, DeviceKeys, EventEncryptionAlgorithm, MasterPubkey, OneTimeKey, SignedKey,
80 },
81 OlmError, SignatureError,
82};
83
84#[derive(Debug)]
85enum PrekeyBundle {
86 Olm3DH { key: SignedKey },
87}
88
89#[derive(Debug, Clone)]
90pub(crate) enum SessionType {
91 New(Session),
92 Existing(Session),
93}
94
95#[derive(Debug)]
96pub struct InboundCreationResult {
97 pub session: Session,
98 pub plaintext: String,
99}
100
101impl SessionType {
102 #[cfg(test)]
103 pub fn session(self) -> Session {
104 match self {
105 SessionType::New(s) => s,
106 SessionType::Existing(s) => s,
107 }
108 }
109}
110
111#[derive(Debug)]
117pub(crate) struct OlmDecryptionInfo {
118 pub session: SessionType,
119 pub message_hash: OlmMessageHash,
120 pub inbound_group_session: Option<InboundGroupSession>,
121 pub result: DecryptionResult,
122}
123
124#[derive(Debug)]
125pub(crate) struct DecryptionResult {
126 pub event: Box<AnyDecryptedOlmEvent>,
128 pub raw_event: Raw<AnyToDeviceEvent>,
129 pub sender_key: Curve25519PublicKey,
130}
131
132#[derive(Debug, Clone, Serialize, Deserialize)]
136pub struct OlmMessageHash {
137 pub sender_key: String,
139 pub hash: String,
141}
142
143impl OlmMessageHash {
144 fn new(sender_key: Curve25519PublicKey, ciphertext: &OlmMessage) -> Self {
145 let (message_type, ciphertext) = ciphertext.clone().to_parts();
146 let sender_key = sender_key.to_base64();
147
148 let sha = Sha256::new()
149 .chain_update(sender_key.as_bytes())
150 .chain_update([message_type as u8])
151 .chain_update(ciphertext)
152 .finalize();
153
154 Self { sender_key, hash: base64_encode(sha.as_slice()) }
155 }
156}
157
158#[derive(Clone)]
163#[cfg_attr(not(tarpaulin_include), derive(Debug))]
164pub struct StaticAccountData {
165 pub user_id: OwnedUserId,
167 pub device_id: OwnedDeviceId,
169 pub identity_keys: Arc<IdentityKeys>,
171 pub dehydrated: bool,
173 creation_local_time: MilliSecondsSinceUnixEpoch,
175}
176
177impl StaticAccountData {
178 const ALGORITHMS: &'static [&'static EventEncryptionAlgorithm] = &[
179 &EventEncryptionAlgorithm::OlmV1Curve25519AesSha2,
180 #[cfg(feature = "experimental-algorithms")]
181 &EventEncryptionAlgorithm::OlmV2Curve25519AesSha2,
182 &EventEncryptionAlgorithm::MegolmV1AesSha2,
183 #[cfg(feature = "experimental-algorithms")]
184 &EventEncryptionAlgorithm::MegolmV2AesSha2,
185 ];
186
187 pub async fn create_group_session_pair(
202 &self,
203 room_id: &RoomId,
204 settings: EncryptionSettings,
205 own_sender_data: SenderData,
206 ) -> Result<(OutboundGroupSession, InboundGroupSession), MegolmSessionCreationError> {
207 trace!(?room_id, algorithm = settings.algorithm.as_str(), "Creating a new room key");
208
209 let visibility = settings.history_visibility.clone();
210 let algorithm = settings.algorithm.to_owned();
211
212 let outbound = OutboundGroupSession::new(
213 self.device_id.clone(),
214 self.identity_keys.clone(),
215 room_id,
216 settings,
217 )?;
218
219 let identity_keys = &self.identity_keys;
220
221 let sender_key = identity_keys.curve25519;
222 let signing_key = identity_keys.ed25519;
223 let shared_history = shared_history_from_history_visibility(&visibility);
224
225 let inbound = InboundGroupSession::new(
226 sender_key,
227 signing_key,
228 room_id,
229 &outbound.session_key().await,
230 own_sender_data,
231 algorithm,
232 Some(visibility),
233 shared_history,
234 )?;
235
236 Ok((outbound, inbound))
237 }
238
239 #[cfg(any(test, feature = "testing"))]
240 #[allow(dead_code)]
241 pub async fn create_group_session_pair_with_defaults(
244 &self,
245 room_id: &RoomId,
246 ) -> (OutboundGroupSession, InboundGroupSession) {
247 self.create_group_session_pair(
248 room_id,
249 EncryptionSettings::default(),
250 SenderData::unknown(),
251 )
252 .await
253 .expect("Can't create default group session pair")
254 }
255
256 pub fn signing_key_id(&self) -> OwnedDeviceKeyId {
258 DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, self.device_id())
259 }
260
261 pub fn has_signed_raw(
271 &self,
272 signatures: &crate::types::Signatures,
273 canonical_json: &str,
274 ) -> Result<(), SignatureError> {
275 use crate::olm::utility::VerifyJson;
276
277 let signing_key = self.identity_keys.ed25519;
278
279 signing_key.verify_canonicalized_json(
280 &self.user_id,
281 &DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, self.device_id()),
282 signatures,
283 canonical_json,
284 )
285 }
286
287 pub fn unsigned_device_keys(&self) -> DeviceKeys {
289 let identity_keys = self.identity_keys();
290 let keys = BTreeMap::from([
291 (
292 DeviceKeyId::from_parts(DeviceKeyAlgorithm::Curve25519, &self.device_id),
293 identity_keys.curve25519.into(),
294 ),
295 (
296 DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, &self.device_id),
297 identity_keys.ed25519.into(),
298 ),
299 ]);
300
301 let mut ret = DeviceKeys::new(
302 (*self.user_id).to_owned(),
303 (*self.device_id).to_owned(),
304 Self::ALGORITHMS.iter().map(|a| (**a).clone()).collect(),
305 keys,
306 Default::default(),
307 );
308 if self.dehydrated {
309 ret.dehydrated = JsOption::Some(true);
310 }
311 ret
312 }
313
314 pub fn user_id(&self) -> &UserId {
316 &self.user_id
317 }
318
319 pub fn device_id(&self) -> &DeviceId {
321 &self.device_id
322 }
323
324 pub fn identity_keys(&self) -> IdentityKeys {
326 *self.identity_keys
327 }
328
329 pub fn creation_local_time(&self) -> MilliSecondsSinceUnixEpoch {
331 self.creation_local_time
332 }
333}
334
335pub struct Account {
340 pub(crate) static_data: StaticAccountData,
341 inner: Box<InnerAccount>,
343 shared: bool,
346 uploaded_signed_key_count: u64,
351 fallback_creation_timestamp: Option<MilliSecondsSinceUnixEpoch>,
359}
360
361impl Deref for Account {
362 type Target = StaticAccountData;
363
364 fn deref(&self) -> &Self::Target {
365 &self.static_data
366 }
367}
368
369#[derive(Serialize, Deserialize)]
374#[allow(missing_debug_implementations)]
375pub struct PickledAccount {
376 pub user_id: OwnedUserId,
378 pub device_id: OwnedDeviceId,
380 pub pickle: AccountPickle,
382 pub shared: bool,
384 #[serde(default)]
386 pub dehydrated: bool,
387 pub uploaded_signed_key_count: u64,
389 #[serde(default = "default_account_creation_time")]
392 pub creation_local_time: MilliSecondsSinceUnixEpoch,
393 #[serde(default)]
395 pub fallback_key_creation_timestamp: Option<MilliSecondsSinceUnixEpoch>,
396}
397
398fn default_account_creation_time() -> MilliSecondsSinceUnixEpoch {
399 MilliSecondsSinceUnixEpoch(UInt::default())
400}
401
402#[cfg(not(tarpaulin_include))]
403impl fmt::Debug for Account {
404 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
405 f.debug_struct("Account")
406 .field("identity_keys", &self.identity_keys())
407 .field("shared", &self.shared())
408 .finish()
409 }
410}
411
412pub type OneTimeKeys = BTreeMap<OwnedOneTimeKeyId, Raw<ruma::encryption::OneTimeKey>>;
413pub type FallbackKeys = OneTimeKeys;
414
415impl Account {
416 pub(crate) fn new_helper(
417 mut account: InnerAccount,
418 user_id: &UserId,
419 device_id: &DeviceId,
420 ) -> Self {
421 let identity_keys = account.identity_keys();
422
423 account.generate_one_time_keys(account.max_number_of_one_time_keys());
435
436 Self {
437 static_data: StaticAccountData {
438 user_id: user_id.into(),
439 device_id: device_id.into(),
440 identity_keys: Arc::new(identity_keys),
441 dehydrated: false,
442 creation_local_time: MilliSecondsSinceUnixEpoch::now(),
443 },
444 inner: Box::new(account),
445 shared: false,
446 uploaded_signed_key_count: 0,
447 fallback_creation_timestamp: None,
448 }
449 }
450
451 pub fn with_device_id(user_id: &UserId, device_id: &DeviceId) -> Self {
453 let account = InnerAccount::new();
454
455 Self::new_helper(account, user_id, device_id)
456 }
457
458 pub fn new(user_id: &UserId) -> Self {
461 let account = InnerAccount::new();
462 let device_id: OwnedDeviceId =
463 base64_encode(account.identity_keys().curve25519.as_bytes()).into();
464
465 Self::new_helper(account, user_id, &device_id)
466 }
467
468 pub fn new_dehydrated(user_id: &UserId) -> Self {
470 let account = InnerAccount::new();
471 let device_id: OwnedDeviceId =
472 base64_encode(account.identity_keys().curve25519.as_bytes()).into();
473
474 let mut ret = Self::new_helper(account, user_id, &device_id);
475 ret.static_data.dehydrated = true;
476 ret
477 }
478
479 pub fn static_data(&self) -> &StaticAccountData {
481 &self.static_data
482 }
483
484 pub fn update_uploaded_key_count(&mut self, new_count: u64) {
490 self.uploaded_signed_key_count = new_count;
491 }
492
493 pub fn uploaded_key_count(&self) -> u64 {
495 self.uploaded_signed_key_count
496 }
497
498 pub fn shared(&self) -> bool {
500 self.shared
501 }
502
503 pub fn mark_as_shared(&mut self) {
508 self.shared = true;
509 }
510
511 pub fn one_time_keys(&self) -> HashMap<KeyId, Curve25519PublicKey> {
515 self.inner.one_time_keys()
516 }
517
518 pub fn generate_one_time_keys(&mut self, count: usize) -> OneTimeKeyGenerationResult {
520 self.inner.generate_one_time_keys(count)
521 }
522
523 pub fn max_one_time_keys(&self) -> usize {
525 self.inner.max_number_of_one_time_keys()
526 }
527
528 pub(crate) fn update_key_counts(
529 &mut self,
530 one_time_key_counts: &BTreeMap<OneTimeKeyAlgorithm, UInt>,
531 unused_fallback_keys: Option<&[OneTimeKeyAlgorithm]>,
532 ) {
533 if let Some(count) = one_time_key_counts.get(&OneTimeKeyAlgorithm::SignedCurve25519) {
534 let count: u64 = (*count).into();
535 let old_count = self.uploaded_key_count();
536
537 if count != old_count {
541 debug!(
542 "Updated uploaded one-time key count {} -> {count}.",
543 self.uploaded_key_count(),
544 );
545 }
546
547 self.update_uploaded_key_count(count);
548 self.generate_one_time_keys_if_needed();
549 }
550
551 if unused_fallback_keys.is_some() || self.fallback_creation_timestamp.is_some() {
555 self.generate_fallback_key_if_needed();
556 }
557 }
558
559 #[instrument(skip_all)]
568 pub fn generate_one_time_keys_if_needed(&mut self) -> Option<u64> {
569 if !self.one_time_keys().is_empty() {
573 return Some(0);
574 }
575
576 let count = self.uploaded_key_count();
577 let max_keys = self.max_one_time_keys();
578
579 if count >= max_keys as u64 {
580 return None;
581 }
582
583 let key_count = (max_keys as u64) - count;
584 let key_count: usize = key_count.try_into().unwrap_or(max_keys);
585
586 let result = self.generate_one_time_keys(key_count);
587
588 debug!(
589 count = key_count,
590 discarded_keys = ?result.removed,
591 created_keys = ?result.created,
592 "Generated new one-time keys"
593 );
594
595 Some(key_count as u64)
596 }
597
598 pub(crate) fn generate_fallback_key_if_needed(&mut self) {
605 if self.inner.fallback_key().is_empty() && self.fallback_key_expired() {
606 let removed_fallback_key = self.inner.generate_fallback_key();
607 self.fallback_creation_timestamp = Some(MilliSecondsSinceUnixEpoch::now());
608
609 debug!(
610 ?removed_fallback_key,
611 "The fallback key either expired or we didn't have one: generated a new fallback key.",
612 );
613 }
614 }
615
616 fn fallback_key_expired(&self) -> bool {
624 const FALLBACK_KEY_MAX_AGE: Duration = Duration::from_secs(3600 * 24 * 7);
625
626 if let Some(time) = self.fallback_creation_timestamp {
627 let Some(system_time) = time.to_system_time() else {
631 return true;
632 };
633
634 let Ok(elapsed) = system_time.elapsed() else {
638 return true;
639 };
640
641 elapsed > FALLBACK_KEY_MAX_AGE
646 } else {
647 true
650 }
651 }
652
653 fn fallback_key(&self) -> HashMap<KeyId, Curve25519PublicKey> {
654 self.inner.fallback_key()
655 }
656
657 pub fn keys_for_upload(&self) -> (Option<DeviceKeys>, OneTimeKeys, FallbackKeys) {
663 let device_keys = self.shared().not().then(|| self.device_keys());
664
665 let one_time_keys = self.signed_one_time_keys();
666 let fallback_keys = self.signed_fallback_keys();
667
668 (device_keys, one_time_keys, fallback_keys)
669 }
670
671 pub fn mark_keys_as_published(&mut self) {
673 self.inner.mark_keys_as_published();
674 }
675
676 pub fn sign(&self, string: &str) -> Ed25519Signature {
680 self.inner.sign(string)
681 }
682
683 pub fn pickle(&self) -> PickledAccount {
685 let pickle = self.inner.pickle();
686
687 PickledAccount {
688 user_id: self.user_id().to_owned(),
689 device_id: self.device_id().to_owned(),
690 pickle,
691 shared: self.shared(),
692 dehydrated: self.static_data.dehydrated,
693 uploaded_signed_key_count: self.uploaded_key_count(),
694 creation_local_time: self.static_data.creation_local_time,
695 fallback_key_creation_timestamp: self.fallback_creation_timestamp,
696 }
697 }
698
699 pub(crate) fn dehydrate(&self, pickle_key: &[u8; 32]) -> Raw<DehydratedDeviceData> {
700 let dehydration_result = self
701 .inner
702 .to_dehydrated_device(pickle_key)
703 .expect("We should be able to convert a freshly created Account into a libolm pickle");
704
705 let data = DehydratedDeviceData::V2(DehydratedDeviceV2::new(
706 dehydration_result.ciphertext,
707 dehydration_result.nonce,
708 ));
709 Raw::from_json(to_raw_value(&data).expect("Couldn't serialize our dehydrated device data"))
710 }
711
712 pub(crate) fn rehydrate(
713 pickle_key: &[u8; 32],
714 user_id: &UserId,
715 device_id: &DeviceId,
716 device_data: Raw<DehydratedDeviceData>,
717 ) -> Result<Self, DehydrationError> {
718 let data = device_data.deserialize()?;
719
720 match data {
721 DehydratedDeviceData::V1(d) => {
722 let pickle_key = expand_legacy_pickle_key(pickle_key, device_id);
723 let account =
724 InnerAccount::from_libolm_pickle(&d.device_pickle, pickle_key.as_ref())?;
725 Ok(Self::new_helper(account, user_id, device_id))
726 }
727 DehydratedDeviceData::V2(d) => {
728 let account =
729 InnerAccount::from_dehydrated_device(&d.device_pickle, &d.nonce, pickle_key)?;
730 Ok(Self::new_helper(account, user_id, device_id))
731 }
732 _ => Err(DehydrationError::Json(serde_json::Error::custom(format!(
733 "Unsupported dehydrated device algorithm {:?}",
734 data.algorithm()
735 )))),
736 }
737 }
738
739 #[cfg(test)]
742 pub(crate) fn legacy_dehydrate(&self, pickle_key: &[u8; 32]) -> Raw<DehydratedDeviceData> {
743 let pickle_key = expand_legacy_pickle_key(pickle_key, &self.device_id);
744 let device_pickle = self
745 .inner
746 .to_libolm_pickle(pickle_key.as_ref())
747 .expect("We should be able to convert a freshly created Account into a libolm pickle");
748
749 let data = DehydratedDeviceData::V1(DehydratedDeviceV1::new(device_pickle));
750 Raw::from_json(to_raw_value(&data).expect("Couldn't serialize our dehydrated device data"))
751 }
752
753 pub fn from_pickle(pickle: PickledAccount) -> Result<Self, PickleError> {
762 let account: vodozemac::olm::Account = pickle.pickle.into();
763 let identity_keys = account.identity_keys();
764
765 Ok(Self {
766 static_data: StaticAccountData {
767 user_id: (*pickle.user_id).into(),
768 device_id: (*pickle.device_id).into(),
769 identity_keys: Arc::new(identity_keys),
770 dehydrated: pickle.dehydrated,
771 creation_local_time: pickle.creation_local_time,
772 },
773 inner: Box::new(account),
774 shared: pickle.shared,
775 uploaded_signed_key_count: pickle.uploaded_signed_key_count,
776 fallback_creation_timestamp: pickle.fallback_key_creation_timestamp,
777 })
778 }
779
780 pub fn device_keys(&self) -> DeviceKeys {
783 let mut device_keys = self.unsigned_device_keys();
784
785 let json_device_keys =
788 serde_json::to_value(&device_keys).expect("device key is always safe to serialize");
789 let signature = self
790 .sign_json(json_device_keys)
791 .expect("Newly created device keys can always be signed");
792
793 device_keys.signatures.add_signature(
794 self.user_id().to_owned(),
795 DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, &self.static_data.device_id),
796 signature,
797 );
798
799 device_keys
800 }
801
802 pub async fn bootstrap_cross_signing(
804 &self,
805 ) -> (PrivateCrossSigningIdentity, UploadSigningKeysRequest, SignatureUploadRequest) {
806 PrivateCrossSigningIdentity::with_account(self).await
807 }
808
809 pub fn sign_cross_signing_key(
811 &self,
812 cross_signing_key: &mut CrossSigningKey,
813 ) -> Result<(), SignatureError> {
814 #[allow(clippy::needless_borrows_for_generic_args)]
815 let signature = self.sign_json(serde_json::to_value(&cross_signing_key)?)?;
817
818 cross_signing_key.signatures.add_signature(
819 self.user_id().to_owned(),
820 DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, self.device_id()),
821 signature,
822 );
823
824 Ok(())
825 }
826
827 pub fn sign_master_key(
829 &self,
830 master_key: &MasterPubkey,
831 ) -> Result<SignatureUploadRequest, SignatureError> {
832 let public_key =
833 master_key.get_first_key().ok_or(SignatureError::MissingSigningKey)?.to_base64().into();
834
835 let mut cross_signing_key: CrossSigningKey = master_key.as_ref().clone();
836 cross_signing_key.signatures.clear();
837 self.sign_cross_signing_key(&mut cross_signing_key)?;
838
839 let mut user_signed_keys = SignedKeys::new();
840 user_signed_keys.add_cross_signing_keys(public_key, cross_signing_key.to_raw());
841
842 let signed_keys = [(self.user_id().to_owned(), user_signed_keys)].into();
843 Ok(SignatureUploadRequest::new(signed_keys))
844 }
845
846 pub fn sign_json(&self, json: Value) -> Result<Ed25519Signature, SignatureError> {
854 self.inner.sign_json(json)
855 }
856
857 pub fn signed_one_time_keys(&self) -> OneTimeKeys {
861 let one_time_keys = self.one_time_keys();
862
863 if one_time_keys.is_empty() {
864 BTreeMap::new()
865 } else {
866 self.signed_keys(one_time_keys, false)
867 }
868 }
869
870 pub fn signed_fallback_keys(&self) -> FallbackKeys {
874 let fallback_key = self.fallback_key();
875
876 if fallback_key.is_empty() {
877 BTreeMap::new()
878 } else {
879 self.signed_keys(fallback_key, true)
880 }
881 }
882
883 fn signed_keys(
884 &self,
885 keys: HashMap<KeyId, Curve25519PublicKey>,
886 fallback: bool,
887 ) -> OneTimeKeys {
888 let mut keys_map = BTreeMap::new();
889
890 for (key_id, key) in keys {
891 let signed_key = self.sign_key(key, fallback);
892
893 keys_map.insert(
894 OneTimeKeyId::from_parts(
895 OneTimeKeyAlgorithm::SignedCurve25519,
896 key_id.to_base64().as_str().into(),
897 ),
898 signed_key.into_raw(),
899 );
900 }
901
902 keys_map
903 }
904
905 fn sign_key(&self, key: Curve25519PublicKey, fallback: bool) -> SignedKey {
906 let mut key = if fallback {
907 SignedKey::new_fallback(key.to_owned())
908 } else {
909 SignedKey::new(key.to_owned())
910 };
911
912 let signature = self
913 .sign_json(serde_json::to_value(&key).expect("Can't serialize a signed key"))
914 .expect("Newly created one-time keys can always be signed");
915
916 key.signatures_mut().add_signature(
917 self.user_id().to_owned(),
918 DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, self.device_id()),
919 signature,
920 );
921
922 key
923 }
924
925 pub fn create_outbound_session_helper(
945 &self,
946 config: SessionConfig,
947 identity_key: Curve25519PublicKey,
948 one_time_key: Curve25519PublicKey,
949 fallback_used: bool,
950 our_device_keys: DeviceKeys,
951 ) -> Session {
952 let session = self.inner.create_outbound_session(config, identity_key, one_time_key);
953
954 let now = SecondsSinceUnixEpoch::now();
955 let session_id = session.session_id();
956
957 Session {
958 inner: Arc::new(Mutex::new(session)),
959 session_id: session_id.into(),
960 sender_key: identity_key,
961 our_device_keys,
962 created_using_fallback_key: fallback_used,
963 creation_time: now,
964 last_use_time: now,
965 }
966 }
967
968 #[instrument(
969 skip_all,
970 fields(
971 user_id = ?device.user_id(),
972 device_id = ?device.device_id(),
973 algorithms = ?device.algorithms()
974 )
975 )]
976 fn find_pre_key_bundle(
977 device: &DeviceData,
978 key_map: &OneTimeKeys,
979 ) -> Result<PrekeyBundle, SessionCreationError> {
980 let mut keys = key_map.iter();
981
982 let first_key = keys.next().ok_or_else(|| {
983 SessionCreationError::OneTimeKeyMissing(
984 device.user_id().to_owned(),
985 device.device_id().into(),
986 )
987 })?;
988
989 let first_key_id = first_key.0.to_owned();
990 let first_key = OneTimeKey::deserialize(first_key_id.algorithm(), first_key.1)?;
991
992 let result = match first_key {
993 OneTimeKey::SignedKey(key) => Ok(PrekeyBundle::Olm3DH { key }),
994 };
995
996 trace!(?result, "Finished searching for a valid pre-key bundle");
997
998 result
999 }
1000
1001 #[allow(clippy::result_large_err)]
1016 pub fn create_outbound_session(
1017 &self,
1018 device: &DeviceData,
1019 key_map: &OneTimeKeys,
1020 our_device_keys: DeviceKeys,
1021 ) -> Result<Session, SessionCreationError> {
1022 let pre_key_bundle = Self::find_pre_key_bundle(device, key_map)?;
1023
1024 match pre_key_bundle {
1025 PrekeyBundle::Olm3DH { key } => {
1026 device.verify_one_time_key(&key).map_err(|error| {
1027 SessionCreationError::InvalidSignature {
1028 signing_key: device.ed25519_key().map(Box::new),
1029 one_time_key: key.clone().into(),
1030 error: error.into(),
1031 }
1032 })?;
1033
1034 let identity_key = device.curve25519_key().ok_or_else(|| {
1035 SessionCreationError::DeviceMissingCurveKey(
1036 device.user_id().to_owned(),
1037 device.device_id().into(),
1038 )
1039 })?;
1040
1041 let is_fallback = key.fallback();
1042 let one_time_key = key.key();
1043 let config = device.olm_session_config();
1044
1045 Ok(self.create_outbound_session_helper(
1046 config,
1047 identity_key,
1048 one_time_key,
1049 is_fallback,
1050 our_device_keys,
1051 ))
1052 }
1053 }
1054 }
1055
1056 pub fn create_inbound_session(
1071 &mut self,
1072 their_identity_key: Curve25519PublicKey,
1073 our_device_keys: DeviceKeys,
1074 message: &PreKeyMessage,
1075 ) -> Result<InboundCreationResult, SessionCreationError> {
1076 Span::current().record("session_id", debug(message.session_id()));
1077 trace!("Creating a new Olm session from a pre-key message");
1078
1079 let result = self.inner.create_inbound_session(their_identity_key, message)?;
1080 let now = SecondsSinceUnixEpoch::now();
1081 let session_id = result.session.session_id();
1082
1083 debug!(session=?result.session, "Decrypted an Olm message from a new Olm session");
1084
1085 let session = Session {
1086 inner: Arc::new(Mutex::new(result.session)),
1087 session_id: session_id.into(),
1088 sender_key: their_identity_key,
1089 our_device_keys,
1090 created_using_fallback_key: false,
1091 creation_time: now,
1092 last_use_time: now,
1093 };
1094
1095 let plaintext = String::from_utf8_lossy(&result.plaintext).to_string();
1096
1097 Ok(InboundCreationResult { session, plaintext })
1098 }
1099
1100 #[cfg(any(test, feature = "testing"))]
1101 #[allow(dead_code)]
1102 pub async fn create_session_for_test_helper(
1104 &mut self,
1105 other: &mut Account,
1106 ) -> (Session, Session) {
1107 use ruma::events::dummy::ToDeviceDummyEventContent;
1108
1109 other.generate_one_time_keys(1);
1110 let one_time_map = other.signed_one_time_keys();
1111 let device = DeviceData::from_account(other);
1112
1113 let mut our_session =
1114 self.create_outbound_session(&device, &one_time_map, self.device_keys()).unwrap();
1115
1116 other.mark_keys_as_published();
1117
1118 let message = our_session
1119 .encrypt(&device, "m.dummy", ToDeviceDummyEventContent::new(), None)
1120 .await
1121 .unwrap()
1122 .deserialize()
1123 .unwrap();
1124
1125 #[cfg(feature = "experimental-algorithms")]
1126 let content = if let ToDeviceEncryptedEventContent::OlmV2Curve25519AesSha2(c) = message {
1127 c
1128 } else {
1129 panic!("Invalid encrypted event algorithm {}", message.algorithm());
1130 };
1131
1132 #[cfg(not(feature = "experimental-algorithms"))]
1133 let content = if let ToDeviceEncryptedEventContent::OlmV1Curve25519AesSha2(c) = message {
1134 c
1135 } else {
1136 panic!("Invalid encrypted event algorithm {}", message.algorithm());
1137 };
1138
1139 let prekey = if let OlmMessage::PreKey(m) = content.ciphertext {
1140 m
1141 } else {
1142 panic!("Wrong Olm message type");
1143 };
1144
1145 let our_device = DeviceData::from_account(self);
1146 let other_session = other
1147 .create_inbound_session(
1148 our_device.curve25519_key().unwrap(),
1149 other.device_keys(),
1150 &prekey,
1151 )
1152 .unwrap();
1153
1154 (our_session, other_session.session)
1155 }
1156
1157 async fn decrypt_olm_helper(
1158 &mut self,
1159 store: &Store,
1160 sender: &UserId,
1161 sender_key: Curve25519PublicKey,
1162 ciphertext: &OlmMessage,
1163 ) -> OlmResult<OlmDecryptionInfo> {
1164 let message_hash = OlmMessageHash::new(sender_key, ciphertext);
1165
1166 match self.decrypt_and_parse_olm_message(store, sender, sender_key, ciphertext).await {
1167 Ok((session, result)) => {
1168 Ok(OlmDecryptionInfo { session, message_hash, result, inbound_group_session: None })
1169 }
1170 Err(OlmError::SessionWedged(user_id, sender_key)) => {
1171 if store.is_message_known(&message_hash).await? {
1172 info!(?sender_key, "An Olm message got replayed, decryption failed");
1173 Err(OlmError::ReplayedMessage(user_id, sender_key))
1174 } else {
1175 Err(OlmError::SessionWedged(user_id, sender_key))
1176 }
1177 }
1178 Err(e) => Err(e),
1179 }
1180 }
1181
1182 #[cfg(feature = "experimental-algorithms")]
1183 async fn decrypt_olm_v2(
1184 &mut self,
1185 store: &Store,
1186 sender: &UserId,
1187 content: &OlmV2Curve25519AesSha2Content,
1188 ) -> OlmResult<OlmDecryptionInfo> {
1189 self.decrypt_olm_helper(store, sender, content.sender_key, &content.ciphertext).await
1190 }
1191
1192 #[instrument(skip_all, fields(sender, sender_key = ?content.sender_key))]
1193 async fn decrypt_olm_v1(
1194 &mut self,
1195 store: &Store,
1196 sender: &UserId,
1197 content: &OlmV1Curve25519AesSha2Content,
1198 ) -> OlmResult<OlmDecryptionInfo> {
1199 if content.recipient_key != self.static_data.identity_keys.curve25519 {
1200 warn!("Olm event doesn't contain a ciphertext for our key");
1201
1202 Err(EventError::MissingCiphertext.into())
1203 } else {
1204 Box::pin(self.decrypt_olm_helper(
1205 store,
1206 sender,
1207 content.sender_key,
1208 &content.ciphertext,
1209 ))
1210 .await
1211 }
1212 }
1213
1214 #[instrument(skip_all, fields(algorithm = ?event.content.algorithm()))]
1215 pub(crate) async fn decrypt_to_device_event(
1216 &mut self,
1217 store: &Store,
1218 event: &EncryptedToDeviceEvent,
1219 ) -> OlmResult<OlmDecryptionInfo> {
1220 trace!("Decrypting a to-device event");
1221
1222 match &event.content {
1223 ToDeviceEncryptedEventContent::OlmV1Curve25519AesSha2(c) => {
1224 self.decrypt_olm_v1(store, &event.sender, c).await
1225 }
1226 #[cfg(feature = "experimental-algorithms")]
1227 ToDeviceEncryptedEventContent::OlmV2Curve25519AesSha2(c) => {
1228 self.decrypt_olm_v2(store, &event.sender, c).await
1229 }
1230 ToDeviceEncryptedEventContent::Unknown(_) => {
1231 warn!(
1232 "Error decrypting an to-device event, unsupported \
1233 encryption algorithm"
1234 );
1235
1236 Err(EventError::UnsupportedAlgorithm.into())
1237 }
1238 }
1239 }
1240
1241 pub fn receive_keys_upload_response(
1243 &mut self,
1244 response: &upload_keys::v3::Response,
1245 ) -> OlmResult<()> {
1246 if !self.shared() {
1247 debug!("Marking account as shared");
1248 }
1249 self.mark_as_shared();
1250
1251 debug!("Marking one-time keys as published");
1252 self.mark_keys_as_published();
1255 self.update_key_counts(&response.one_time_key_counts, None);
1256
1257 Ok(())
1258 }
1259
1260 async fn decrypt_olm_message(
1262 &mut self,
1263 store: &Store,
1264 sender: &UserId,
1265 sender_key: Curve25519PublicKey,
1266 message: &OlmMessage,
1267 ) -> Result<(SessionType, String), OlmError> {
1268 let existing_sessions = store.get_sessions(&sender_key.to_base64()).await?;
1269
1270 match message {
1271 OlmMessage::Normal(_) => {
1272 let mut errors_by_olm_session = Vec::new();
1273
1274 if let Some(sessions) = existing_sessions {
1275 for session in sessions.lock().await.iter_mut() {
1278 match session.decrypt(message).await {
1279 Ok(p) => {
1280 return Ok((SessionType::Existing(session.clone()), p));
1282 }
1283
1284 Err(e) => {
1285 errors_by_olm_session.push((session.session_id().to_owned(), e));
1290 }
1291 }
1292 }
1293 }
1294
1295 warn!(
1296 ?errors_by_olm_session,
1297 "Failed to decrypt a non-pre-key message with all available sessions"
1298 );
1299 Err(OlmError::SessionWedged(sender.to_owned(), sender_key))
1300 }
1301
1302 OlmMessage::PreKey(prekey_message) => {
1303 if let Some(sessions) = existing_sessions {
1305 for session in sessions.lock().await.iter_mut() {
1306 if prekey_message.session_id() != session.session_id() {
1307 continue;
1309 }
1310
1311 if let Ok(p) = session.decrypt(message).await {
1312 return Ok((SessionType::Existing(session.clone()), p));
1314 }
1315
1316 warn!(
1330 session_id = session.session_id(),
1331 "Failed to decrypt a pre-key message with the corresponding session"
1332 );
1333
1334 return Err(OlmError::SessionWedged(
1335 session.our_device_keys.user_id.to_owned(),
1336 session.sender_key(),
1337 ));
1338 }
1339 }
1340
1341 let device_keys = store.get_own_device().await?.as_device_keys().clone();
1342 let result =
1343 match self.create_inbound_session(sender_key, device_keys, prekey_message) {
1344 Ok(r) => r,
1345 Err(e) => {
1346 warn!(
1347 "Failed to create a new Olm session from a pre-key message: {e:?}"
1348 );
1349 return Err(OlmError::SessionWedged(sender.to_owned(), sender_key));
1350 }
1351 };
1352
1353 let mut changes =
1358 Changes { sessions: vec![result.session.clone()], ..Default::default() };
1359
1360 if let Some(device) = store.get_device_from_curve_key(sender, sender_key).await? {
1367 let mut device_data = device.inner;
1368 device_data.olm_wedging_index.increment();
1369
1370 changes.devices =
1371 DeviceChanges { changed: vec![device_data], ..Default::default() };
1372 }
1373
1374 store.save_changes(changes).await?;
1375
1376 Ok((SessionType::New(result.session), result.plaintext))
1377 }
1378 }
1379 }
1380
1381 #[instrument(skip(self, store), fields(session, session_id))]
1384 async fn decrypt_and_parse_olm_message(
1385 &mut self,
1386 store: &Store,
1387 sender: &UserId,
1388 sender_key: Curve25519PublicKey,
1389 message: &OlmMessage,
1390 ) -> OlmResult<(SessionType, DecryptionResult)> {
1391 let (session, plaintext) =
1392 self.decrypt_olm_message(store, sender, sender_key, message).await?;
1393
1394 trace!("Successfully decrypted an Olm message");
1395
1396 match self.parse_decrypted_to_device_event(store, sender, sender_key, plaintext).await {
1397 Ok(result) => Ok((session, result)),
1398 Err(e) => {
1399 match session {
1403 SessionType::New(s) | SessionType::Existing(s) => {
1404 store.save_sessions(&[s]).await?;
1405 }
1406 }
1407
1408 warn!(
1409 error = ?e,
1410 "A to-device message was successfully decrypted but \
1411 parsing and checking the event fields failed"
1412 );
1413
1414 Err(e)
1415 }
1416 }
1417 }
1418
1419 async fn parse_decrypted_to_device_event(
1440 &self,
1441 store: &Store,
1442 sender: &UserId,
1443 sender_key: Curve25519PublicKey,
1444 plaintext: String,
1445 ) -> OlmResult<DecryptionResult> {
1446 let event: Box<AnyDecryptedOlmEvent> = serde_json::from_str(&plaintext)?;
1447 let identity_keys = &self.static_data.identity_keys;
1448
1449 if event.recipient() != self.static_data.user_id {
1450 Err(EventError::MismatchedSender(
1451 event.recipient().to_owned(),
1452 self.static_data.user_id.clone(),
1453 )
1454 .into())
1455 }
1456 else if event.sender() != sender {
1459 Err(EventError::MismatchedSender(event.sender().to_owned(), sender.to_owned()).into())
1460 } else if identity_keys.ed25519 != event.recipient_keys().ed25519 {
1461 Err(EventError::MismatchedKeys(
1462 identity_keys.ed25519.into(),
1463 event.recipient_keys().ed25519.into(),
1464 )
1465 .into())
1466 } else {
1467 if !matches!(*event, AnyDecryptedOlmEvent::RoomKey(_)) {
1472 let Some(device) =
1473 store.get_device_from_curve_key(event.sender(), sender_key).await?
1474 else {
1475 return Err(EventError::MissingSigningKey.into());
1476 };
1477
1478 let Some(key) = device.ed25519_key() else {
1479 return Err(EventError::MissingSigningKey.into());
1480 };
1481
1482 if key != event.keys().ed25519 {
1483 return Err(EventError::MismatchedKeys(
1484 key.into(),
1485 event.keys().ed25519.into(),
1486 )
1487 .into());
1488 }
1489 }
1490
1491 Ok(DecryptionResult {
1492 event,
1493 raw_event: Raw::from_json(RawJsonValue::from_string(plaintext)?),
1494 sender_key,
1495 })
1496 }
1497 }
1498
1499 #[doc(hidden)]
1504 pub fn deep_clone(&self) -> Self {
1505 Self::from_pickle(self.pickle()).unwrap()
1507 }
1508}
1509
1510impl PartialEq for Account {
1511 fn eq(&self, other: &Self) -> bool {
1512 self.identity_keys() == other.identity_keys() && self.shared() == other.shared()
1513 }
1514}
1515
1516pub(crate) fn shared_history_from_history_visibility(
1536 history_visibility: &HistoryVisibility,
1537) -> bool {
1538 match history_visibility {
1539 HistoryVisibility::Shared | HistoryVisibility::WorldReadable => true,
1540 HistoryVisibility::Invited | HistoryVisibility::Joined | _ => false,
1541 }
1542}
1543
1544fn expand_legacy_pickle_key(key: &[u8; 32], device_id: &DeviceId) -> Box<[u8; 32]> {
1563 let kdf: Hkdf<Sha256> = Hkdf::new(Some(device_id.as_bytes()), key);
1564 let mut key = Box::new([0u8; 32]);
1565
1566 kdf.expand(b"dehydrated-device-pickle-key", key.as_mut_slice())
1567 .expect("We should be able to expand the 32 byte pickle key");
1568
1569 key
1570}
1571
1572#[cfg(test)]
1573mod tests {
1574 use std::{
1575 collections::{BTreeMap, BTreeSet},
1576 ops::Deref,
1577 time::Duration,
1578 };
1579
1580 use anyhow::Result;
1581 use matrix_sdk_test::async_test;
1582 use ruma::{
1583 device_id, events::room::history_visibility::HistoryVisibility, room_id, user_id, DeviceId,
1584 MilliSecondsSinceUnixEpoch, OneTimeKeyAlgorithm, OneTimeKeyId, UserId,
1585 };
1586 use serde_json::json;
1587
1588 use super::Account;
1589 use crate::{
1590 olm::{account::shared_history_from_history_visibility, SignedJsonObject},
1591 types::{DeviceKeys, SignedKey},
1592 DeviceData, EncryptionSettings,
1593 };
1594
1595 fn user_id() -> &'static UserId {
1596 user_id!("@alice:localhost")
1597 }
1598
1599 fn device_id() -> &'static DeviceId {
1600 device_id!("DEVICEID")
1601 }
1602
1603 #[test]
1604 fn test_one_time_key_creation() -> Result<()> {
1605 let mut account = Account::with_device_id(user_id(), device_id());
1606
1607 let (_, one_time_keys, _) = account.keys_for_upload();
1608 assert!(!one_time_keys.is_empty());
1609
1610 let (_, second_one_time_keys, _) = account.keys_for_upload();
1611 assert!(!second_one_time_keys.is_empty());
1612
1613 let one_time_key_ids: BTreeSet<&OneTimeKeyId> =
1614 one_time_keys.keys().map(Deref::deref).collect();
1615 let second_one_time_key_ids: BTreeSet<&OneTimeKeyId> =
1616 second_one_time_keys.keys().map(Deref::deref).collect();
1617
1618 assert_eq!(one_time_key_ids, second_one_time_key_ids);
1619
1620 account.mark_keys_as_published();
1621 account.update_uploaded_key_count(50);
1622 account.generate_one_time_keys_if_needed();
1623
1624 let (_, third_one_time_keys, _) = account.keys_for_upload();
1625 assert!(third_one_time_keys.is_empty());
1626
1627 account.update_uploaded_key_count(0);
1628 account.generate_one_time_keys_if_needed();
1629
1630 let (_, fourth_one_time_keys, _) = account.keys_for_upload();
1631 assert!(!fourth_one_time_keys.is_empty());
1632
1633 let fourth_one_time_key_ids: BTreeSet<&OneTimeKeyId> =
1634 fourth_one_time_keys.keys().map(Deref::deref).collect();
1635
1636 assert_ne!(one_time_key_ids, fourth_one_time_key_ids);
1637 Ok(())
1638 }
1639
1640 #[test]
1641 fn test_fallback_key_creation() -> Result<()> {
1642 let mut account = Account::with_device_id(user_id(), device_id());
1643
1644 let (_, _, fallback_keys) = account.keys_for_upload();
1645
1646 assert!(
1650 fallback_keys.is_empty(),
1651 "We should not upload fallback keys until we know if the server supports them."
1652 );
1653
1654 let one_time_keys = BTreeMap::from([(OneTimeKeyAlgorithm::SignedCurve25519, 50u8.into())]);
1655
1656 account.update_key_counts(&one_time_keys, None);
1659 let (_, _, fallback_keys) = account.keys_for_upload();
1660 assert!(
1661 fallback_keys.is_empty(),
1662 "We should not upload a fallback key if we're certain that the server doesn't support \
1663 them."
1664 );
1665
1666 let unused_fallback_keys = &[];
1670 account.update_key_counts(&one_time_keys, Some(unused_fallback_keys.as_ref()));
1671 let (_, _, fallback_keys) = account.keys_for_upload();
1672 assert!(
1673 !fallback_keys.is_empty(),
1674 "We should upload the initial fallback key if the server supports them."
1675 );
1676 account.mark_keys_as_published();
1677
1678 let unused_fallback_keys = &[];
1681 account.update_key_counts(&one_time_keys, Some(unused_fallback_keys.as_ref()));
1682 let (_, _, fallback_keys) = account.keys_for_upload();
1683 assert!(
1684 fallback_keys.is_empty(),
1685 "We should not upload new fallback keys unless our current fallback key expires."
1686 );
1687
1688 let fallback_key_timestamp =
1689 account.fallback_creation_timestamp.unwrap().to_system_time().unwrap()
1690 - Duration::from_secs(3600 * 24 * 30);
1691
1692 account.fallback_creation_timestamp =
1693 Some(MilliSecondsSinceUnixEpoch::from_system_time(fallback_key_timestamp).unwrap());
1694
1695 account.update_key_counts(&one_time_keys, None);
1696 let (_, _, fallback_keys) = account.keys_for_upload();
1697 assert!(
1698 !fallback_keys.is_empty(),
1699 "Now that our fallback key has expired, we should try to upload a new one, even if the \
1700 server supposedly doesn't support fallback keys anymore"
1701 );
1702
1703 Ok(())
1704 }
1705
1706 #[test]
1707 fn test_fallback_key_signing() -> Result<()> {
1708 let key = vodozemac::Curve25519PublicKey::from_base64(
1709 "7PUPP6Ijt5R8qLwK2c8uK5hqCNF9tOzWYgGaAay5JBs",
1710 )?;
1711 let account = Account::with_device_id(user_id(), device_id());
1712
1713 let key = account.sign_key(key, true);
1714
1715 let canonical_key = key.to_canonical_json()?;
1716
1717 assert_eq!(
1718 canonical_key,
1719 "{\"fallback\":true,\"key\":\"7PUPP6Ijt5R8qLwK2c8uK5hqCNF9tOzWYgGaAay5JBs\"}"
1720 );
1721
1722 account
1723 .has_signed_raw(key.signatures(), &canonical_key)
1724 .expect("Couldn't verify signature");
1725
1726 let device = DeviceData::from_account(&account);
1727 device.verify_one_time_key(&key).expect("The device can verify its own signature");
1728
1729 Ok(())
1730 }
1731
1732 #[test]
1733 fn test_account_and_device_creation_timestamp() -> Result<()> {
1734 let now = MilliSecondsSinceUnixEpoch::now();
1735 let account = Account::with_device_id(user_id(), device_id());
1736 let then = MilliSecondsSinceUnixEpoch::now();
1737
1738 assert!(account.creation_local_time() >= now);
1739 assert!(account.creation_local_time() <= then);
1740
1741 let device = DeviceData::from_account(&account);
1742 assert_eq!(account.creation_local_time(), device.first_time_seen_ts());
1743
1744 Ok(())
1745 }
1746
1747 #[async_test]
1748 async fn test_fallback_key_signature_verification() -> Result<()> {
1749 let fallback_key = json!({
1750 "fallback": true,
1751 "key": "XPFqtLvBepBmW6jSAbBuJbhEpprBhQOX1IjUu+cnMF4",
1752 "signatures": {
1753 "@dkasak_c:matrix.org": {
1754 "ed25519:EXPDYDPWZH": "RJCBMJPL5hvjxgq8rmLmqkNOuPsaan7JeL1wsE+gW6R39G894lb2sBmzapHeKCn/KFjmkonPLkICApRDS+zyDw"
1755 }
1756 }
1757 });
1758
1759 let device_keys = json!({
1760 "algorithms": [
1761 "m.olm.v1.curve25519-aes-sha2",
1762 "m.megolm.v1.aes-sha2"
1763 ],
1764 "device_id": "EXPDYDPWZH",
1765 "keys": {
1766 "curve25519:EXPDYDPWZH": "k7f3igo0Vrdm88JSSA5d3OCuUfHYELChB2b57aOROB8",
1767 "ed25519:EXPDYDPWZH": "GdjYI8fxs175gSpYRJkyN6FRfvcyTsNOhJ2OR/Ggp+E"
1768 },
1769 "signatures": {
1770 "@dkasak_c:matrix.org": {
1771 "ed25519:EXPDYDPWZH": "kzrtfQMbJXWXQ1uzhybtwFnGk0JJBS4Mg8VPMusMu6U8MPJccwoHVZKo5+owuHTzIodI+GZYqLmMSzvfvsChAA"
1772 }
1773 },
1774 "user_id": "@dkasak_c:matrix.org",
1775 "unsigned": {}
1776 });
1777
1778 let device_keys: DeviceKeys = serde_json::from_value(device_keys).unwrap();
1779 let device = DeviceData::try_from(&device_keys).unwrap();
1780 let fallback_key: SignedKey = serde_json::from_value(fallback_key).unwrap();
1781
1782 device
1783 .verify_one_time_key(&fallback_key)
1784 .expect("The fallback key should pass the signature verification");
1785
1786 Ok(())
1787 }
1788
1789 #[test]
1790 fn test_shared_history_flag_from_history_visibility() {
1791 assert!(
1792 shared_history_from_history_visibility(&HistoryVisibility::WorldReadable),
1793 "The world readable visibility should set the shared history flag to true"
1794 );
1795
1796 assert!(
1797 shared_history_from_history_visibility(&HistoryVisibility::Shared),
1798 "The shared visibility should set the shared history flag to true"
1799 );
1800
1801 assert!(
1802 !shared_history_from_history_visibility(&HistoryVisibility::Joined),
1803 "The joined visibility should set the shared history flag to false"
1804 );
1805
1806 assert!(
1807 !shared_history_from_history_visibility(&HistoryVisibility::Invited),
1808 "The invited visibility should set the shared history flag to false"
1809 );
1810
1811 let visibility = HistoryVisibility::from("custom_visibility");
1812 assert!(
1813 !shared_history_from_history_visibility(&visibility),
1814 "A custom visibility should set the shared history flag to false"
1815 );
1816 }
1817
1818 #[async_test]
1819 async fn test_shared_history_set_when_creating_group_sessions() {
1820 let account = Account::new(user_id());
1821 let room_id = room_id!("!room:id");
1822 let settings = EncryptionSettings {
1823 history_visibility: HistoryVisibility::Shared,
1824 ..Default::default()
1825 };
1826
1827 let (_, session) = account
1828 .create_group_session_pair(room_id, settings, Default::default())
1829 .await
1830 .expect("We should be able to create a group session pair");
1831
1832 assert!(
1833 session.shared_history(),
1834 "The shared history flag should have been set when we created the new session"
1835 );
1836 }
1837}