1use std::collections::BTreeMap;
22
23use js_option::JsOption;
24use ruma::{
25 DeviceKeyAlgorithm, DeviceKeyId, OwnedDeviceId, OwnedDeviceKeyId, OwnedUserId,
26 serde::{JsonCastable, Raw},
27};
28use serde::{Deserialize, Serialize};
29use serde_json::{Value, value::to_raw_value};
30use vodozemac::{Curve25519PublicKey, Ed25519PublicKey};
31
32use super::{EventEncryptionAlgorithm, Signatures};
33use crate::{
34 SignatureError,
35 olm::{SignedJsonObject, VerifyJson},
36};
37
38#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
54#[serde(try_from = "DeviceKeyHelper", into = "DeviceKeyHelper")]
55pub struct DeviceKeys {
56 pub user_id: OwnedUserId,
60
61 pub device_id: OwnedDeviceId,
65
66 pub algorithms: Vec<EventEncryptionAlgorithm>,
68
69 pub keys: BTreeMap<OwnedDeviceKeyId, DeviceKey>,
71
72 pub signatures: Signatures,
74
75 #[serde(default, skip_serializing_if = "JsOption::is_undefined")]
77 pub dehydrated: JsOption<bool>,
78
79 #[serde(default, skip_serializing_if = "UnsignedDeviceInfo::is_empty")]
82 pub unsigned: UnsignedDeviceInfo,
83
84 #[serde(flatten)]
85 other: BTreeMap<String, Value>,
86}
87
88impl DeviceKeys {
89 pub fn new(
92 user_id: OwnedUserId,
93 device_id: OwnedDeviceId,
94 algorithms: Vec<EventEncryptionAlgorithm>,
95 keys: BTreeMap<OwnedDeviceKeyId, DeviceKey>,
96 signatures: Signatures,
97 ) -> Self {
98 Self {
99 user_id,
100 device_id,
101 algorithms,
102 keys,
103 signatures,
104 dehydrated: JsOption::Undefined,
105 unsigned: Default::default(),
106 other: BTreeMap::new(),
107 }
108 }
109
110 pub fn to_raw<T>(&self) -> Raw<T> {
112 Raw::from_json(to_raw_value(&self).expect("Couldn't serialize device keys"))
113 }
114
115 pub fn get_key(&self, algorithm: DeviceKeyAlgorithm) -> Option<&DeviceKey> {
117 self.keys.get(&DeviceKeyId::from_parts(algorithm, &self.device_id))
118 }
119
120 pub fn curve25519_key(&self) -> Option<Curve25519PublicKey> {
122 self.get_key(DeviceKeyAlgorithm::Curve25519)
123 .and_then(|k| if let DeviceKey::Curve25519(k) = k { Some(*k) } else { None })
124 }
125
126 pub fn ed25519_key(&self) -> Option<Ed25519PublicKey> {
128 self.get_key(DeviceKeyAlgorithm::Ed25519)
129 .and_then(|k| if let DeviceKey::Ed25519(k) = k { Some(*k) } else { None })
130 }
131
132 pub fn has_signed(&self, signed_object: &impl SignedJsonObject) -> Result<(), SignatureError> {
135 let key = self.ed25519_key().ok_or(SignatureError::MissingSigningKey)?;
136 let key_id = &DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, &self.device_id);
137 key.verify_json(&self.user_id, key_id, signed_object)
138 }
139
140 pub fn check_self_signature(&self) -> Result<(), SignatureError> {
143 self.has_signed(self)
144 }
145}
146
147impl JsonCastable<DeviceKeys> for ruma::encryption::DeviceKeys {}
148
149#[derive(Clone, Debug, Default, Deserialize, Serialize, PartialEq)]
151pub struct UnsignedDeviceInfo {
152 #[serde(skip_serializing_if = "Option::is_none")]
154 pub device_display_name: Option<String>,
155
156 #[serde(flatten)]
157 other: BTreeMap<String, Value>,
158}
159
160impl UnsignedDeviceInfo {
161 pub fn new() -> Self {
163 Default::default()
164 }
165
166 pub fn is_empty(&self) -> bool {
168 self.device_display_name.is_none()
169 }
170}
171
172#[derive(Clone, Debug, PartialEq, Eq)]
178pub enum DeviceKey {
179 Curve25519(Curve25519PublicKey),
181 Ed25519(Ed25519PublicKey),
183 Unknown(String),
185}
186
187impl DeviceKey {
188 pub fn to_base64(&self) -> String {
190 match self {
191 DeviceKey::Curve25519(k) => k.to_base64(),
192 DeviceKey::Ed25519(k) => k.to_base64(),
193 DeviceKey::Unknown(k) => k.to_owned(),
194 }
195 }
196}
197
198impl From<Curve25519PublicKey> for DeviceKey {
199 fn from(val: Curve25519PublicKey) -> Self {
200 DeviceKey::Curve25519(val)
201 }
202}
203
204impl From<Ed25519PublicKey> for DeviceKey {
205 fn from(val: Ed25519PublicKey) -> Self {
206 DeviceKey::Ed25519(val)
207 }
208}
209
210#[derive(Clone, Debug, Deserialize, Serialize)]
213struct DeviceKeyHelper {
214 pub user_id: OwnedUserId,
215 pub device_id: OwnedDeviceId,
216 pub algorithms: Vec<EventEncryptionAlgorithm>,
217 pub keys: BTreeMap<OwnedDeviceKeyId, String>,
218 #[serde(default, skip_serializing_if = "JsOption::is_undefined")]
219 pub dehydrated: JsOption<bool>,
220 pub signatures: Signatures,
221 #[serde(default, skip_serializing_if = "UnsignedDeviceInfo::is_empty")]
222 pub unsigned: UnsignedDeviceInfo,
223 #[serde(flatten)]
224 other: BTreeMap<String, Value>,
225}
226
227impl TryFrom<DeviceKeyHelper> for DeviceKeys {
228 type Error = vodozemac::KeyError;
229
230 fn try_from(value: DeviceKeyHelper) -> Result<Self, Self::Error> {
231 let keys: Result<BTreeMap<OwnedDeviceKeyId, DeviceKey>, vodozemac::KeyError> = value
232 .keys
233 .into_iter()
234 .map(|(k, v)| {
235 let key = match k.algorithm() {
236 DeviceKeyAlgorithm::Ed25519 => {
237 DeviceKey::Ed25519(Ed25519PublicKey::from_base64(&v)?)
238 }
239 DeviceKeyAlgorithm::Curve25519 => {
240 DeviceKey::Curve25519(Curve25519PublicKey::from_base64(&v)?)
241 }
242 _ => DeviceKey::Unknown(v),
243 };
244
245 Ok((k, key))
246 })
247 .collect();
248
249 Ok(Self {
250 user_id: value.user_id,
251 device_id: value.device_id,
252 algorithms: value.algorithms,
253 keys: keys?,
254 dehydrated: value.dehydrated,
255 signatures: value.signatures,
256 unsigned: value.unsigned,
257 other: value.other,
258 })
259 }
260}
261
262impl From<DeviceKeys> for DeviceKeyHelper {
263 fn from(value: DeviceKeys) -> Self {
264 let keys: BTreeMap<OwnedDeviceKeyId, String> =
265 value.keys.into_iter().map(|(k, v)| (k, v.to_base64())).collect();
266
267 Self {
268 user_id: value.user_id,
269 device_id: value.device_id,
270 algorithms: value.algorithms,
271 keys,
272 dehydrated: value.dehydrated,
273 signatures: value.signatures,
274 unsigned: value.unsigned,
275 other: value.other,
276 }
277 }
278}
279
280#[cfg(test)]
281mod tests {
282 use std::str::FromStr;
283
284 use ruma::{OwnedDeviceKeyId, device_id, user_id};
285 use serde_json::json;
286 use vodozemac::{Curve25519PublicKey, Curve25519SecretKey};
287
288 use super::DeviceKeys;
289
290 #[test]
291 fn serialization() {
292 let json = json!({
293 "algorithms": vec![
294 "m.olm.v1.curve25519-aes-sha2",
295 "m.megolm.v1.aes-sha2"
296 ],
297 "device_id": "BNYQQWUMXO",
298 "user_id": "@example:localhost",
299 "keys": {
300 "curve25519:BNYQQWUMXO": "xfgbLIC5WAl1OIkpOzoxpCe8FsRDT6nch7NQsOb15nc",
301 "ed25519:BNYQQWUMXO": "2/5LWJMow5zhJqakV88SIc7q/1pa8fmkfgAzx72w9G4"
302 },
303 "signatures": {
304 "@example:localhost": {
305 "ed25519:BNYQQWUMXO": "kTwMrbsLJJM/uFGOj/oqlCaRuw7i9p/6eGrTlXjo8UJMCFAetoyWzoMcF35vSe4S6FTx8RJmqX6rM7ep53MHDQ"
306 }
307 },
308 "unsigned": {
309 "device_display_name": "Alice's mobile phone",
310 "other_data": "other_value"
311 },
312
313 "other_data": "other_value"
314 });
315
316 let device_keys: DeviceKeys =
317 serde_json::from_value(json.clone()).expect("Can't deserialize device keys");
318
319 assert_eq!(device_keys.user_id, user_id!("@example:localhost"));
320 assert_eq!(&device_keys.device_id, device_id!("BNYQQWUMXO"));
321
322 let serialized = serde_json::to_value(device_keys).expect("Can't reserialize device keys");
323
324 assert_eq!(json, serialized);
325 }
326
327 #[test]
328 fn test_check_self_signature() {
329 let mut device_keys: DeviceKeys = serde_json::from_value(json!({
331 "algorithms": vec![
332 "m.olm.v1.curve25519-aes-sha2",
333 "m.megolm.v1.aes-sha2"
334 ],
335 "device_id": "BNYQQWUMXO",
336 "user_id": "@example:localhost",
337 "keys": {
338 "curve25519:BNYQQWUMXO": "xfgbLIC5WAl1OIkpOzoxpCe8FsRDT6nch7NQsOb15nc",
339 "ed25519:BNYQQWUMXO": "2/5LWJMow5zhJqakV88SIc7q/1pa8fmkfgAzx72w9G4"
340 },
341 "signatures": {
342 "@example:localhost": {
343 "ed25519:BNYQQWUMXO": "kTwMrbsLJJM/uFGOj/oqlCaRuw7i9p/6eGrTlXjo8UJMCFAetoyWzoMcF35vSe4S6FTx8RJmqX6rM7ep53MHDQ"
344 }
345 },
346 "unsigned": {
347 "device_display_name": "Alice's mobile phone"
348 }
349 })).expect("Can't deserialize device keys");
350
351 device_keys.check_self_signature().expect("Self-signature check failed");
352
353 let new_curve_key = Curve25519SecretKey::new();
355 let key_id = OwnedDeviceKeyId::from_str("curve25519:BNYQQWUMXO").unwrap();
356 device_keys.keys.insert(key_id, Curve25519PublicKey::from(&new_curve_key).into());
357
358 assert!(device_keys.check_self_signature().is_err());
359 }
360}