matrix_sdk_crypto/types/
backup.rs1use std::collections::BTreeMap;
16
17use serde::{Deserialize, Serialize};
18use serde_json::Value;
19use vodozemac::Curve25519PublicKey;
20
21use super::{deserialize_curve_key, serialize_curve_key, Signatures};
22
23#[derive(Clone, Debug, Serialize, Deserialize)]
28pub struct MegolmV1AuthData {
29 #[serde(deserialize_with = "deserialize_curve_key", serialize_with = "serialize_curve_key")]
31 pub public_key: Curve25519PublicKey,
32 #[serde(default)]
34 pub signatures: Signatures,
35 #[serde(flatten)]
36 extra: BTreeMap<String, Value>,
37}
38
39impl MegolmV1AuthData {
40 pub(crate) fn new(public_key: Curve25519PublicKey, signatures: Signatures) -> Self {
43 Self { public_key, signatures, extra: Default::default() }
44 }
45}
46
47#[derive(Clone, Debug, Deserialize)]
52#[serde(try_from = "BackupInfoHelper")]
53pub enum RoomKeyBackupInfo {
54 MegolmBackupV1Curve25519AesSha2(MegolmV1AuthData),
56 Other {
58 algorithm: String,
60 auth_data: BTreeMap<String, Value>,
62 },
63}
64
65#[derive(Clone, Debug, Serialize, Deserialize)]
66struct BackupInfoHelper {
67 algorithm: String,
68 auth_data: Value,
69}
70
71impl TryFrom<BackupInfoHelper> for RoomKeyBackupInfo {
72 type Error = serde_json::Error;
73
74 fn try_from(value: BackupInfoHelper) -> Result<Self, Self::Error> {
75 Ok(match value.algorithm.as_str() {
76 "m.megolm_backup.v1.curve25519-aes-sha2" => {
77 let data: MegolmV1AuthData = serde_json::from_value(value.auth_data)?;
78 RoomKeyBackupInfo::MegolmBackupV1Curve25519AesSha2(data)
79 }
80 _ => RoomKeyBackupInfo::Other {
81 algorithm: value.algorithm,
82 auth_data: serde_json::from_value(value.auth_data)?,
83 },
84 })
85 }
86}
87
88impl Serialize for RoomKeyBackupInfo {
89 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
90 where
91 S: serde::Serializer,
92 {
93 let helper = match self {
94 RoomKeyBackupInfo::MegolmBackupV1Curve25519AesSha2(d) => BackupInfoHelper {
95 algorithm: "m.megolm_backup.v1.curve25519-aes-sha2".to_owned(),
96 auth_data: serde_json::to_value(d).map_err(serde::ser::Error::custom)?,
97 },
98 RoomKeyBackupInfo::Other { algorithm, auth_data } => BackupInfoHelper {
99 algorithm: algorithm.to_owned(),
100 auth_data: serde_json::to_value(auth_data.clone())
101 .map_err(serde::ser::Error::custom)?,
102 },
103 };
104
105 helper.serialize(serializer)
106 }
107}
108
109#[cfg(test)]
110mod tests {
111 use std::collections::BTreeMap;
112
113 use assert_matches::assert_matches;
114 use insta::{assert_json_snapshot, with_settings};
115 use ruma::{user_id, DeviceKeyAlgorithm, KeyId};
116 use serde_json::{json, Value};
117 use vodozemac::{Curve25519PublicKey, Ed25519Signature};
118
119 use super::RoomKeyBackupInfo;
120 use crate::types::{MegolmV1AuthData, Signature, Signatures};
121
122 #[test]
123 fn serialization() {
124 let json = json!({
125 "algorithm": "m.megolm_backup.v2",
126 "auth_data": {
127 "some": "data"
128 }
129 });
130
131 let deserialized: RoomKeyBackupInfo = serde_json::from_value(json.clone()).unwrap();
132 assert_matches!(deserialized, RoomKeyBackupInfo::Other { algorithm: _, auth_data: _ });
133
134 let serialized = serde_json::to_value(deserialized).unwrap();
135 assert_eq!(json, serialized);
136
137 let json = json!({
138 "algorithm": "m.megolm_backup.v1.curve25519-aes-sha2",
139 "auth_data": {
140 "public_key":"XjhWTCjW7l59pbfx9tlCBQolfnIQWARoKOzjTOPSlWM",
141 "signatures": {
142 "@alice:example.org": {
143 "ed25519:deviceid": "signature"
144 }
145 }
146 }
147 });
148
149 let deserialized: RoomKeyBackupInfo = serde_json::from_value(json.clone()).unwrap();
150 assert_matches!(deserialized, RoomKeyBackupInfo::MegolmBackupV1Curve25519AesSha2(_));
151
152 let serialized = serde_json::to_value(deserialized).unwrap();
153 assert_eq!(json, serialized);
154 }
155
156 #[test]
157 fn snapshot_room_key_backup_info() {
158 let info = RoomKeyBackupInfo::MegolmBackupV1Curve25519AesSha2(MegolmV1AuthData {
159 public_key: Curve25519PublicKey::from_bytes([2u8; 32]),
160 signatures: Signatures(BTreeMap::from([(
161 user_id!("@alice:localhost").to_owned(),
162 BTreeMap::from([(
163 KeyId::from_parts(DeviceKeyAlgorithm::Ed25519, "ABCDEFG".into()),
164 Ok(Signature::from(Ed25519Signature::from_slice(&[0u8; 64]).unwrap())),
165 )]),
166 )])),
167 extra: BTreeMap::from([("foo".to_owned(), Value::from("bar"))]),
168 });
169
170 with_settings!({sort_maps =>true}, {
171 assert_json_snapshot!(info)
172 });
173
174 let info = RoomKeyBackupInfo::Other {
175 algorithm: "caesar.cipher".to_owned(),
176 auth_data: BTreeMap::from([("foo".to_owned(), Value::from("bar"))]),
177 };
178
179 with_settings!({sort_maps =>true}, {
180 assert_json_snapshot!(info);
181 })
182 }
183}