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