1use std::{
21 collections::{BTreeMap, HashMap},
22 time::Duration,
23};
24
25use ruma::{OwnedDeviceId, OwnedRoomId, OwnedUserId};
26use serde::{Deserialize, Serialize};
27use vodozemac::{base64_encode, Curve25519PublicKey};
28use zeroize::{Zeroize, ZeroizeOnDrop};
29
30use super::{DehydrationError, GossipRequest};
31use crate::{
32 olm::{
33 InboundGroupSession, OlmMessageHash, OutboundGroupSession, PrivateCrossSigningIdentity,
34 SenderData,
35 },
36 types::{
37 events::{room_key_bundle::RoomKeyBundleContent, room_key_withheld::RoomKeyWithheldEvent},
38 EventEncryptionAlgorithm,
39 },
40 Account, Device, DeviceData, GossippedSecret, Session, UserIdentity, UserIdentityData,
41};
42
43#[derive(Default, Debug)]
49#[allow(missing_docs)]
50pub struct PendingChanges {
51 pub account: Option<Account>,
52}
53
54impl PendingChanges {
55 pub fn is_empty(&self) -> bool {
57 self.account.is_none()
58 }
59}
60
61#[derive(Default, Debug)]
64#[allow(missing_docs)]
65pub struct Changes {
66 pub private_identity: Option<PrivateCrossSigningIdentity>,
67 pub backup_version: Option<String>,
68 pub backup_decryption_key: Option<BackupDecryptionKey>,
69 pub dehydrated_device_pickle_key: Option<DehydratedDeviceKey>,
70 pub sessions: Vec<Session>,
71 pub message_hashes: Vec<OlmMessageHash>,
72 pub inbound_group_sessions: Vec<InboundGroupSession>,
73 pub outbound_group_sessions: Vec<OutboundGroupSession>,
74 pub key_requests: Vec<GossipRequest>,
75 pub identities: IdentityChanges,
76 pub devices: DeviceChanges,
77 pub withheld_session_info: BTreeMap<OwnedRoomId, BTreeMap<String, RoomKeyWithheldEvent>>,
79 pub room_settings: HashMap<OwnedRoomId, RoomSettings>,
80 pub secrets: Vec<GossippedSecret>,
81 pub next_batch_token: Option<String>,
82
83 pub received_room_key_bundles: Vec<StoredRoomKeyBundleData>,
86}
87
88#[derive(Clone, Debug, Serialize, Deserialize)]
92pub struct StoredRoomKeyBundleData {
93 pub sender_user: OwnedUserId,
95
96 pub sender_key: Curve25519PublicKey,
98
99 pub sender_data: SenderData,
102
103 pub bundle_data: RoomKeyBundleContent,
105}
106
107#[derive(Clone, Debug, Serialize, Deserialize)]
109pub struct TrackedUser {
110 pub user_id: OwnedUserId,
112 pub dirty: bool,
117}
118
119impl Changes {
120 pub fn is_empty(&self) -> bool {
122 self.private_identity.is_none()
123 && self.backup_version.is_none()
124 && self.backup_decryption_key.is_none()
125 && self.dehydrated_device_pickle_key.is_none()
126 && self.sessions.is_empty()
127 && self.message_hashes.is_empty()
128 && self.inbound_group_sessions.is_empty()
129 && self.outbound_group_sessions.is_empty()
130 && self.key_requests.is_empty()
131 && self.identities.is_empty()
132 && self.devices.is_empty()
133 && self.withheld_session_info.is_empty()
134 && self.room_settings.is_empty()
135 && self.secrets.is_empty()
136 && self.next_batch_token.is_none()
137 && self.received_room_key_bundles.is_empty()
138 }
139}
140
141#[derive(Debug, Clone, Default)]
152#[allow(missing_docs)]
153pub struct IdentityChanges {
154 pub new: Vec<UserIdentityData>,
155 pub changed: Vec<UserIdentityData>,
156 pub unchanged: Vec<UserIdentityData>,
157}
158
159impl IdentityChanges {
160 pub(super) fn is_empty(&self) -> bool {
161 self.new.is_empty() && self.changed.is_empty()
162 }
163
164 pub(super) fn into_maps(
167 self,
168 ) -> (
169 BTreeMap<OwnedUserId, UserIdentityData>,
170 BTreeMap<OwnedUserId, UserIdentityData>,
171 BTreeMap<OwnedUserId, UserIdentityData>,
172 ) {
173 let new: BTreeMap<_, _> = self
174 .new
175 .into_iter()
176 .map(|identity| (identity.user_id().to_owned(), identity))
177 .collect();
178
179 let changed: BTreeMap<_, _> = self
180 .changed
181 .into_iter()
182 .map(|identity| (identity.user_id().to_owned(), identity))
183 .collect();
184
185 let unchanged: BTreeMap<_, _> = self
186 .unchanged
187 .into_iter()
188 .map(|identity| (identity.user_id().to_owned(), identity))
189 .collect();
190
191 (new, changed, unchanged)
192 }
193}
194
195#[derive(Debug, Clone, Default)]
196#[allow(missing_docs)]
197pub struct DeviceChanges {
198 pub new: Vec<DeviceData>,
199 pub changed: Vec<DeviceData>,
200 pub deleted: Vec<DeviceData>,
201}
202
203#[derive(Clone, Debug, Default)]
206pub struct DeviceUpdates {
207 pub new: BTreeMap<OwnedUserId, BTreeMap<OwnedDeviceId, Device>>,
213 pub changed: BTreeMap<OwnedUserId, BTreeMap<OwnedDeviceId, Device>>,
215}
216
217#[derive(Clone, Debug, Default)]
220pub struct IdentityUpdates {
221 pub new: BTreeMap<OwnedUserId, UserIdentity>,
227 pub changed: BTreeMap<OwnedUserId, UserIdentity>,
229 pub unchanged: BTreeMap<OwnedUserId, UserIdentity>,
231}
232
233#[derive(Clone, Zeroize, ZeroizeOnDrop, Deserialize, Serialize)]
243#[serde(transparent)]
244pub struct BackupDecryptionKey {
245 pub(crate) inner: Box<[u8; BackupDecryptionKey::KEY_SIZE]>,
246}
247
248impl BackupDecryptionKey {
249 pub const KEY_SIZE: usize = 32;
251
252 pub fn new() -> Result<Self, rand::Error> {
254 let mut rng = rand::thread_rng();
255
256 let mut key = Box::new([0u8; Self::KEY_SIZE]);
257 rand::Fill::try_fill(key.as_mut_slice(), &mut rng)?;
258
259 Ok(Self { inner: key })
260 }
261
262 pub fn to_base64(&self) -> String {
264 base64_encode(self.inner.as_slice())
265 }
266}
267
268#[cfg(not(tarpaulin_include))]
269impl std::fmt::Debug for BackupDecryptionKey {
270 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
271 f.debug_tuple("BackupDecryptionKey").field(&"...").finish()
272 }
273}
274
275#[derive(Clone, Zeroize, ZeroizeOnDrop, Deserialize, Serialize)]
280#[serde(transparent)]
281pub struct DehydratedDeviceKey {
282 pub(crate) inner: Box<[u8; DehydratedDeviceKey::KEY_SIZE]>,
283}
284
285impl DehydratedDeviceKey {
286 pub const KEY_SIZE: usize = 32;
288
289 pub fn new() -> Result<Self, rand::Error> {
291 let mut rng = rand::thread_rng();
292
293 let mut key = Box::new([0u8; Self::KEY_SIZE]);
294 rand::Fill::try_fill(key.as_mut_slice(), &mut rng)?;
295
296 Ok(Self { inner: key })
297 }
298
299 pub fn from_slice(slice: &[u8]) -> Result<Self, DehydrationError> {
303 if slice.len() == 32 {
304 let mut key = Box::new([0u8; 32]);
305 key.copy_from_slice(slice);
306 Ok(DehydratedDeviceKey { inner: key })
307 } else {
308 Err(DehydrationError::PickleKeyLength(slice.len()))
309 }
310 }
311
312 pub fn from_bytes(raw_key: &[u8; 32]) -> Self {
314 let mut inner = Box::new([0u8; Self::KEY_SIZE]);
315 inner.copy_from_slice(raw_key);
316
317 Self { inner }
318 }
319
320 pub fn to_base64(&self) -> String {
322 base64_encode(self.inner.as_slice())
323 }
324}
325
326impl From<&[u8; 32]> for DehydratedDeviceKey {
327 fn from(value: &[u8; 32]) -> Self {
328 DehydratedDeviceKey { inner: Box::new(*value) }
329 }
330}
331
332impl From<DehydratedDeviceKey> for Vec<u8> {
333 fn from(key: DehydratedDeviceKey) -> Self {
334 key.inner.to_vec()
335 }
336}
337
338#[cfg(not(tarpaulin_include))]
339impl std::fmt::Debug for DehydratedDeviceKey {
340 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
341 f.debug_tuple("DehydratedDeviceKey").field(&"...").finish()
342 }
343}
344
345impl DeviceChanges {
346 pub fn extend(&mut self, other: DeviceChanges) {
348 self.new.extend(other.new);
349 self.changed.extend(other.changed);
350 self.deleted.extend(other.deleted);
351 }
352
353 pub fn is_empty(&self) -> bool {
355 self.new.is_empty() && self.changed.is_empty() && self.deleted.is_empty()
356 }
357}
358
359#[derive(Debug, Clone, Default)]
361pub struct RoomKeyCounts {
362 pub total: usize,
364 pub backed_up: usize,
366}
367
368#[derive(Default, Clone, Debug)]
370pub struct BackupKeys {
371 pub decryption_key: Option<BackupDecryptionKey>,
373 pub backup_version: Option<String>,
375}
376
377#[derive(Default, Zeroize, ZeroizeOnDrop)]
380pub struct CrossSigningKeyExport {
381 pub master_key: Option<String>,
383 pub self_signing_key: Option<String>,
385 pub user_signing_key: Option<String>,
387}
388
389#[cfg(not(tarpaulin_include))]
390impl std::fmt::Debug for CrossSigningKeyExport {
391 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
392 f.debug_struct("CrossSigningKeyExport")
393 .field("master_key", &self.master_key.is_some())
394 .field("self_signing_key", &self.self_signing_key.is_some())
395 .field("user_signing_key", &self.user_signing_key.is_some())
396 .finish_non_exhaustive()
397 }
398}
399
400#[derive(Clone, Copy, Debug, PartialEq, Eq)]
403pub(crate) enum UserKeyQueryResult {
404 WasPending,
405 WasNotPending,
406
407 TimeoutExpired,
409}
410
411#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
413pub struct RoomSettings {
414 pub algorithm: EventEncryptionAlgorithm,
416
417 #[cfg(feature = "experimental-encrypted-state-events")]
419 #[serde(default)]
420 pub encrypt_state_events: bool,
421
422 pub only_allow_trusted_devices: bool,
425
426 pub session_rotation_period: Option<Duration>,
429
430 pub session_rotation_period_messages: Option<usize>,
433}
434
435impl Default for RoomSettings {
436 fn default() -> Self {
437 Self {
438 algorithm: EventEncryptionAlgorithm::MegolmV1AesSha2,
439 #[cfg(feature = "experimental-encrypted-state-events")]
440 encrypt_state_events: false,
441 only_allow_trusted_devices: false,
442 session_rotation_period: None,
443 session_rotation_period_messages: None,
444 }
445 }
446}
447
448#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
450pub struct RoomKeyInfo {
451 pub algorithm: EventEncryptionAlgorithm,
456
457 pub room_id: OwnedRoomId,
459
460 pub sender_key: Curve25519PublicKey,
462
463 pub session_id: String,
465}
466
467impl From<&InboundGroupSession> for RoomKeyInfo {
468 fn from(group_session: &InboundGroupSession) -> Self {
469 RoomKeyInfo {
470 algorithm: group_session.algorithm().clone(),
471 room_id: group_session.room_id().to_owned(),
472 sender_key: group_session.sender_key(),
473 session_id: group_session.session_id().to_owned(),
474 }
475 }
476}
477
478#[derive(Clone, Debug, Deserialize, Serialize)]
480pub struct RoomKeyWithheldInfo {
481 pub room_id: OwnedRoomId,
483
484 pub session_id: String,
486
487 pub withheld_event: RoomKeyWithheldEvent,
490}
491
492#[derive(Debug, Clone)]
500pub struct RoomKeyBundleInfo {
501 pub sender: OwnedUserId,
503
504 pub sender_key: Curve25519PublicKey,
506
507 pub room_id: OwnedRoomId,
509}
510
511impl From<&StoredRoomKeyBundleData> for RoomKeyBundleInfo {
512 fn from(value: &StoredRoomKeyBundleData) -> Self {
513 let StoredRoomKeyBundleData { sender_user, sender_data: _, bundle_data, sender_key } =
514 value;
515 let sender_key = *sender_key;
516
517 Self { sender: sender_user.clone(), room_id: bundle_data.room_id.clone(), sender_key }
518 }
519}