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};
33use crate::{
34 olm::{SignedJsonObject, VerifyJson},
35 SignatureError,
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).and_then(|k| {
123 if let DeviceKey::Curve25519(k) = k {
124 Some(*k)
125 } else {
126 None
127 }
128 })
129 }
130
131 pub fn ed25519_key(&self) -> Option<Ed25519PublicKey> {
133 self.get_key(DeviceKeyAlgorithm::Ed25519).and_then(|k| {
134 if let DeviceKey::Ed25519(k) = k {
135 Some(*k)
136 } else {
137 None
138 }
139 })
140 }
141
142 pub fn has_signed(&self, signed_object: &impl SignedJsonObject) -> Result<(), SignatureError> {
145 let key = self.ed25519_key().ok_or(SignatureError::MissingSigningKey)?;
146 let key_id = &DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, &self.device_id);
147 key.verify_json(&self.user_id, key_id, signed_object)
148 }
149
150 pub fn check_self_signature(&self) -> Result<(), SignatureError> {
153 self.has_signed(self)
154 }
155}
156
157impl JsonCastable<DeviceKeys> for ruma::encryption::DeviceKeys {}
158
159#[derive(Clone, Debug, Default, Deserialize, Serialize, PartialEq)]
161pub struct UnsignedDeviceInfo {
162 #[serde(skip_serializing_if = "Option::is_none")]
164 pub device_display_name: Option<String>,
165
166 #[serde(flatten)]
167 other: BTreeMap<String, Value>,
168}
169
170impl UnsignedDeviceInfo {
171 pub fn new() -> Self {
173 Default::default()
174 }
175
176 pub fn is_empty(&self) -> bool {
178 self.device_display_name.is_none()
179 }
180}
181
182#[derive(Clone, Debug, PartialEq, Eq)]
188pub enum DeviceKey {
189 Curve25519(Curve25519PublicKey),
191 Ed25519(Ed25519PublicKey),
193 Unknown(String),
195}
196
197impl DeviceKey {
198 pub fn to_base64(&self) -> String {
200 match self {
201 DeviceKey::Curve25519(k) => k.to_base64(),
202 DeviceKey::Ed25519(k) => k.to_base64(),
203 DeviceKey::Unknown(k) => k.to_owned(),
204 }
205 }
206}
207
208impl From<Curve25519PublicKey> for DeviceKey {
209 fn from(val: Curve25519PublicKey) -> Self {
210 DeviceKey::Curve25519(val)
211 }
212}
213
214impl From<Ed25519PublicKey> for DeviceKey {
215 fn from(val: Ed25519PublicKey) -> Self {
216 DeviceKey::Ed25519(val)
217 }
218}
219
220#[derive(Clone, Debug, Deserialize, Serialize)]
223struct DeviceKeyHelper {
224 pub user_id: OwnedUserId,
225 pub device_id: OwnedDeviceId,
226 pub algorithms: Vec<EventEncryptionAlgorithm>,
227 pub keys: BTreeMap<OwnedDeviceKeyId, String>,
228 #[serde(default, skip_serializing_if = "JsOption::is_undefined")]
229 pub dehydrated: JsOption<bool>,
230 pub signatures: Signatures,
231 #[serde(default, skip_serializing_if = "UnsignedDeviceInfo::is_empty")]
232 pub unsigned: UnsignedDeviceInfo,
233 #[serde(flatten)]
234 other: BTreeMap<String, Value>,
235}
236
237impl TryFrom<DeviceKeyHelper> for DeviceKeys {
238 type Error = vodozemac::KeyError;
239
240 fn try_from(value: DeviceKeyHelper) -> Result<Self, Self::Error> {
241 let keys: Result<BTreeMap<OwnedDeviceKeyId, DeviceKey>, vodozemac::KeyError> = value
242 .keys
243 .into_iter()
244 .map(|(k, v)| {
245 let key = match k.algorithm() {
246 DeviceKeyAlgorithm::Ed25519 => {
247 DeviceKey::Ed25519(Ed25519PublicKey::from_base64(&v)?)
248 }
249 DeviceKeyAlgorithm::Curve25519 => {
250 DeviceKey::Curve25519(Curve25519PublicKey::from_base64(&v)?)
251 }
252 _ => DeviceKey::Unknown(v),
253 };
254
255 Ok((k, key))
256 })
257 .collect();
258
259 Ok(Self {
260 user_id: value.user_id,
261 device_id: value.device_id,
262 algorithms: value.algorithms,
263 keys: keys?,
264 dehydrated: value.dehydrated,
265 signatures: value.signatures,
266 unsigned: value.unsigned,
267 other: value.other,
268 })
269 }
270}
271
272impl From<DeviceKeys> for DeviceKeyHelper {
273 fn from(value: DeviceKeys) -> Self {
274 let keys: BTreeMap<OwnedDeviceKeyId, String> =
275 value.keys.into_iter().map(|(k, v)| (k, v.to_base64())).collect();
276
277 Self {
278 user_id: value.user_id,
279 device_id: value.device_id,
280 algorithms: value.algorithms,
281 keys,
282 dehydrated: value.dehydrated,
283 signatures: value.signatures,
284 unsigned: value.unsigned,
285 other: value.other,
286 }
287 }
288}
289
290#[cfg(test)]
291mod tests {
292 use std::str::FromStr;
293
294 use ruma::{device_id, user_id, OwnedDeviceKeyId};
295 use serde_json::json;
296 use vodozemac::{Curve25519PublicKey, Curve25519SecretKey};
297
298 use super::DeviceKeys;
299
300 #[test]
301 fn serialization() {
302 let json = json!({
303 "algorithms": vec![
304 "m.olm.v1.curve25519-aes-sha2",
305 "m.megolm.v1.aes-sha2"
306 ],
307 "device_id": "BNYQQWUMXO",
308 "user_id": "@example:localhost",
309 "keys": {
310 "curve25519:BNYQQWUMXO": "xfgbLIC5WAl1OIkpOzoxpCe8FsRDT6nch7NQsOb15nc",
311 "ed25519:BNYQQWUMXO": "2/5LWJMow5zhJqakV88SIc7q/1pa8fmkfgAzx72w9G4"
312 },
313 "signatures": {
314 "@example:localhost": {
315 "ed25519:BNYQQWUMXO": "kTwMrbsLJJM/uFGOj/oqlCaRuw7i9p/6eGrTlXjo8UJMCFAetoyWzoMcF35vSe4S6FTx8RJmqX6rM7ep53MHDQ"
316 }
317 },
318 "unsigned": {
319 "device_display_name": "Alice's mobile phone",
320 "other_data": "other_value"
321 },
322
323 "other_data": "other_value"
324 });
325
326 let device_keys: DeviceKeys =
327 serde_json::from_value(json.clone()).expect("Can't deserialize device keys");
328
329 assert_eq!(device_keys.user_id, user_id!("@example:localhost"));
330 assert_eq!(&device_keys.device_id, device_id!("BNYQQWUMXO"));
331
332 let serialized = serde_json::to_value(device_keys).expect("Can't reserialize device keys");
333
334 assert_eq!(json, serialized);
335 }
336
337 #[test]
338 fn test_check_self_signature() {
339 let mut device_keys: DeviceKeys = serde_json::from_value(json!({
341 "algorithms": vec![
342 "m.olm.v1.curve25519-aes-sha2",
343 "m.megolm.v1.aes-sha2"
344 ],
345 "device_id": "BNYQQWUMXO",
346 "user_id": "@example:localhost",
347 "keys": {
348 "curve25519:BNYQQWUMXO": "xfgbLIC5WAl1OIkpOzoxpCe8FsRDT6nch7NQsOb15nc",
349 "ed25519:BNYQQWUMXO": "2/5LWJMow5zhJqakV88SIc7q/1pa8fmkfgAzx72w9G4"
350 },
351 "signatures": {
352 "@example:localhost": {
353 "ed25519:BNYQQWUMXO": "kTwMrbsLJJM/uFGOj/oqlCaRuw7i9p/6eGrTlXjo8UJMCFAetoyWzoMcF35vSe4S6FTx8RJmqX6rM7ep53MHDQ"
354 }
355 },
356 "unsigned": {
357 "device_display_name": "Alice's mobile phone"
358 }
359 })).expect("Can't deserialize device keys");
360
361 device_keys.check_self_signature().expect("Self-signature check failed");
362
363 let new_curve_key = Curve25519SecretKey::new();
365 let key_id = OwnedDeviceKeyId::from_str("curve25519:BNYQQWUMXO").unwrap();
366 device_keys.keys.insert(key_id, Curve25519PublicKey::from(&new_curve_key).into());
367
368 assert!(device_keys.check_self_signature().is_err());
369 }
370}