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