1use std::fmt::Debug;
19
20use ruma::{OwnedUserId, UserId};
21use serde::{Deserialize, Serialize};
22use serde_json::value::RawValue;
23use vodozemac::Ed25519PublicKey;
24
25use super::{
26 dummy::DummyEventContent,
27 forwarded_room_key::ForwardedRoomKeyContent,
28 room_key::RoomKeyContent,
29 room_key_request::{self, SupportedKeyInfo},
30 secret_send::SecretSendContent,
31 EventType,
32};
33use crate::types::{
34 deserialize_ed25519_key,
35 events::{from_str, room_key_bundle::RoomKeyBundleContent},
36 serialize_ed25519_key, DeviceKeys,
37};
38
39pub type DecryptedDummyEvent = DecryptedOlmV1Event<DummyEventContent>;
42
43pub type DecryptedRoomKeyEvent = DecryptedOlmV1Event<RoomKeyContent>;
46
47pub type DecryptedForwardedRoomKeyEvent = DecryptedOlmV1Event<ForwardedRoomKeyContent>;
50
51impl DecryptedForwardedRoomKeyEvent {
52 pub fn room_key_info(&self) -> Option<SupportedKeyInfo> {
58 match &self.content {
59 ForwardedRoomKeyContent::MegolmV1AesSha2(c) => Some(
60 room_key_request::MegolmV1AesSha2Content {
61 room_id: c.room_id.to_owned(),
62 sender_key: c.claimed_sender_key,
63 session_id: c.session_id.to_owned(),
64 }
65 .into(),
66 ),
67 #[cfg(feature = "experimental-algorithms")]
68 ForwardedRoomKeyContent::MegolmV2AesSha2(c) => Some(
69 room_key_request::MegolmV2AesSha2Content {
70 room_id: c.room_id.to_owned(),
71 session_id: c.session_id.to_owned(),
72 }
73 .into(),
74 ),
75 ForwardedRoomKeyContent::Unknown(_) => None,
76 }
77 }
78}
79
80pub type DecryptedSecretSendEvent = DecryptedOlmV1Event<SecretSendContent>;
83
84pub type DecryptedRoomKeyBundleEvent = DecryptedOlmV1Event<RoomKeyBundleContent>;
87
88#[derive(Debug)]
91pub enum AnyDecryptedOlmEvent {
92 RoomKey(DecryptedRoomKeyEvent),
94 ForwardedRoomKey(DecryptedForwardedRoomKeyEvent),
96 SecretSend(DecryptedSecretSendEvent),
98 Dummy(DecryptedDummyEvent),
100 RoomKeyBundle(DecryptedRoomKeyBundleEvent),
102 Custom(Box<ToDeviceCustomEvent>),
104}
105
106impl AnyDecryptedOlmEvent {
107 pub fn sender(&self) -> &UserId {
109 match self {
110 AnyDecryptedOlmEvent::RoomKey(e) => &e.sender,
111 AnyDecryptedOlmEvent::ForwardedRoomKey(e) => &e.sender,
112 AnyDecryptedOlmEvent::SecretSend(e) => &e.sender,
113 AnyDecryptedOlmEvent::Custom(e) => &e.sender,
114 AnyDecryptedOlmEvent::RoomKeyBundle(e) => &e.sender,
115 AnyDecryptedOlmEvent::Dummy(e) => &e.sender,
116 }
117 }
118
119 pub fn recipient(&self) -> &UserId {
121 match self {
122 AnyDecryptedOlmEvent::RoomKey(e) => &e.recipient,
123 AnyDecryptedOlmEvent::ForwardedRoomKey(e) => &e.recipient,
124 AnyDecryptedOlmEvent::SecretSend(e) => &e.recipient,
125 AnyDecryptedOlmEvent::Custom(e) => &e.recipient,
126 AnyDecryptedOlmEvent::RoomKeyBundle(e) => &e.recipient,
127 AnyDecryptedOlmEvent::Dummy(e) => &e.recipient,
128 }
129 }
130
131 pub fn keys(&self) -> &OlmV1Keys {
133 match self {
134 AnyDecryptedOlmEvent::RoomKey(e) => &e.keys,
135 AnyDecryptedOlmEvent::ForwardedRoomKey(e) => &e.keys,
136 AnyDecryptedOlmEvent::SecretSend(e) => &e.keys,
137 AnyDecryptedOlmEvent::Custom(e) => &e.keys,
138 AnyDecryptedOlmEvent::RoomKeyBundle(e) => &e.keys,
139 AnyDecryptedOlmEvent::Dummy(e) => &e.keys,
140 }
141 }
142
143 pub fn recipient_keys(&self) -> &OlmV1Keys {
145 match self {
146 AnyDecryptedOlmEvent::RoomKey(e) => &e.recipient_keys,
147 AnyDecryptedOlmEvent::ForwardedRoomKey(e) => &e.recipient_keys,
148 AnyDecryptedOlmEvent::SecretSend(e) => &e.recipient_keys,
149 AnyDecryptedOlmEvent::Custom(e) => &e.recipient_keys,
150 AnyDecryptedOlmEvent::RoomKeyBundle(e) => &e.recipient_keys,
151 AnyDecryptedOlmEvent::Dummy(e) => &e.recipient_keys,
152 }
153 }
154
155 pub fn event_type(&self) -> &str {
157 match self {
158 AnyDecryptedOlmEvent::Custom(e) => &e.event_type,
159 AnyDecryptedOlmEvent::RoomKey(e) => e.content.event_type(),
160 AnyDecryptedOlmEvent::ForwardedRoomKey(e) => e.content.event_type(),
161 AnyDecryptedOlmEvent::SecretSend(e) => e.content.event_type(),
162 AnyDecryptedOlmEvent::RoomKeyBundle(e) => e.content.event_type(),
163 AnyDecryptedOlmEvent::Dummy(e) => e.content.event_type(),
164 }
165 }
166
167 pub fn sender_device_keys(&self) -> Option<&DeviceKeys> {
169 match self {
170 AnyDecryptedOlmEvent::Custom(e) => e.sender_device_keys.as_ref(),
171 AnyDecryptedOlmEvent::RoomKey(e) => e.sender_device_keys.as_ref(),
172 AnyDecryptedOlmEvent::ForwardedRoomKey(e) => e.sender_device_keys.as_ref(),
173 AnyDecryptedOlmEvent::SecretSend(e) => e.sender_device_keys.as_ref(),
174 AnyDecryptedOlmEvent::RoomKeyBundle(e) => e.sender_device_keys.as_ref(),
175 AnyDecryptedOlmEvent::Dummy(e) => e.sender_device_keys.as_ref(),
176 }
177 }
178}
179
180#[derive(Clone, Debug, Deserialize)]
185pub struct DecryptedOlmV1Event<C>
186where
187 C: EventType + Debug + Sized + Serialize,
188{
189 pub sender: OwnedUserId,
191 pub recipient: OwnedUserId,
193 pub keys: OlmV1Keys,
195 pub recipient_keys: OlmV1Keys,
197 #[serde(alias = "org.matrix.msc4147.device_keys")]
199 pub sender_device_keys: Option<DeviceKeys>,
200 pub content: C,
202}
203
204impl<C: EventType + Debug + Sized + Serialize> Serialize for DecryptedOlmV1Event<C> {
205 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
213 where
214 S: serde::Serializer,
215 {
216 #[derive(Serialize)]
217 struct DecryptedEventSerializationHelper<'a, C: EventType + Debug + Sized + Serialize> {
218 sender: &'a UserId,
219 recipient: &'a UserId,
220 keys: &'a OlmV1Keys,
221 recipient_keys: &'a OlmV1Keys,
222 #[serde(skip_serializing_if = "Option::is_none")]
223 sender_device_keys: Option<&'a DeviceKeys>,
224 content: &'a C,
225 #[serde(rename = "type")]
226 event_type: &'a str,
227 }
228
229 let event_type = self.content.event_type();
230
231 let DecryptedOlmV1Event {
232 sender,
233 recipient,
234 keys,
235 recipient_keys,
236 sender_device_keys,
237 content,
238 } = &self;
239
240 let event = DecryptedEventSerializationHelper {
241 sender,
242 recipient,
243 keys,
244 recipient_keys,
245 sender_device_keys: sender_device_keys.as_ref(),
246 content,
247 event_type,
248 };
249
250 event.serialize(serializer)
251 }
252}
253
254impl<C: EventType + Debug + Sized + Serialize> DecryptedOlmV1Event<C> {
255 #[cfg(test)]
256 pub fn new(
262 sender: &UserId,
263 recipient: &UserId,
264 key: Ed25519PublicKey,
265 device_keys: Option<DeviceKeys>,
266 content: C,
267 ) -> Self {
268 Self {
269 sender: sender.to_owned(),
270 recipient: recipient.to_owned(),
271 keys: OlmV1Keys { ed25519: key },
272 recipient_keys: OlmV1Keys { ed25519: key },
273 sender_device_keys: device_keys,
274 content,
275 }
276 }
277}
278
279#[derive(Clone, Debug, Deserialize, Serialize)]
281pub struct ToDeviceCustomEvent {
282 pub sender: OwnedUserId,
284 pub recipient: OwnedUserId,
286 pub keys: OlmV1Keys,
288 pub recipient_keys: OlmV1Keys,
290 #[serde(alias = "org.matrix.msc4147.device_keys")]
292 pub sender_device_keys: Option<DeviceKeys>,
293 #[serde(rename = "type")]
295 pub event_type: String,
296}
297
298#[derive(Clone, Debug, Deserialize, Serialize)]
300pub struct OlmV1Keys {
301 #[serde(
303 deserialize_with = "deserialize_ed25519_key",
304 serialize_with = "serialize_ed25519_key"
305 )]
306 pub ed25519: Ed25519PublicKey,
307}
308
309impl<'de> Deserialize<'de> for AnyDecryptedOlmEvent {
310 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
311 where
312 D: serde::Deserializer<'de>,
313 {
314 #[derive(Debug, Deserialize)]
315 struct Helper<'a> {
316 #[serde(rename = "type")]
317 event_type: &'a str,
318 }
319
320 let json = Box::<RawValue>::deserialize(deserializer)?;
321 let helper: Helper<'_> =
322 serde_json::from_str(json.get()).map_err(serde::de::Error::custom)?;
323
324 let json = json.get();
325
326 Ok(match helper.event_type {
327 "m.room_key" => AnyDecryptedOlmEvent::RoomKey(from_str(json)?),
328 "m.forwarded_room_key" => AnyDecryptedOlmEvent::ForwardedRoomKey(from_str(json)?),
329 "m.secret.send" => AnyDecryptedOlmEvent::SecretSend(from_str(json)?),
330 "m.dummy" => AnyDecryptedOlmEvent::Dummy(from_str(json)?),
331 RoomKeyBundleContent::EVENT_TYPE => {
332 AnyDecryptedOlmEvent::RoomKeyBundle(from_str(json)?)
333 }
334 _ => AnyDecryptedOlmEvent::Custom(from_str(json)?),
335 })
336 }
337}
338
339#[cfg(test)]
340mod tests {
341 use std::collections::BTreeMap;
342
343 use assert_matches::assert_matches;
344 use insta::{assert_json_snapshot, with_settings};
345 use ruma::{device_id, owned_user_id, KeyId};
346 use serde_json::{json, Value};
347 use similar_asserts::assert_eq;
348 use vodozemac::{Curve25519PublicKey, Ed25519PublicKey, Ed25519Signature};
349
350 use super::AnyDecryptedOlmEvent;
351 use crate::types::{
352 events::olm_v1::DecryptedRoomKeyEvent, DeviceKey, DeviceKeys, EventEncryptionAlgorithm,
353 Signatures,
354 };
355
356 const ED25519_KEY: &str = "aOfOnlaeMb5GW1TxkZ8pXnblkGMgAvps+lAukrdYaZk";
357
358 fn dummy_event() -> Value {
359 json!({
360 "sender": "@alice:example.org",
361 "sender_device": "DEVICEID",
362 "keys": {
363 "ed25519": ED25519_KEY,
364 },
365 "recipient": "@bob:example.org",
366 "recipient_keys": {
367 "ed25519": ED25519_KEY,
368 },
369 "content": {},
370 "type": "m.dummy"
371 })
372 }
373
374 fn room_key_event() -> Value {
375 json!({
376 "sender": "@alice:example.org",
377 "keys": {
378 "ed25519": ED25519_KEY,
379 },
380 "recipient": "@bob:example.org",
381 "recipient_keys": {
382 "ed25519": ED25519_KEY,
383 },
384 "content": {
385 "algorithm": "m.megolm.v1.aes-sha2",
386 "room_id": "!Cuyf34gef24t:localhost",
387 "session_id": "ZFD6+OmV7fVCsJ7Gap8UnORH8EnmiAkes8FAvQuCw/I",
388 "session_key": "AgAAAADNp1EbxXYOGmJtyX4AkD1bvJvAUyPkbIaKxtnGKjv\
389 SQ3E/4mnuqdM4vsmNzpO1EeWzz1rDkUpYhYE9kP7sJhgLXi\
390 jVv80fMPHfGc49hPdu8A+xnwD4SQiYdFmSWJOIqsxeo/fiH\
391 tino//CDQENtcKuEt0I9s0+Kk4YSH310Szse2RQ+vjple31\
392 QrCexmqfFJzkR/BJ5ogJHrPBQL0LgsPyglIbMTLg7qygIaY\
393 U5Fe2QdKMH7nTZPNIRHh1RaMfHVETAUJBax88EWZBoifk80\
394 gdHUwHSgMk77vCc2a5KHKLDA"
395 },
396 "type": "m.room_key"
397 })
398 }
399
400 fn forwarded_room_key_event() -> Value {
401 json!({
402 "sender": "@alice:example.org",
403 "sender_device": "DEVICEID",
404 "keys": {
405 "ed25519": ED25519_KEY,
406 },
407 "recipient": "@bob:example.org",
408 "recipient_keys": {
409 "ed25519": ED25519_KEY,
410 },
411 "content": {
412 "algorithm": "m.megolm.v1.aes-sha2",
413 "forwarding_curve25519_key_chain": [
414 "hPQNcabIABgGnx3/ACv/jmMmiQHoeFfuLB17tzWp6Hw"
415 ],
416 "room_id": "!Cuyf34gef24t:localhost",
417 "sender_claimed_ed25519_key": "aj40p+aw64yPIdsxoog8jhPu9i7l7NcFRecuOQblE3Y",
418 "sender_key": "RF3s+E7RkTQTGF2d8Deol0FkQvgII2aJDf3/Jp5mxVU",
419 "session_id": "X3lUlvLELLYxeTx4yOVu6UDpasGEVO0Jbu+QFnm0cKQ",
420 "session_key": "AQAAAAq2JpkMceK5f6JrZPJWwzQTn59zliuIv0F7apVLXDcZCCT\
421 3LqBjD21sULYEO5YTKdpMVhi9i6ZSZhdvZvp//tzRpDT7wpWVWI\
422 00Y3EPEjmpm/HfZ4MMAKpk+tzJVuuvfAcHBZgpnxBGzYOc/DAqa\
423 pK7Tk3t3QJ1UMSD94HfAqlb1JF5QBPwoh0fOvD8pJdanB8zxz05\
424 tKFdR73/vo2Q/zE3"
425 },
426 "type": "m.forwarded_room_key"
427 })
428 }
429
430 fn secret_send_event() -> Value {
431 json!({
432 "sender": "@alice:example.org",
433 "sender_device": "DEVICEID",
434 "keys": {
435 "ed25519": ED25519_KEY,
436 },
437 "recipient": "@bob:example.org",
438 "recipient_keys": {
439 "ed25519": ED25519_KEY,
440 },
441 "content": {
442 "request_id": "randomly_generated_id_9573",
443 "secret": "ThisIsASecretDon'tTellAnyone"
444 },
445 "type": "m.secret.send"
446 })
447 }
448
449 fn sender_device_keys() -> (Value, DeviceKeys) {
453 let sender_device_keys_json = json!({
454 "user_id": "@u:s.co",
455 "device_id": "DEV",
456 "algorithms": [
457 "m.olm.v1.curve25519-aes-sha2"
458 ],
459 "keys": {
460 "curve25519:DEV": "c29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb28",
461 "ed25519:DEV": "b29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb28"
462 },
463 "signatures": {
464 "@u:s.co": {
465 "ed25519:DEV": "mia28GKixFzOWKJ0h7Bdrdy2fjxiHCsst1qpe467FbW85H61UlshtKBoAXfTLlVfi0FX+/noJ8B3noQPnY+9Cg",
466 "ed25519:ssk": "mia28GKixFzOWKJ0h7Bdrdy2fjxiHCsst1qpe467FbW85H61UlshtKBoAXfTLlVfi0FX+/noJ8B3noQPnY+9Cg"
467 }
468 }
469 }
470 );
471
472 let user_id = owned_user_id!("@u:s.co");
473 let device_id = device_id!("DEV");
474 let ssk_id = device_id!("ssk");
475
476 let ed25519_device_key_id = KeyId::from_parts(ruma::DeviceKeyAlgorithm::Ed25519, device_id);
477 let curve25519_device_key_id =
478 KeyId::from_parts(ruma::DeviceKeyAlgorithm::Curve25519, device_id);
479 let ed25519_ssk_id = KeyId::from_parts(ruma::DeviceKeyAlgorithm::Ed25519, ssk_id);
480
481 let mut keys = BTreeMap::new();
482 keys.insert(
483 ed25519_device_key_id.clone(),
484 DeviceKey::Ed25519(
485 Ed25519PublicKey::from_base64("b29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb28")
486 .unwrap(),
487 ),
488 );
489 keys.insert(
490 curve25519_device_key_id,
491 DeviceKey::Curve25519(
492 Curve25519PublicKey::from_base64("c29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb28")
493 .unwrap(),
494 ),
495 );
496
497 let mut signatures = Signatures::new();
498 signatures.add_signature(
499 user_id.clone(),
500 ed25519_device_key_id,
501 Ed25519Signature::from_base64(
502 "mia28GKixFzOWKJ0h7Bdrdy2fjxiHCsst1qpe467FbW85H61UlshtKBoAXfTLlVfi0FX+/noJ8B3noQPnY+9Cg"
503 ).unwrap()
504 );
505 signatures. add_signature(
506 user_id.clone(),
507 ed25519_ssk_id,
508 Ed25519Signature::from_base64(
509 "mia28GKixFzOWKJ0h7Bdrdy2fjxiHCsst1qpe467FbW85H61UlshtKBoAXfTLlVfi0FX+/noJ8B3noQPnY+9Cg"
510 ).unwrap()
511 );
512 let sender_device_keys = DeviceKeys::new(
513 user_id,
514 device_id.to_owned(),
515 vec![EventEncryptionAlgorithm::OlmV1Curve25519AesSha2],
516 keys,
517 signatures,
518 );
519
520 (sender_device_keys_json, sender_device_keys)
521 }
522
523 #[test]
524 fn decrypted_to_device_event_snapshot() {
525 let event_json = room_key_event();
526 let event: DecryptedRoomKeyEvent = serde_json::from_value(event_json)
527 .expect("JSON should deserialize to the right event type");
528
529 with_settings!({ sort_maps => true, prepend_module_to_snapshot => false }, {
530 assert_json_snapshot!(event);
531 })
532 }
533
534 #[test]
535 fn deserialization() -> Result<(), serde_json::Error> {
536 macro_rules! assert_deserialization_result {
537 ( $( $json:path => $to_device_events:ident ),* $(,)? ) => {
538 $(
539 let json = $json();
540 let event: AnyDecryptedOlmEvent = serde_json::from_value(json)?;
541
542 assert_matches!(event, AnyDecryptedOlmEvent::$to_device_events(_));
543 )*
544 }
545 }
546
547 assert_deserialization_result!(
548 room_key_event => RoomKey,
550
551 forwarded_room_key_event => ForwardedRoomKey,
553
554 secret_send_event => SecretSend,
556
557 dummy_event => Dummy,
559 );
560
561 Ok(())
562 }
563
564 #[test]
565 fn sender_device_keys_are_deserialized_unstable() {
566 let (sender_device_keys_json, sender_device_keys) = sender_device_keys();
567
568 let mut event_json = room_key_event();
571 event_json
572 .as_object_mut()
573 .unwrap()
574 .insert("org.matrix.msc4147.device_keys".to_owned(), sender_device_keys_json);
575
576 let event: DecryptedRoomKeyEvent = serde_json::from_value(event_json)
578 .expect("JSON should deserialize to the right event type");
579
580 assert_eq!(event.sender_device_keys, Some(sender_device_keys));
582 }
583
584 #[test]
585 fn sender_device_keys_are_deserialized() {
586 let (sender_device_keys_json, sender_device_keys) = sender_device_keys();
587
588 let mut event_json = room_key_event();
590 event_json
591 .as_object_mut()
592 .unwrap()
593 .insert("sender_device_keys".to_owned(), sender_device_keys_json);
594
595 let event: DecryptedRoomKeyEvent = serde_json::from_value(event_json)
597 .expect("JSON should deserialize to the right event type");
598
599 assert_eq!(event.sender_device_keys, Some(sender_device_keys));
601
602 with_settings!({ sort_maps => true, prepend_module_to_snapshot => false }, {
603 assert_json_snapshot!(event);
604 });
605 }
606
607 #[test]
608 fn test_serialization_cycle() {
609 let event_json = json!({
610 "sender": "@alice:example.org",
611 "keys": {
612 "ed25519": ED25519_KEY,
613 },
614 "recipient": "@bob:example.org",
615 "recipient_keys": {
616 "ed25519": ED25519_KEY,
617 },
618 "content": {
619 "algorithm": "m.megolm.v1.aes-sha2",
620 "room_id": "!Cuyf34gef24t:localhost",
621 "org.matrix.msc3061.shared_history": true,
622 "session_id": "ZFD6+OmV7fVCsJ7Gap8UnORH8EnmiAkes8FAvQuCw/I",
623 "session_key": "AgAAAADNp1EbxXYOGmJtyX4AkD1bvJvAUyPkbIaKxtnGKjv\
624 SQ3E/4mnuqdM4vsmNzpO1EeWzz1rDkUpYhYE9kP7sJhgLXi\
625 jVv80fMPHfGc49hPdu8A+xnwD4SQiYdFmSWJOIqsxeo/fiH\
626 tino//CDQENtcKuEt0I9s0+Kk4YSH310Szse2RQ+vjple31\
627 QrCexmqfFJzkR/BJ5ogJHrPBQL0LgsPyglIbMTLg7qygIaY\
628 U5Fe2QdKMH7nTZPNIRHh1RaMfHVETAUJBax88EWZBoifk80\
629 gdHUwHSgMk77vCc2a5KHKLDA"
630 },
631 "type": "m.room_key"
632 });
633
634 let event: DecryptedRoomKeyEvent = serde_json::from_value(event_json.clone())
635 .expect("JSON should deserialize to the right event type");
636
637 let reserialized =
638 serde_json::to_value(event).expect("We should be able to serialize the event");
639
640 assert_eq!(
641 event_json, reserialized,
642 "The reserialized JSON should match the original value"
643 );
644 }
645}