1use std::{
29 borrow::Borrow,
30 collections::{
31 BTreeMap,
32 btree_map::{IntoIter, Iter},
33 },
34};
35
36use matrix_sdk_common::deserialized_responses::PrivOwnedStr;
37use ruma::{DeviceKeyAlgorithm, DeviceKeyId, OwnedDeviceKeyId, RoomId, serde::StringEnum};
38use serde::{Deserialize, Deserializer, Serialize, Serializer};
39use vodozemac::{Curve25519PublicKey, Ed25519PublicKey, KeyError};
40use zeroize::{Zeroize, ZeroizeOnDrop};
41
42mod backup;
43mod cross_signing;
44mod device_keys;
45pub mod events;
46mod one_time_keys;
47pub mod qr_login;
48pub mod requests;
49pub mod room_history;
50mod signatures;
51
52pub use self::{backup::*, cross_signing::*, device_keys::*, one_time_keys::*, signatures::*};
53use crate::store::types::BackupDecryptionKey;
54
55macro_rules! from_base64 {
56 ($foo:ident, $name:ident) => {
57 pub(crate) fn $name<'de, D>(deserializer: D) -> Result<$foo, D::Error>
58 where
59 D: Deserializer<'de>,
60 {
61 let mut string = String::deserialize(deserializer)?;
62
63 let result = $foo::from_base64(&string);
64 string.zeroize();
65
66 result.map_err(serde::de::Error::custom)
67 }
68 };
69}
70
71macro_rules! to_base64 {
72 ($foo:ident, $name:ident) => {
73 pub(crate) fn $name<S>(v: &$foo, serializer: S) -> Result<S::Ok, S::Error>
74 where
75 S: Serializer,
76 {
77 let mut string = v.to_base64();
78 let ret = string.serialize(serializer);
79
80 string.zeroize();
81
82 ret
83 }
84 };
85}
86
87#[derive(Debug, Deserialize, Clone, Serialize, ZeroizeOnDrop)]
90#[cfg_attr(feature = "uniffi", derive(uniffi::Object))]
91pub struct SecretsBundle {
92 pub cross_signing: CrossSigningSecrets,
94 pub backup: Option<BackupSecrets>,
96}
97
98#[cfg_attr(feature = "uniffi", derive(uniffi::Object))]
100#[derive(Deserialize, Clone, Serialize, ZeroizeOnDrop)]
101pub struct CrossSigningSecrets {
102 pub master_key: String,
105 pub user_signing_key: String,
108 pub self_signing_key: String,
111}
112
113impl std::fmt::Debug for CrossSigningSecrets {
114 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
115 f.debug_struct("CrossSigningSecrets")
116 .field("master_key", &"...")
117 .field("user_signing_key", &"...")
118 .field("self_signing_key", &"...")
119 .finish()
120 }
121}
122
123#[derive(Debug, Deserialize, Clone, Serialize, ZeroizeOnDrop)]
126pub struct MegolmBackupV1Curve25519AesSha2Secrets {
127 #[serde(serialize_with = "backup_key_to_base64", deserialize_with = "backup_key_from_base64")]
131 pub key: BackupDecryptionKey,
132 pub backup_version: String,
134}
135
136from_base64!(BackupDecryptionKey, backup_key_from_base64);
137to_base64!(BackupDecryptionKey, backup_key_to_base64);
138
139#[derive(Debug, Clone, ZeroizeOnDrop, Serialize, Deserialize)]
141#[cfg_attr(feature = "uniffi", derive(uniffi::Object))]
142#[serde(tag = "algorithm")]
143pub enum BackupSecrets {
144 #[serde(rename = "m.megolm_backup.v1.curve25519-aes-sha2")]
147 MegolmBackupV1Curve25519AesSha2(MegolmBackupV1Curve25519AesSha2Secrets),
148}
149
150impl BackupSecrets {
151 pub fn algorithm(&self) -> &str {
153 match &self {
154 BackupSecrets::MegolmBackupV1Curve25519AesSha2(_) => {
155 "m.megolm_backup.v1.curve25519-aes-sha2"
156 }
157 }
158 }
159}
160
161#[derive(Debug, Clone, PartialEq, Eq)]
163pub struct SigningKeys<T: Ord>(BTreeMap<T, SigningKey>);
164
165impl<T: Ord> SigningKeys<T> {
166 pub fn new() -> Self {
168 Self(BTreeMap::new())
169 }
170
171 pub fn insert(&mut self, key_id: T, key: SigningKey) -> Option<SigningKey> {
173 self.0.insert(key_id, key)
174 }
175
176 pub fn get<Q>(&self, key_id: &Q) -> Option<&SigningKey>
178 where
179 T: Borrow<Q>,
180 Q: Ord + ?Sized,
181 {
182 self.0.get(key_id)
183 }
184
185 pub fn iter(&self) -> Iter<'_, T, SigningKey> {
187 self.0.iter()
188 }
189
190 pub fn is_empty(&self) -> bool {
192 self.0.is_empty()
193 }
194}
195
196impl<T: Ord> Default for SigningKeys<T> {
197 fn default() -> Self {
198 Self::new()
199 }
200}
201
202impl<T: Ord> IntoIterator for SigningKeys<T> {
203 type Item = (T, SigningKey);
204
205 type IntoIter = IntoIter<T, SigningKey>;
206
207 fn into_iter(self) -> Self::IntoIter {
208 self.0.into_iter()
209 }
210}
211
212impl<K: Ord> FromIterator<(K, SigningKey)> for SigningKeys<K> {
213 fn from_iter<T: IntoIterator<Item = (K, SigningKey)>>(iter: T) -> Self {
214 let map = BTreeMap::from_iter(iter);
215
216 Self(map)
217 }
218}
219
220impl<K: Ord, const N: usize> From<[(K, SigningKey); N]> for SigningKeys<K> {
221 fn from(v: [(K, SigningKey); N]) -> Self {
222 let map = BTreeMap::from(v);
223
224 Self(map)
225 }
226}
227
228trait Algorithm {
232 fn algorithm(&self) -> DeviceKeyAlgorithm;
233}
234
235impl Algorithm for OwnedDeviceKeyId {
236 fn algorithm(&self) -> DeviceKeyAlgorithm {
237 DeviceKeyId::algorithm(self)
238 }
239}
240
241impl Algorithm for DeviceKeyAlgorithm {
242 fn algorithm(&self) -> DeviceKeyAlgorithm {
243 self.to_owned()
244 }
245}
246
247#[derive(Clone, StringEnum)]
249#[non_exhaustive]
250pub enum EventEncryptionAlgorithm {
251 #[ruma_enum(rename = "m.olm.v1.curve25519-aes-sha2")]
253 OlmV1Curve25519AesSha2,
254
255 #[cfg(feature = "experimental-algorithms")]
257 #[ruma_enum(rename = "m.olm.v2.curve25519-aes-sha2")]
258 OlmV2Curve25519AesSha2,
259
260 #[ruma_enum(rename = "m.megolm.v1.aes-sha2")]
262 MegolmV1AesSha2,
263
264 #[cfg(feature = "experimental-algorithms")]
266 #[ruma_enum(rename = "m.megolm.v2.aes-sha2")]
267 MegolmV2AesSha2,
268
269 #[doc(hidden)]
270 _Custom(PrivOwnedStr),
271}
272
273impl<T: Ord + Serialize> Serialize for SigningKeys<T> {
274 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
275 where
276 S: Serializer,
277 {
278 let keys: BTreeMap<&T, String> =
279 self.0.iter().map(|(key_id, key)| (key_id, key.to_base64())).collect();
280
281 keys.serialize(serializer)
282 }
283}
284
285impl<'de, T: Algorithm + Ord + Deserialize<'de>> Deserialize<'de> for SigningKeys<T> {
286 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
287 where
288 D: Deserializer<'de>,
289 {
290 let map: BTreeMap<T, String> = Deserialize::deserialize(deserializer)?;
291
292 let map: Result<_, _> = map
293 .into_iter()
294 .map(|(key_id, key)| {
295 let key = SigningKey::from_parts(&key_id.algorithm(), key)
296 .map_err(serde::de::Error::custom)?;
297
298 Ok((key_id, key))
299 })
300 .collect();
301
302 Ok(SigningKeys(map?))
303 }
304}
305
306from_base64!(Curve25519PublicKey, deserialize_curve_key);
311to_base64!(Curve25519PublicKey, serialize_curve_key);
312
313from_base64!(Ed25519PublicKey, deserialize_ed25519_key);
314to_base64!(Ed25519PublicKey, serialize_ed25519_key);
315
316pub(crate) fn deserialize_curve_key_vec<'de, D>(de: D) -> Result<Vec<Curve25519PublicKey>, D::Error>
317where
318 D: Deserializer<'de>,
319{
320 let keys: Vec<String> = Deserialize::deserialize(de)?;
321 let keys: Result<Vec<Curve25519PublicKey>, KeyError> =
322 keys.iter().map(|k| Curve25519PublicKey::from_base64(k)).collect();
323
324 keys.map_err(serde::de::Error::custom)
325}
326
327pub(crate) fn serialize_curve_key_vec<S>(
328 keys: &[Curve25519PublicKey],
329 s: S,
330) -> Result<S::Ok, S::Error>
331where
332 S: Serializer,
333{
334 let keys: Vec<String> = keys.iter().map(|k| k.to_base64()).collect();
335 keys.serialize(s)
336}
337
338mod serde_curve_key_option {
339 use super::{Curve25519PublicKey, Deserialize, Deserializer, Serialize, Serializer};
340
341 pub(crate) fn deserialize<'de, D>(de: D) -> Result<Option<Curve25519PublicKey>, D::Error>
342 where
343 D: Deserializer<'de>,
344 {
345 let key: Option<String> = Deserialize::deserialize(de)?;
346 key.map(|k| Curve25519PublicKey::from_base64(&k))
347 .transpose()
348 .map_err(serde::de::Error::custom)
349 }
350
351 pub(crate) fn serialize<S>(key: &Option<Curve25519PublicKey>, s: S) -> Result<S::Ok, S::Error>
352 where
353 S: Serializer,
354 {
355 let key = key.as_ref().map(|k| k.to_base64());
356 key.serialize(s)
357 }
358}
359
360pub trait RoomKeyExport {
363 fn room_id(&self) -> &RoomId;
365 fn session_id(&self) -> &str;
367 fn sender_key(&self) -> Curve25519PublicKey;
370}
371
372#[cfg(test)]
373mod test {
374 use insta::{assert_debug_snapshot, assert_json_snapshot};
375 use serde_json::json;
376 use similar_asserts::assert_eq;
377
378 use super::*;
379
380 #[test]
381 fn serialize_secrets_bundle() {
382 let json = json!({
383 "cross_signing": {
384 "master_key": "rTtSv67XGS6k/rg6/yTG/m573cyFTPFRqluFhQY+hSw",
385 "self_signing_key": "4jbPt7jh5D2iyM4U+3IDa+WthgJB87IQN1ATdkau+xk",
386 "user_signing_key": "YkFKtkjcsTxF6UAzIIG/l6Nog/G2RigCRfWj3cjNWeM",
387 },
388 "backup": {
389 "algorithm": "m.megolm_backup.v1.curve25519-aes-sha2",
390 "backup_version": "2",
391 "key": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
392 },
393 });
394
395 let deserialized: SecretsBundle = serde_json::from_value(json.clone())
396 .expect("We should be able to deserialize the secrets bundle");
397
398 let serialized = serde_json::to_value(&deserialized)
399 .expect("We should be able to serialize a secrets bundle");
400
401 assert_eq!(json, serialized, "A serialization cycle should yield the same result");
402 }
403
404 #[test]
405 fn snapshot_backup_decryption_key() {
406 let decryption_key = BackupDecryptionKey { inner: Box::new([1u8; 32]) };
407 assert_json_snapshot!(decryption_key);
408
409 assert_debug_snapshot!(decryption_key);
411 }
412
413 #[test]
414 fn snapshot_secret_bundle() {
415 let secret_bundle = SecretsBundle {
416 cross_signing: CrossSigningSecrets {
417 master_key: "MSKMSKMSKMSKMSKMSKMSKMSKMSKMSKMSKMSK".to_owned(),
418 user_signing_key: "USKUSKUSKUSKUSKUSKUSKUSKUSKUSKUSKUSK".to_owned(),
419 self_signing_key: "SSKSSKSSKSSKSSKSSKSSKSSKSSKSSKSSK".to_owned(),
420 },
421 backup: Some(BackupSecrets::MegolmBackupV1Curve25519AesSha2(
422 MegolmBackupV1Curve25519AesSha2Secrets {
423 key: BackupDecryptionKey::from_bytes(&[0u8; 32]),
424 backup_version: "v1.1".to_owned(),
425 },
426 )),
427 };
428
429 assert_json_snapshot!(secret_bundle);
430
431 let secret_bundle = SecretsBundle {
432 cross_signing: CrossSigningSecrets {
433 master_key: "MSKMSKMSKMSKMSKMSKMSKMSKMSKMSKMSKMSK".to_owned(),
434 user_signing_key: "USKUSKUSKUSKUSKUSKUSKUSKUSKUSKUSKUSK".to_owned(),
435 self_signing_key: "SSKSSKSSKSSKSSKSSKSSKSSKSSKSSKSSK".to_owned(),
436 },
437 backup: None,
438 };
439
440 assert_json_snapshot!(secret_bundle);
441 }
442}