1use std::collections::BTreeMap;
22
23use js_option::JsOption;
24use ruma::{
25 serde::{JsonCastable, Raw},
26 DeviceKeyAlgorithm, DeviceKeyId, OwnedDeviceId, OwnedDeviceKeyId, OwnedUserId,
27};
28use serde::{Deserialize, Serialize};
29use serde_json::{value::to_raw_value, Value};
30use vodozemac::{Curve25519PublicKey, Ed25519PublicKey};
31
32use super::{EventEncryptionAlgorithm, Signatures};
33
34#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
50#[serde(try_from = "DeviceKeyHelper", into = "DeviceKeyHelper")]
51pub struct DeviceKeys {
52 pub user_id: OwnedUserId,
56
57 pub device_id: OwnedDeviceId,
61
62 pub algorithms: Vec<EventEncryptionAlgorithm>,
64
65 pub keys: BTreeMap<OwnedDeviceKeyId, DeviceKey>,
67
68 pub signatures: Signatures,
70
71 #[serde(default, skip_serializing_if = "JsOption::is_undefined")]
73 pub dehydrated: JsOption<bool>,
74
75 #[serde(default, skip_serializing_if = "UnsignedDeviceInfo::is_empty")]
78 pub unsigned: UnsignedDeviceInfo,
79
80 #[serde(flatten)]
81 other: BTreeMap<String, Value>,
82}
83
84impl DeviceKeys {
85 pub fn new(
88 user_id: OwnedUserId,
89 device_id: OwnedDeviceId,
90 algorithms: Vec<EventEncryptionAlgorithm>,
91 keys: BTreeMap<OwnedDeviceKeyId, DeviceKey>,
92 signatures: Signatures,
93 ) -> Self {
94 Self {
95 user_id,
96 device_id,
97 algorithms,
98 keys,
99 signatures,
100 dehydrated: JsOption::Undefined,
101 unsigned: Default::default(),
102 other: BTreeMap::new(),
103 }
104 }
105
106 pub fn to_raw<T>(&self) -> Raw<T> {
108 Raw::from_json(to_raw_value(&self).expect("Couldn't serialize device keys"))
109 }
110
111 pub fn get_key(&self, algorithm: DeviceKeyAlgorithm) -> Option<&DeviceKey> {
113 self.keys.get(&DeviceKeyId::from_parts(algorithm, &self.device_id))
114 }
115
116 pub fn curve25519_key(&self) -> Option<Curve25519PublicKey> {
118 self.get_key(DeviceKeyAlgorithm::Curve25519).and_then(|k| {
119 if let DeviceKey::Curve25519(k) = k {
120 Some(*k)
121 } else {
122 None
123 }
124 })
125 }
126
127 pub fn ed25519_key(&self) -> Option<Ed25519PublicKey> {
129 self.get_key(DeviceKeyAlgorithm::Ed25519).and_then(|k| {
130 if let DeviceKey::Ed25519(k) = k {
131 Some(*k)
132 } else {
133 None
134 }
135 })
136 }
137}
138
139impl JsonCastable<DeviceKeys> for ruma::encryption::DeviceKeys {}
140
141#[derive(Clone, Debug, Default, Deserialize, Serialize, PartialEq)]
143pub struct UnsignedDeviceInfo {
144 #[serde(skip_serializing_if = "Option::is_none")]
146 pub device_display_name: Option<String>,
147
148 #[serde(flatten)]
149 other: BTreeMap<String, Value>,
150}
151
152impl UnsignedDeviceInfo {
153 pub fn new() -> Self {
155 Default::default()
156 }
157
158 pub fn is_empty(&self) -> bool {
160 self.device_display_name.is_none()
161 }
162}
163
164#[derive(Clone, Debug, PartialEq, Eq)]
170pub enum DeviceKey {
171 Curve25519(Curve25519PublicKey),
173 Ed25519(Ed25519PublicKey),
175 Unknown(String),
177}
178
179impl DeviceKey {
180 pub fn to_base64(&self) -> String {
182 match self {
183 DeviceKey::Curve25519(k) => k.to_base64(),
184 DeviceKey::Ed25519(k) => k.to_base64(),
185 DeviceKey::Unknown(k) => k.to_owned(),
186 }
187 }
188}
189
190impl From<Curve25519PublicKey> for DeviceKey {
191 fn from(val: Curve25519PublicKey) -> Self {
192 DeviceKey::Curve25519(val)
193 }
194}
195
196impl From<Ed25519PublicKey> for DeviceKey {
197 fn from(val: Ed25519PublicKey) -> Self {
198 DeviceKey::Ed25519(val)
199 }
200}
201
202#[derive(Clone, Debug, Deserialize, Serialize)]
205struct DeviceKeyHelper {
206 pub user_id: OwnedUserId,
207 pub device_id: OwnedDeviceId,
208 pub algorithms: Vec<EventEncryptionAlgorithm>,
209 pub keys: BTreeMap<OwnedDeviceKeyId, String>,
210 #[serde(default, skip_serializing_if = "JsOption::is_undefined")]
211 pub dehydrated: JsOption<bool>,
212 pub signatures: Signatures,
213 #[serde(default, skip_serializing_if = "UnsignedDeviceInfo::is_empty")]
214 pub unsigned: UnsignedDeviceInfo,
215 #[serde(flatten)]
216 other: BTreeMap<String, Value>,
217}
218
219impl TryFrom<DeviceKeyHelper> for DeviceKeys {
220 type Error = vodozemac::KeyError;
221
222 fn try_from(value: DeviceKeyHelper) -> Result<Self, Self::Error> {
223 let keys: Result<BTreeMap<OwnedDeviceKeyId, DeviceKey>, vodozemac::KeyError> = value
224 .keys
225 .into_iter()
226 .map(|(k, v)| {
227 let key = match k.algorithm() {
228 DeviceKeyAlgorithm::Ed25519 => {
229 DeviceKey::Ed25519(Ed25519PublicKey::from_base64(&v)?)
230 }
231 DeviceKeyAlgorithm::Curve25519 => {
232 DeviceKey::Curve25519(Curve25519PublicKey::from_base64(&v)?)
233 }
234 _ => DeviceKey::Unknown(v),
235 };
236
237 Ok((k, key))
238 })
239 .collect();
240
241 Ok(Self {
242 user_id: value.user_id,
243 device_id: value.device_id,
244 algorithms: value.algorithms,
245 keys: keys?,
246 dehydrated: value.dehydrated,
247 signatures: value.signatures,
248 unsigned: value.unsigned,
249 other: value.other,
250 })
251 }
252}
253
254impl From<DeviceKeys> for DeviceKeyHelper {
255 fn from(value: DeviceKeys) -> Self {
256 let keys: BTreeMap<OwnedDeviceKeyId, String> =
257 value.keys.into_iter().map(|(k, v)| (k, v.to_base64())).collect();
258
259 Self {
260 user_id: value.user_id,
261 device_id: value.device_id,
262 algorithms: value.algorithms,
263 keys,
264 dehydrated: value.dehydrated,
265 signatures: value.signatures,
266 unsigned: value.unsigned,
267 other: value.other,
268 }
269 }
270}
271
272#[cfg(test)]
273mod tests {
274 use ruma::{device_id, user_id};
275 use serde_json::json;
276
277 use super::DeviceKeys;
278
279 #[test]
280 fn serialization() {
281 let json = json!({
282 "algorithms": vec![
283 "m.olm.v1.curve25519-aes-sha2",
284 "m.megolm.v1.aes-sha2"
285 ],
286 "device_id": "BNYQQWUMXO",
287 "user_id": "@example:localhost",
288 "keys": {
289 "curve25519:BNYQQWUMXO": "xfgbLIC5WAl1OIkpOzoxpCe8FsRDT6nch7NQsOb15nc",
290 "ed25519:BNYQQWUMXO": "2/5LWJMow5zhJqakV88SIc7q/1pa8fmkfgAzx72w9G4"
291 },
292 "signatures": {
293 "@example:localhost": {
294 "ed25519:BNYQQWUMXO": "kTwMrbsLJJM/uFGOj/oqlCaRuw7i9p/6eGrTlXjo8UJMCFAetoyWzoMcF35vSe4S6FTx8RJmqX6rM7ep53MHDQ"
295 }
296 },
297 "unsigned": {
298 "device_display_name": "Alice's mobile phone",
299 "other_data": "other_value"
300 },
301
302 "other_data": "other_value"
303 });
304
305 let device_keys: DeviceKeys =
306 serde_json::from_value(json.clone()).expect("Can't deserialize device keys");
307
308 assert_eq!(device_keys.user_id, user_id!("@example:localhost"));
309 assert_eq!(&device_keys.device_id, device_id!("BNYQQWUMXO"));
310
311 let serialized = serde_json::to_value(device_keys).expect("Can't reserialize device keys");
312
313 assert_eq!(json, serialized);
314 }
315}