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