1use std::{
29 borrow::Borrow,
30 collections::{
31 btree_map::{IntoIter, Iter},
32 BTreeMap,
33 },
34};
35
36use as_variant::as_variant;
37use matrix_sdk_common::deserialized_responses::PrivOwnedStr;
38use ruma::{
39 serde::StringEnum, DeviceKeyAlgorithm, DeviceKeyId, OwnedDeviceKeyId, OwnedUserId, UserId,
40};
41use serde::{Deserialize, Deserializer, Serialize, Serializer};
42use vodozemac::{Curve25519PublicKey, Ed25519PublicKey, Ed25519Signature, KeyError};
43use zeroize::{Zeroize, ZeroizeOnDrop};
44
45mod backup;
46mod cross_signing;
47mod device_keys;
48pub mod events;
49mod one_time_keys;
50pub mod qr_login;
51pub mod requests;
52pub mod room_history;
53
54pub use self::{backup::*, cross_signing::*, device_keys::*, one_time_keys::*};
55use crate::store::BackupDecryptionKey;
56
57macro_rules! from_base64 {
58 ($foo:ident, $name:ident) => {
59 pub(crate) fn $name<'de, D>(deserializer: D) -> Result<$foo, D::Error>
60 where
61 D: Deserializer<'de>,
62 {
63 let mut string = String::deserialize(deserializer)?;
64
65 let result = $foo::from_base64(&string);
66 string.zeroize();
67
68 result.map_err(serde::de::Error::custom)
69 }
70 };
71}
72
73macro_rules! to_base64 {
74 ($foo:ident, $name:ident) => {
75 pub(crate) fn $name<S>(v: &$foo, serializer: S) -> Result<S::Ok, S::Error>
76 where
77 S: Serializer,
78 {
79 let mut string = v.to_base64();
80 let ret = string.serialize(serializer);
81
82 string.zeroize();
83
84 ret
85 }
86 };
87}
88
89#[derive(Debug, Deserialize, Clone, Serialize, ZeroizeOnDrop)]
92pub struct SecretsBundle {
93 pub cross_signing: CrossSigningSecrets,
95 pub backup: Option<BackupSecrets>,
97}
98
99#[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#[serde(tag = "algorithm")]
142pub enum BackupSecrets {
143 #[serde(rename = "m.megolm_backup.v1.curve25519-aes-sha2")]
146 MegolmBackupV1Curve25519AesSha2(MegolmBackupV1Curve25519AesSha2Secrets),
147}
148
149impl BackupSecrets {
150 pub fn algorithm(&self) -> &str {
152 match &self {
153 BackupSecrets::MegolmBackupV1Curve25519AesSha2(_) => {
154 "m.megolm_backup.v1.curve25519-aes-sha2"
155 }
156 }
157 }
158}
159
160#[derive(Clone, Debug, PartialEq, Eq)]
171pub enum Signature {
172 Ed25519(Ed25519Signature),
174 Other(String),
177}
178
179#[derive(Debug, Clone, PartialEq, Eq)]
183pub struct InvalidSignature {
184 pub source: String,
187}
188
189impl Signature {
190 pub fn ed25519(&self) -> Option<Ed25519Signature> {
192 as_variant!(self, Self::Ed25519).copied()
193 }
194
195 pub fn to_base64(&self) -> String {
197 match self {
198 Signature::Ed25519(s) => s.to_base64(),
199 Signature::Other(s) => s.to_owned(),
200 }
201 }
202}
203
204impl From<Ed25519Signature> for Signature {
205 fn from(signature: Ed25519Signature) -> Self {
206 Self::Ed25519(signature)
207 }
208}
209
210#[derive(Debug, Clone, PartialEq, Eq)]
212pub struct Signatures(
213 BTreeMap<OwnedUserId, BTreeMap<OwnedDeviceKeyId, Result<Signature, InvalidSignature>>>,
214);
215
216impl Signatures {
217 pub fn new() -> Self {
219 Signatures(Default::default())
220 }
221
222 pub fn add_signature(
225 &mut self,
226 signer: OwnedUserId,
227 key_id: OwnedDeviceKeyId,
228 signature: Ed25519Signature,
229 ) -> Option<Result<Signature, InvalidSignature>> {
230 self.0.entry(signer).or_default().insert(key_id, Ok(signature.into()))
231 }
232
233 pub fn get_signature(&self, signer: &UserId, key_id: &DeviceKeyId) -> Option<Ed25519Signature> {
236 self.get(signer)?.get(key_id)?.as_ref().ok()?.ed25519()
237 }
238
239 pub fn get(
241 &self,
242 signer: &UserId,
243 ) -> Option<&BTreeMap<OwnedDeviceKeyId, Result<Signature, InvalidSignature>>> {
244 self.0.get(signer)
245 }
246
247 pub fn clear(&mut self) {
249 self.0.clear()
250 }
251
252 pub fn is_empty(&self) -> bool {
254 self.0.is_empty()
255 }
256
257 pub fn signature_count(&self) -> usize {
259 self.0.values().map(|u| u.len()).sum()
260 }
261}
262
263impl Default for Signatures {
264 fn default() -> Self {
265 Self::new()
266 }
267}
268
269impl IntoIterator for Signatures {
270 type Item = (OwnedUserId, BTreeMap<OwnedDeviceKeyId, Result<Signature, InvalidSignature>>);
271
272 type IntoIter =
273 IntoIter<OwnedUserId, BTreeMap<OwnedDeviceKeyId, Result<Signature, InvalidSignature>>>;
274
275 fn into_iter(self) -> Self::IntoIter {
276 self.0.into_iter()
277 }
278}
279
280impl<'de> Deserialize<'de> for Signatures {
281 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
282 where
283 D: Deserializer<'de>,
284 {
285 let map: BTreeMap<OwnedUserId, BTreeMap<OwnedDeviceKeyId, String>> =
286 Deserialize::deserialize(deserializer)?;
287
288 let map = map
289 .into_iter()
290 .map(|(user, signatures)| {
291 let signatures = signatures
292 .into_iter()
293 .map(|(key_id, s)| {
294 let algorithm = key_id.algorithm();
295 let signature = match algorithm {
296 DeviceKeyAlgorithm::Ed25519 => Ed25519Signature::from_base64(&s)
297 .map(|s| s.into())
298 .map_err(|_| InvalidSignature { source: s }),
299 _ => Ok(Signature::Other(s)),
300 };
301
302 Ok((key_id, signature))
303 })
304 .collect::<Result<BTreeMap<_, _>, _>>()?;
305
306 Ok((user, signatures))
307 })
308 .collect::<Result<_, _>>()?;
309
310 Ok(Signatures(map))
311 }
312}
313
314impl Serialize for Signatures {
315 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
316 where
317 S: Serializer,
318 {
319 let signatures: BTreeMap<&OwnedUserId, BTreeMap<&OwnedDeviceKeyId, String>> = self
320 .0
321 .iter()
322 .map(|(u, m)| {
323 (
324 u,
325 m.iter()
326 .map(|(d, s)| {
327 (
328 d,
329 match s {
330 Ok(s) => s.to_base64(),
331 Err(i) => i.source.to_owned(),
332 },
333 )
334 })
335 .collect(),
336 )
337 })
338 .collect();
339
340 Serialize::serialize(&signatures, serializer)
341 }
342}
343
344#[derive(Debug, Clone, PartialEq, Eq)]
346pub struct SigningKeys<T: Ord>(BTreeMap<T, SigningKey>);
347
348impl<T: Ord> SigningKeys<T> {
349 pub fn new() -> Self {
351 Self(BTreeMap::new())
352 }
353
354 pub fn insert(&mut self, key_id: T, key: SigningKey) -> Option<SigningKey> {
356 self.0.insert(key_id, key)
357 }
358
359 pub fn get<Q>(&self, key_id: &Q) -> Option<&SigningKey>
361 where
362 T: Borrow<Q>,
363 Q: Ord + ?Sized,
364 {
365 self.0.get(key_id)
366 }
367
368 pub fn iter(&self) -> Iter<'_, T, SigningKey> {
370 self.0.iter()
371 }
372
373 pub fn is_empty(&self) -> bool {
375 self.0.is_empty()
376 }
377}
378
379impl<T: Ord> Default for SigningKeys<T> {
380 fn default() -> Self {
381 Self::new()
382 }
383}
384
385impl<T: Ord> IntoIterator for SigningKeys<T> {
386 type Item = (T, SigningKey);
387
388 type IntoIter = IntoIter<T, SigningKey>;
389
390 fn into_iter(self) -> Self::IntoIter {
391 self.0.into_iter()
392 }
393}
394
395impl<K: Ord> FromIterator<(K, SigningKey)> for SigningKeys<K> {
396 fn from_iter<T: IntoIterator<Item = (K, SigningKey)>>(iter: T) -> Self {
397 let map = BTreeMap::from_iter(iter);
398
399 Self(map)
400 }
401}
402
403impl<K: Ord, const N: usize> From<[(K, SigningKey); N]> for SigningKeys<K> {
404 fn from(v: [(K, SigningKey); N]) -> Self {
405 let map = BTreeMap::from(v);
406
407 Self(map)
408 }
409}
410
411trait Algorithm {
415 fn algorithm(&self) -> DeviceKeyAlgorithm;
416}
417
418impl Algorithm for OwnedDeviceKeyId {
419 fn algorithm(&self) -> DeviceKeyAlgorithm {
420 DeviceKeyId::algorithm(self)
421 }
422}
423
424impl Algorithm for DeviceKeyAlgorithm {
425 fn algorithm(&self) -> DeviceKeyAlgorithm {
426 self.to_owned()
427 }
428}
429
430#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, StringEnum)]
432#[non_exhaustive]
433pub enum EventEncryptionAlgorithm {
434 #[ruma_enum(rename = "m.olm.v1.curve25519-aes-sha2")]
436 OlmV1Curve25519AesSha2,
437
438 #[cfg(feature = "experimental-algorithms")]
440 #[ruma_enum(rename = "m.olm.v2.curve25519-aes-sha2")]
441 OlmV2Curve25519AesSha2,
442
443 #[ruma_enum(rename = "m.megolm.v1.aes-sha2")]
445 MegolmV1AesSha2,
446
447 #[cfg(feature = "experimental-algorithms")]
449 #[ruma_enum(rename = "m.megolm.v2.aes-sha2")]
450 MegolmV2AesSha2,
451
452 #[doc(hidden)]
453 _Custom(PrivOwnedStr),
454}
455
456impl<T: Ord + Serialize> Serialize for SigningKeys<T> {
457 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
458 where
459 S: Serializer,
460 {
461 let keys: BTreeMap<&T, String> =
462 self.0.iter().map(|(key_id, key)| (key_id, key.to_base64())).collect();
463
464 keys.serialize(serializer)
465 }
466}
467
468impl<'de, T: Algorithm + Ord + Deserialize<'de>> Deserialize<'de> for SigningKeys<T> {
469 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
470 where
471 D: Deserializer<'de>,
472 {
473 let map: BTreeMap<T, String> = Deserialize::deserialize(deserializer)?;
474
475 let map: Result<_, _> = map
476 .into_iter()
477 .map(|(key_id, key)| {
478 let key = SigningKey::from_parts(&key_id.algorithm(), key)
479 .map_err(serde::de::Error::custom)?;
480
481 Ok((key_id, key))
482 })
483 .collect();
484
485 Ok(SigningKeys(map?))
486 }
487}
488
489from_base64!(Curve25519PublicKey, deserialize_curve_key);
494to_base64!(Curve25519PublicKey, serialize_curve_key);
495
496from_base64!(Ed25519PublicKey, deserialize_ed25519_key);
497to_base64!(Ed25519PublicKey, serialize_ed25519_key);
498
499pub(crate) fn deserialize_curve_key_vec<'de, D>(de: D) -> Result<Vec<Curve25519PublicKey>, D::Error>
500where
501 D: Deserializer<'de>,
502{
503 let keys: Vec<String> = Deserialize::deserialize(de)?;
504 let keys: Result<Vec<Curve25519PublicKey>, KeyError> =
505 keys.iter().map(|k| Curve25519PublicKey::from_base64(k)).collect();
506
507 keys.map_err(serde::de::Error::custom)
508}
509
510pub(crate) fn serialize_curve_key_vec<S>(
511 keys: &[Curve25519PublicKey],
512 s: S,
513) -> Result<S::Ok, S::Error>
514where
515 S: Serializer,
516{
517 let keys: Vec<String> = keys.iter().map(|k| k.to_base64()).collect();
518 keys.serialize(s)
519}
520
521#[cfg(test)]
522mod test {
523 use insta::{assert_debug_snapshot, assert_json_snapshot, with_settings};
524 use ruma::{device_id, user_id};
525 use serde_json::json;
526 use similar_asserts::assert_eq;
527
528 use super::*;
529
530 #[test]
531 fn serialize_secrets_bundle() {
532 let json = json!({
533 "cross_signing": {
534 "master_key": "rTtSv67XGS6k/rg6/yTG/m573cyFTPFRqluFhQY+hSw",
535 "self_signing_key": "4jbPt7jh5D2iyM4U+3IDa+WthgJB87IQN1ATdkau+xk",
536 "user_signing_key": "YkFKtkjcsTxF6UAzIIG/l6Nog/G2RigCRfWj3cjNWeM",
537 },
538 "backup": {
539 "algorithm": "m.megolm_backup.v1.curve25519-aes-sha2",
540 "backup_version": "2",
541 "key": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
542 },
543 });
544
545 let deserialized: SecretsBundle = serde_json::from_value(json.clone())
546 .expect("We should be able to deserialize the secrets bundle");
547
548 let serialized = serde_json::to_value(&deserialized)
549 .expect("We should be able to serialize a secrets bundle");
550
551 assert_eq!(json, serialized, "A serialization cycle should yield the same result");
552 }
553
554 #[test]
555 fn snapshot_backup_decryption_key() {
556 let decryption_key = BackupDecryptionKey { inner: Box::new([1u8; 32]) };
557 assert_json_snapshot!(decryption_key);
558
559 assert_debug_snapshot!(decryption_key);
561 }
562
563 #[test]
564 fn snapshot_signatures() {
565 let signatures = Signatures(BTreeMap::from([
566 (
567 user_id!("@alice:localhost").to_owned(),
568 BTreeMap::from([
569 (
570 DeviceKeyId::from_parts(
571 DeviceKeyAlgorithm::Ed25519,
572 device_id!("ABCDEFGH"),
573 ),
574 Ok(Signature::from(Ed25519Signature::from_slice(&[0u8; 64]).unwrap())),
575 ),
576 (
577 DeviceKeyId::from_parts(
578 DeviceKeyAlgorithm::Curve25519,
579 device_id!("IJKLMNOP"),
580 ),
581 Ok(Signature::from(Ed25519Signature::from_slice(&[1u8; 64]).unwrap())),
582 ),
583 ]),
584 ),
585 (
586 user_id!("@bob:localhost").to_owned(),
587 BTreeMap::from([(
588 DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, device_id!("ABCDEFGH")),
589 Err(InvalidSignature { source: "SOME+B64+SOME+B64+SOME+B64+==".to_owned() }),
590 )]),
591 ),
592 ]));
593
594 with_settings!({sort_maps =>true}, {
595 assert_json_snapshot!(signatures)
596 });
597 }
598
599 #[test]
600 fn snapshot_secret_bundle() {
601 let secret_bundle = SecretsBundle {
602 cross_signing: CrossSigningSecrets {
603 master_key: "MSKMSKMSKMSKMSKMSKMSKMSKMSKMSKMSKMSK".to_owned(),
604 user_signing_key: "USKUSKUSKUSKUSKUSKUSKUSKUSKUSKUSKUSK".to_owned(),
605 self_signing_key: "SSKSSKSSKSSKSSKSSKSSKSSKSSKSSKSSK".to_owned(),
606 },
607 backup: Some(BackupSecrets::MegolmBackupV1Curve25519AesSha2(
608 MegolmBackupV1Curve25519AesSha2Secrets {
609 key: BackupDecryptionKey::from_bytes(&[0u8; 32]),
610 backup_version: "v1.1".to_owned(),
611 },
612 )),
613 };
614
615 assert_json_snapshot!(secret_bundle);
616
617 let secret_bundle = SecretsBundle {
618 cross_signing: CrossSigningSecrets {
619 master_key: "MSKMSKMSKMSKMSKMSKMSKMSKMSKMSKMSKMSK".to_owned(),
620 user_signing_key: "USKUSKUSKUSKUSKUSKUSKUSKUSKUSKUSKUSK".to_owned(),
621 self_signing_key: "SSKSSKSSKSSKSSKSSKSSKSSKSSKSSKSSK".to_owned(),
622 },
623 backup: None,
624 };
625
626 assert_json_snapshot!(secret_bundle);
627 }
628}