From 7ec2b0b555a33f541c1f2590aacba11c65983b0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Thu, 3 Dec 2020 10:46:11 +0100 Subject: [PATCH] ruma-events: Create separate to-device content structs While it's possible to share the content between different event types in the usual case some event types have slightly different contents if they are sent out as a to-device event vs a room message event. The canonical example for this are key verification events where the to-device version has a transaction id field but the room message version uses event relationships for the same purpose. This patch makes it possible for to-device events to have different content structs. Type aliases are used where a common struct can be used. --- ruma-events-macros/src/event_enum.rs | 22 +++++-- ruma-events/src/dummy.rs | 3 + ruma-events/src/forwarded_room_key.rs | 9 +-- ruma-events/src/key/verification/accept.rs | 60 +++++++++++------- ruma-events/src/key/verification/cancel.rs | 9 +-- ruma-events/src/key/verification/key.rs | 9 +-- ruma-events/src/key/verification/mac.rs | 9 +-- ruma-events/src/key/verification/request.rs | 8 +-- ruma-events/src/key/verification/start.rs | 69 +++++++++++++-------- ruma-events/src/room/encrypted.rs | 3 + ruma-events/src/room_key.rs | 7 ++- ruma-events/src/room_key_request.rs | 9 +-- 12 files changed, 112 insertions(+), 105 deletions(-) diff --git a/ruma-events-macros/src/event_enum.rs b/ruma-events-macros/src/event_enum.rs index 5ab0332d..d4ac2cee 100644 --- a/ruma-events-macros/src/event_enum.rs +++ b/ruma-events-macros/src/event_enum.rs @@ -372,7 +372,7 @@ fn expand_content_enum( let event_type_str = events; let content = - events.iter().map(|ev| to_event_content_path(ev, ruma_events)).collect::>(); + events.iter().map(|ev| to_event_content_path(kind, ev, ruma_events)).collect::>(); let variant_decls = variants.iter().map(|v| v.decl()); @@ -809,8 +809,7 @@ fn to_event_path(name: &LitStr, struct_name: &Ident, ruma_events: &TokenStream) }; quote! { #ruma_events::room::redaction::#redaction } } - "ToDeviceEvent" - | "SyncStateEvent" + "SyncStateEvent" | "StrippedStateEvent" | "InitialStateEvent" | "SyncMessageEvent" @@ -818,6 +817,10 @@ fn to_event_path(name: &LitStr, struct_name: &Ident, ruma_events: &TokenStream) let content = format_ident!("{}EventContent", event); quote! { #ruma_events::#struct_name<#ruma_events::#( #path )::*::#content> } } + "ToDeviceEvent" => { + let content = format_ident!("{}ToDeviceEventContent", event); + quote! { #ruma_events::#struct_name<#ruma_events::#( #path )::*::#content> } + } struct_str if struct_str.contains("Redacted") => { let content = format_ident!("Redacted{}EventContent", event); quote! { #ruma_events::#struct_name<#ruma_events::#( #path )::*::#content> } @@ -829,7 +832,11 @@ fn to_event_path(name: &LitStr, struct_name: &Ident, ruma_events: &TokenStream) } } -fn to_event_content_path(name: &LitStr, ruma_events: &TokenStream) -> TokenStream { +fn to_event_content_path( + kind: &EventKind, + name: &LitStr, + ruma_events: &TokenStream, +) -> TokenStream { let span = name.span(); let name = name.value(); @@ -844,8 +851,13 @@ fn to_event_content_path(name: &LitStr, ruma_events: &TokenStream) -> TokenStrea .map(|s| s.chars().next().unwrap().to_uppercase().to_string() + &s[1..]) .collect::(); - let content_str = format_ident!("{}EventContent", event); + let content_str = match kind { + EventKind::ToDevice => format_ident!("{}ToDeviceEventContent", event), + _ => format_ident!("{}EventContent", event), + }; + let path = path.iter().map(|s| Ident::new(s, span)); + quote! { #ruma_events::#( #path )::*::#content_str } diff --git a/ruma-events/src/dummy.rs b/ruma-events/src/dummy.rs index e0f30956..11382b6d 100644 --- a/ruma-events/src/dummy.rs +++ b/ruma-events/src/dummy.rs @@ -24,6 +24,9 @@ pub type DummyEvent = BasicEvent; #[ruma_event(type = "m.dummy")] pub struct DummyEventContent(pub Empty); +/// The to-device version of the payload for the `DummyEvent`. +pub type DummyToDeviceEventContent = DummyEventContent; + impl Deref for DummyEventContent { type Target = Empty; diff --git a/ruma-events/src/forwarded_room_key.rs b/ruma-events/src/forwarded_room_key.rs index 20aa3cef..f0afdf87 100644 --- a/ruma-events/src/forwarded_room_key.rs +++ b/ruma-events/src/forwarded_room_key.rs @@ -4,17 +4,10 @@ use ruma_events_macros::BasicEventContent; use ruma_identifiers::{EventEncryptionAlgorithm, RoomId}; use serde::{Deserialize, Serialize}; -use crate::BasicEvent; - -/// This event type is used to forward keys for end-to-end encryption. -/// -/// Typically it is encrypted as an *m.room.encrypted* event, then sent as a to-device event. -pub type ForwardedRoomKeyEvent = BasicEvent; - /// The payload for `ForwardedRoomKeyEvent`. #[derive(Clone, Debug, Deserialize, Serialize, BasicEventContent)] #[ruma_event(type = "m.forwarded_room_key")] -pub struct ForwardedRoomKeyEventContent { +pub struct ForwardedRoomKeyToDeviceEventContent { /// The encryption algorithm the key in this event is to be used with. pub algorithm: EventEncryptionAlgorithm, diff --git a/ruma-events/src/key/verification/accept.rs b/ruma-events/src/key/verification/accept.rs index ec3d1da0..a182ba0d 100644 --- a/ruma-events/src/key/verification/accept.rs +++ b/ruma-events/src/key/verification/accept.rs @@ -9,17 +9,11 @@ use serde_json::Value as JsonValue; use super::{ HashAlgorithm, KeyAgreementProtocol, MessageAuthenticationCode, ShortAuthenticationString, }; -use crate::BasicEvent; - -/// Accepts a previously sent *m.key.verification.start* message. -/// -/// Typically sent as a to-device event. -pub type AcceptEvent = BasicEvent; /// The payload for `AcceptEvent`. #[derive(Clone, Debug, Deserialize, Serialize, BasicEventContent)] #[ruma_event(type = "m.key.verification.accept")] -pub struct AcceptEventContent { +pub struct AcceptToDeviceEventContent { /// An opaque identifier for the verification process. /// /// Must be the same as the one used for the *m.key.verification.start* @@ -130,19 +124,21 @@ mod tests { use std::collections::BTreeMap; use matches::assert_matches; + use ruma_identifiers::user_id; + use ruma_serde::Raw; use serde_json::{ from_value as from_json_value, json, to_value as to_json_value, Value as JsonValue, }; use super::{ - AcceptEvent, AcceptEventContent, AcceptMethod, CustomContent, HashAlgorithm, + AcceptMethod, AcceptToDeviceEventContent, CustomContent, HashAlgorithm, KeyAgreementProtocol, MSasV1Content, MessageAuthenticationCode, ShortAuthenticationString, }; - use ruma_serde::Raw; + use crate::ToDeviceEvent; #[test] fn serialization() { - let key_verification_accept_content = AcceptEventContent { + let key_verification_accept_content = AcceptToDeviceEventContent { transaction_id: "456".into(), method: AcceptMethod::MSasV1(MSasV1Content { hash: HashAlgorithm::Sha256, @@ -153,7 +149,7 @@ mod tests { }), }; - let key_verification_accept = AcceptEvent { content: key_verification_accept_content }; + let sender = user_id!("@example:localhost"); let json_data = json!({ "content": { @@ -165,21 +161,28 @@ mod tests { "message_authentication_code": "hkdf-hmac-sha256", "short_authentication_string": ["decimal"] }, + "sender": sender, "type": "m.key.verification.accept" }); + let key_verification_accept = + ToDeviceEvent { sender, content: key_verification_accept_content }; + assert_eq!(to_json_value(&key_verification_accept).unwrap(), json_data); + let sender = user_id!("@example:localhost"); + let json_data = json!({ "content": { "transaction_id": "456", "method": "m.sas.custom", "test": "field", }, + "sender": sender, "type": "m.key.verification.accept" }); - let key_verification_accept_content = AcceptEventContent { + let key_verification_accept_content = AcceptToDeviceEventContent { transaction_id: "456".into(), method: AcceptMethod::Custom(CustomContent { method: "m.sas.custom".to_owned(), @@ -189,7 +192,8 @@ mod tests { }), }; - let key_verification_accept = AcceptEvent { content: key_verification_accept_content }; + let key_verification_accept = + ToDeviceEvent { sender, content: key_verification_accept_content }; assert_eq!(to_json_value(&key_verification_accept).unwrap(), json_data); } @@ -208,11 +212,11 @@ mod tests { // Deserialize the content struct separately to verify `TryFromRaw` is implemented for it. assert_matches!( - from_json_value::>(json) + from_json_value::>(json) .unwrap() .deserialize() .unwrap(), - AcceptEventContent { + AcceptToDeviceEventContent { transaction_id, method: AcceptMethod::MSasV1(MSasV1Content { commitment, @@ -229,6 +233,8 @@ mod tests { && short_authentication_string == vec![ShortAuthenticationString::Decimal] ); + let sender = user_id!("@example:localhost"); + let json = json!({ "content": { "commitment": "test_commitment", @@ -239,16 +245,18 @@ mod tests { "message_authentication_code": "hkdf-hmac-sha256", "short_authentication_string": ["decimal"] }, - "type": "m.key.verification.accept" + "type": "m.key.verification.accept", + "sender": sender, }); assert_matches!( - from_json_value::>(json) + from_json_value::>>(json) .unwrap() .deserialize() .unwrap(), - AcceptEvent { - content: AcceptEventContent { + ToDeviceEvent { + sender, + content: AcceptToDeviceEventContent { transaction_id, method: AcceptMethod::MSasV1(MSasV1Content { commitment, @@ -259,6 +267,7 @@ mod tests { }) } } if commitment == "test_commitment" + && sender == user_id!("@example:localhost") && transaction_id == "456" && hash == HashAlgorithm::Sha256 && key_agreement_protocol == KeyAgreementProtocol::Curve25519 @@ -266,6 +275,8 @@ mod tests { && short_authentication_string == vec![ShortAuthenticationString::Decimal] ); + let sender = user_id!("@example:localhost"); + let json = json!({ "content": { "from_device": "123", @@ -273,16 +284,18 @@ mod tests { "method": "m.sas.custom", "test": "field", }, - "type": "m.key.verification.accept" + "type": "m.key.verification.accept", + "sender": sender }); assert_matches!( - from_json_value::>(json) + from_json_value::>>(json) .unwrap() .deserialize() .unwrap(), - AcceptEvent { - content: AcceptEventContent { + ToDeviceEvent { + sender, + content: AcceptToDeviceEventContent { transaction_id, method: AcceptMethod::Custom(CustomContent { method, @@ -290,6 +303,7 @@ mod tests { }) } } if transaction_id == "456" + && sender == user_id!("@example:localhost") && method == "m.sas.custom" && fields.get("test").unwrap() == &JsonValue::from("field") ); diff --git a/ruma-events/src/key/verification/cancel.rs b/ruma-events/src/key/verification/cancel.rs index 53105ef0..d9ce80a9 100644 --- a/ruma-events/src/key/verification/cancel.rs +++ b/ruma-events/src/key/verification/cancel.rs @@ -4,17 +4,10 @@ use ruma_events_macros::BasicEventContent; use ruma_serde::StringEnum; use serde::{Deserialize, Serialize}; -use crate::BasicEvent; - -/// Cancels a key verification process/request. -/// -/// Typically sent as a to-device event. -pub type CancelEvent = BasicEvent; - /// The payload for `CancelEvent`. #[derive(Clone, Debug, Deserialize, Serialize, BasicEventContent)] #[ruma_event(type = "m.key.verification.cancel")] -pub struct CancelEventContent { +pub struct CancelToDeviceEventContent { /// The opaque identifier for the verification process/request. pub transaction_id: String, diff --git a/ruma-events/src/key/verification/key.rs b/ruma-events/src/key/verification/key.rs index f4cb18ca..67034d8f 100644 --- a/ruma-events/src/key/verification/key.rs +++ b/ruma-events/src/key/verification/key.rs @@ -3,17 +3,10 @@ use ruma_events_macros::BasicEventContent; use serde::{Deserialize, Serialize}; -use crate::BasicEvent; - -/// Sends the ephemeral public key for a device to the partner device. -/// -/// Typically sent as a to-device event. -pub type KeyEvent = BasicEvent; - /// The payload for `KeyEvent`. #[derive(Clone, Debug, Deserialize, Serialize, BasicEventContent)] #[ruma_event(type = "m.key.verification.key")] -pub struct KeyEventContent { +pub struct KeyToDeviceEventContent { /// An opaque identifier for the verification process. /// /// Must be the same as the one used for the *m.key.verification.start* message. diff --git a/ruma-events/src/key/verification/mac.rs b/ruma-events/src/key/verification/mac.rs index 3f7e75bf..b66e5667 100644 --- a/ruma-events/src/key/verification/mac.rs +++ b/ruma-events/src/key/verification/mac.rs @@ -5,17 +5,10 @@ use std::collections::BTreeMap; use ruma_events_macros::BasicEventContent; use serde::{Deserialize, Serialize}; -use crate::BasicEvent; - -/// Sends the MAC of a device's key to the partner device. -/// -/// Typically sent as a to-device event. -pub type MacEvent = BasicEvent; - /// The payload for `MacEvent`. #[derive(Clone, Debug, Deserialize, Serialize, BasicEventContent)] #[ruma_event(type = "m.key.verification.mac")] -pub struct MacEventContent { +pub struct MacToDeviceEventContent { /// An opaque identifier for the verification process. /// /// Must be the same as the one used for the *m.key.verification.start* message. diff --git a/ruma-events/src/key/verification/request.rs b/ruma-events/src/key/verification/request.rs index b651d90d..cc37347f 100644 --- a/ruma-events/src/key/verification/request.rs +++ b/ruma-events/src/key/verification/request.rs @@ -7,17 +7,11 @@ use ruma_identifiers::DeviceIdBox; use serde::{Deserialize, Serialize}; use super::VerificationMethod; -use crate::BasicEvent; - -/// Requests a key verification with another user's devices. -/// -/// Typically sent as a to-device event. -pub type RequestEvent = BasicEvent; /// The payload for `RequestEvent`. #[derive(Clone, Debug, Deserialize, Serialize, BasicEventContent)] #[ruma_event(type = "m.key.verification.request")] -pub struct RequestEventContent { +pub struct RequestToDeviceEventContent { /// The device ID which is initiating the request. pub from_device: DeviceIdBox, diff --git a/ruma-events/src/key/verification/start.rs b/ruma-events/src/key/verification/start.rs index 70b6dca6..57529231 100644 --- a/ruma-events/src/key/verification/start.rs +++ b/ruma-events/src/key/verification/start.rs @@ -10,17 +10,12 @@ use serde_json::Value as JsonValue; use super::{ HashAlgorithm, KeyAgreementProtocol, MessageAuthenticationCode, ShortAuthenticationString, }; -use crate::{BasicEvent, InvalidInput}; - -/// Begins an SAS key verification process. -/// -/// Typically sent as a to-device event. -pub type StartEvent = BasicEvent; +use crate::InvalidInput; /// The payload of an *m.key.verification.start* event. #[derive(Clone, Debug, Deserialize, Serialize, BasicEventContent)] #[ruma_event(type = "m.key.verification.start")] -pub struct StartEventContent { +pub struct StartToDeviceEventContent { /// The device ID which is initiating the process. pub from_device: DeviceIdBox, @@ -179,16 +174,18 @@ mod tests { use std::collections::BTreeMap; use matches::assert_matches; + use ruma_identifiers::user_id; + use ruma_serde::Raw; use serde_json::{ from_value as from_json_value, json, to_value as to_json_value, Value as JsonValue, }; use super::{ CustomContent, HashAlgorithm, KeyAgreementProtocol, MSasV1Content, MSasV1ContentInit, - MessageAuthenticationCode, ShortAuthenticationString, StartEvent, StartEventContent, - StartMethod, + MessageAuthenticationCode, ShortAuthenticationString, StartMethod, + StartToDeviceEventContent, }; - use ruma_serde::Raw; + use crate::ToDeviceEvent; #[test] fn invalid_m_sas_v1_content_missing_required_key_agreement_protocols() { @@ -248,7 +245,7 @@ mod tests { #[test] fn serialization() { - let key_verification_start_content = StartEventContent { + let key_verification_start_content = StartToDeviceEventContent { from_device: "123".into(), transaction_id: "456".into(), method: StartMethod::MSasV1( @@ -262,7 +259,7 @@ mod tests { ), }; - let key_verification_start = StartEvent { content: key_verification_start_content }; + let sender = user_id!("@example:localhost"); let json_data = json!({ "content": { @@ -274,11 +271,17 @@ mod tests { "message_authentication_codes": ["hkdf-hmac-sha256"], "short_authentication_string": ["decimal"] }, - "type": "m.key.verification.start" + "type": "m.key.verification.start", + "sender": sender }); + let key_verification_start = + ToDeviceEvent { sender, content: key_verification_start_content }; + assert_eq!(to_json_value(&key_verification_start).unwrap(), json_data); + let sender = user_id!("@example:localhost"); + let json_data = json!({ "content": { "from_device": "123", @@ -286,10 +289,11 @@ mod tests { "method": "m.sas.custom", "test": "field", }, - "type": "m.key.verification.start" + "type": "m.key.verification.start", + "sender": sender }); - let key_verification_start_content = StartEventContent { + let key_verification_start_content = StartToDeviceEventContent { from_device: "123".into(), transaction_id: "456".into(), method: StartMethod::Custom(CustomContent { @@ -300,7 +304,8 @@ mod tests { }), }; - let key_verification_start = StartEvent { content: key_verification_start_content }; + let key_verification_start = + ToDeviceEvent { sender, content: key_verification_start_content }; assert_eq!(to_json_value(&key_verification_start).unwrap(), json_data); } @@ -319,11 +324,11 @@ mod tests { // Deserialize the content struct separately to verify `TryFromRaw` is implemented for it. assert_matches!( - from_json_value::>(json) + from_json_value::>(json) .unwrap() .deserialize() .unwrap(), - StartEventContent { + StartToDeviceEventContent { from_device, transaction_id, method: StartMethod::MSasV1(MSasV1Content { @@ -340,6 +345,8 @@ mod tests { && short_authentication_string == vec![ShortAuthenticationString::Decimal] ); + let sender = user_id!("@example:localhost"); + let json = json!({ "content": { "from_device": "123", @@ -350,16 +357,18 @@ mod tests { "message_authentication_codes": ["hkdf-hmac-sha256"], "short_authentication_string": ["decimal"] }, - "type": "m.key.verification.start" + "type": "m.key.verification.start", + "sender": sender }); assert_matches!( - from_json_value::>(json) + from_json_value::>>(json) .unwrap() .deserialize() .unwrap(), - StartEvent { - content: StartEventContent { + ToDeviceEvent { + sender, + content: StartToDeviceEventContent { from_device, transaction_id, method: StartMethod::MSasV1(MSasV1Content { @@ -370,6 +379,7 @@ mod tests { }) } } if from_device == "123" + && sender == user_id!("@example:localhost") && transaction_id == "456" && hashes == vec![HashAlgorithm::Sha256] && key_agreement_protocols == vec![KeyAgreementProtocol::Curve25519] @@ -377,6 +387,8 @@ mod tests { && short_authentication_string == vec![ShortAuthenticationString::Decimal] ); + let sender = user_id!("@example:localhost"); + let json = json!({ "content": { "from_device": "123", @@ -384,16 +396,18 @@ mod tests { "method": "m.sas.custom", "test": "field", }, - "type": "m.key.verification.start" + "type": "m.key.verification.start", + "sender": sender, }); assert_matches!( - from_json_value::>(json) + from_json_value::>>(json) .unwrap() .deserialize() .unwrap(), - StartEvent { - content: StartEventContent { + ToDeviceEvent { + sender, + content: StartToDeviceEventContent { from_device, transaction_id, method: StartMethod::Custom(CustomContent { @@ -402,6 +416,7 @@ mod tests { }) } } if from_device == "123" + && sender == user_id!("@example:localhost") && transaction_id == "456" && method == "m.sas.custom" && fields.get("test").unwrap() == &JsonValue::from("field") @@ -411,7 +426,7 @@ mod tests { #[test] fn deserialization_failure() { // Ensure that invalid JSON creates a `serde_json::Error` and not `InvalidEvent` - assert!(serde_json::from_str::>("{").is_err()); + assert!(serde_json::from_str::>("{").is_err()); } // TODO this fails because the error is a Validation error not deserialization? diff --git a/ruma-events/src/room/encrypted.rs b/ruma-events/src/room/encrypted.rs index 4765d525..802eacde 100644 --- a/ruma-events/src/room/encrypted.rs +++ b/ruma-events/src/room/encrypted.rs @@ -27,6 +27,9 @@ pub enum EncryptedEventContent { MegolmV1AesSha2(MegolmV1AesSha2Content), } +/// The to-device version of the payload for the `EncryptedEvent`. +pub type EncryptedToDeviceEventContent = EncryptedEventContent; + /// The payload for `EncryptedEvent` using the *m.olm.v1.curve25519-aes-sha2* algorithm. #[derive(Clone, Debug, Serialize, Deserialize)] #[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] diff --git a/ruma-events/src/room_key.rs b/ruma-events/src/room_key.rs index 894b78c5..400013b2 100644 --- a/ruma-events/src/room_key.rs +++ b/ruma-events/src/room_key.rs @@ -6,9 +6,7 @@ use serde::{Deserialize, Serialize}; use crate::BasicEvent; -/// This event type is used to exchange keys for end-to-end encryption. -/// -/// Typically it is encrypted as an *m.room.encrypted* event, then sent as a to-device event. +/// Typically encrypted as an *m.room.encrypted* event, then sent as a to-device event. pub type RoomKeyEvent = BasicEvent; /// The payload for `RoomKeyEvent`. @@ -30,6 +28,9 @@ pub struct RoomKeyEventContent { pub session_key: String, } +/// The to-device version of the payload for the `RoomKeyEvent`. +pub type RoomKeyToDeviceEventContent = RoomKeyEventContent; + #[cfg(test)] mod tests { use ruma_identifiers::{room_id, EventEncryptionAlgorithm}; diff --git a/ruma-events/src/room_key_request.rs b/ruma-events/src/room_key_request.rs index 0e3a8373..c0a7ca3f 100644 --- a/ruma-events/src/room_key_request.rs +++ b/ruma-events/src/room_key_request.rs @@ -5,17 +5,10 @@ use ruma_identifiers::{DeviceIdBox, EventEncryptionAlgorithm, RoomId}; use ruma_serde::StringEnum; use serde::{Deserialize, Serialize}; -use crate::BasicEvent; - -/// This event type is used to request keys for end-to-end encryption. -/// -/// It is sent as an unencrypted to-device event. -pub type RoomKeyRequestEvent = BasicEvent; - /// The payload for `RoomKeyRequestEvent`. #[derive(Clone, Debug, Deserialize, Serialize, BasicEventContent)] #[ruma_event(type = "m.room_key_request")] -pub struct RoomKeyRequestEventContent { +pub struct RoomKeyRequestToDeviceEventContent { /// Whether this is a new key request or a cancellation of a previous request. pub action: Action,