matrix_sdk_crypto/types/events/
room_key_request.rs1use std::collections::BTreeMap;
18
19use ruma::{
20 events::AnyToDeviceEventContent, serde::JsonCastable, OwnedDeviceId, OwnedRoomId,
21 OwnedTransactionId, RoomId,
22};
23use serde::{Deserialize, Serialize};
24use serde_json::Value;
25use vodozemac::Curve25519PublicKey;
26
27use super::{EventType, ToDeviceEvent};
28use crate::types::{serde_curve_key_option, EventEncryptionAlgorithm};
29
30pub type RoomKeyRequestEvent = ToDeviceEvent<RoomKeyRequestContent>;
32
33impl Clone for RoomKeyRequestEvent {
34 fn clone(&self) -> Self {
35 Self {
36 sender: self.sender.clone(),
37 content: self.content.clone(),
38 other: self.other.clone(),
39 }
40 }
41}
42
43#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
45pub struct RoomKeyRequestContent {
46 #[serde(flatten)]
48 pub action: Action,
49 pub requesting_device_id: OwnedDeviceId,
51 pub request_id: OwnedTransactionId,
55}
56
57impl RoomKeyRequestContent {
58 pub fn new_request(
61 info: RequestedKeyInfo,
62 requesting_device_id: OwnedDeviceId,
63 request_id: OwnedTransactionId,
64 ) -> Self {
65 Self { action: Action::Request(info), requesting_device_id, request_id }
66 }
67}
68
69impl EventType for RoomKeyRequestContent {
70 const EVENT_TYPE: &'static str = "m.room_key_request";
71}
72
73impl JsonCastable<AnyToDeviceEventContent> for RoomKeyRequestContent {}
74
75#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
77#[serde(tag = "action", content = "body")]
78pub enum Action {
79 #[serde(rename = "request_cancellation")]
81 Cancellation,
82 #[serde(rename = "request")]
84 Request(RequestedKeyInfo),
85}
86
87#[derive(Clone, Debug, Deserialize, PartialEq, Eq)]
89#[serde(try_from = "RequestedKeyInfoHelper")]
90pub enum RequestedKeyInfo {
91 MegolmV1AesSha2(MegolmV1AesSha2Content),
93 #[cfg(feature = "experimental-algorithms")]
95 MegolmV2AesSha2(MegolmV2AesSha2Content),
96 Unknown(UnknownRoomKeyRequest),
99}
100
101impl RequestedKeyInfo {
102 pub fn algorithm(&self) -> EventEncryptionAlgorithm {
104 match self {
105 RequestedKeyInfo::MegolmV1AesSha2(_) => EventEncryptionAlgorithm::MegolmV1AesSha2,
106 #[cfg(feature = "experimental-algorithms")]
107 RequestedKeyInfo::MegolmV2AesSha2(_) => EventEncryptionAlgorithm::MegolmV2AesSha2,
108 RequestedKeyInfo::Unknown(c) => c.algorithm.to_owned(),
109 }
110 }
111}
112
113impl From<SupportedKeyInfo> for RequestedKeyInfo {
114 fn from(s: SupportedKeyInfo) -> Self {
115 match s {
116 SupportedKeyInfo::MegolmV1AesSha2(c) => Self::MegolmV1AesSha2(c),
117 #[cfg(feature = "experimental-algorithms")]
118 SupportedKeyInfo::MegolmV2AesSha2(c) => Self::MegolmV2AesSha2(c),
119 }
120 }
121}
122
123impl From<MegolmV1AesSha2Content> for RequestedKeyInfo {
124 fn from(c: MegolmV1AesSha2Content) -> Self {
125 Self::MegolmV1AesSha2(c)
126 }
127}
128
129#[cfg(feature = "experimental-algorithms")]
130impl From<MegolmV2AesSha2Content> for RequestedKeyInfo {
131 fn from(c: MegolmV2AesSha2Content) -> Self {
132 Self::MegolmV2AesSha2(c)
133 }
134}
135
136#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
141#[serde(try_from = "RequestedKeyInfo", into = "RequestedKeyInfo")]
142pub enum SupportedKeyInfo {
143 MegolmV1AesSha2(MegolmV1AesSha2Content),
145 #[cfg(feature = "experimental-algorithms")]
147 MegolmV2AesSha2(MegolmV2AesSha2Content),
148}
149
150impl SupportedKeyInfo {
151 pub fn algorithm(&self) -> EventEncryptionAlgorithm {
153 match self {
154 Self::MegolmV1AesSha2(_) => EventEncryptionAlgorithm::MegolmV1AesSha2,
155 #[cfg(feature = "experimental-algorithms")]
156 Self::MegolmV2AesSha2(_) => EventEncryptionAlgorithm::MegolmV2AesSha2,
157 }
158 }
159
160 pub fn room_id(&self) -> &RoomId {
162 match self {
163 Self::MegolmV1AesSha2(c) => &c.room_id,
164 #[cfg(feature = "experimental-algorithms")]
165 Self::MegolmV2AesSha2(c) => &c.room_id,
166 }
167 }
168
169 pub fn session_id(&self) -> &str {
171 match self {
172 Self::MegolmV1AesSha2(c) => &c.session_id,
173 #[cfg(feature = "experimental-algorithms")]
174 Self::MegolmV2AesSha2(c) => &c.session_id,
175 }
176 }
177}
178
179impl TryFrom<RequestedKeyInfo> for SupportedKeyInfo {
180 type Error = &'static str;
181
182 fn try_from(value: RequestedKeyInfo) -> Result<Self, Self::Error> {
183 match value {
184 RequestedKeyInfo::MegolmV1AesSha2(c) => Ok(Self::MegolmV1AesSha2(c)),
185 #[cfg(feature = "experimental-algorithms")]
186 RequestedKeyInfo::MegolmV2AesSha2(c) => Ok(Self::MegolmV2AesSha2(c)),
187 RequestedKeyInfo::Unknown(_) => Err("Unsupported algorithm for a room key request"),
188 }
189 }
190}
191
192impl From<MegolmV1AesSha2Content> for SupportedKeyInfo {
193 fn from(c: MegolmV1AesSha2Content) -> Self {
194 Self::MegolmV1AesSha2(c)
195 }
196}
197
198#[cfg(feature = "experimental-algorithms")]
199impl From<MegolmV2AesSha2Content> for SupportedKeyInfo {
200 fn from(c: MegolmV2AesSha2Content) -> Self {
201 Self::MegolmV2AesSha2(c)
202 }
203}
204
205#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
207pub struct MegolmV1AesSha2Content {
208 pub room_id: OwnedRoomId,
210
211 #[serde(default, with = "serde_curve_key_option", skip_serializing_if = "Option::is_none")]
213 pub sender_key: Option<Curve25519PublicKey>,
214
215 pub session_id: String,
217}
218
219#[cfg(feature = "experimental-algorithms")]
221#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
222pub struct MegolmV2AesSha2Content {
223 pub room_id: OwnedRoomId,
225
226 pub session_id: String,
228}
229
230#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
232pub struct UnknownRoomKeyRequest {
233 pub algorithm: EventEncryptionAlgorithm,
235
236 #[serde(flatten)]
238 other: BTreeMap<String, Value>,
239}
240
241#[derive(Clone, Debug, Serialize, Deserialize)]
242struct RequestedKeyInfoHelper {
243 pub algorithm: EventEncryptionAlgorithm,
244 #[serde(flatten)]
245 other: Value,
246}
247
248impl Serialize for RequestedKeyInfo {
249 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
250 where
251 S: serde::Serializer,
252 {
253 let other = match self {
254 Self::MegolmV1AesSha2(r) => {
255 serde_json::to_value(r).map_err(serde::ser::Error::custom)?
256 }
257 #[cfg(feature = "experimental-algorithms")]
258 Self::MegolmV2AesSha2(r) => {
259 serde_json::to_value(r).map_err(serde::ser::Error::custom)?
260 }
261 Self::Unknown(r) => {
262 serde_json::to_value(r.other.clone()).map_err(serde::ser::Error::custom)?
263 }
264 };
265
266 let helper = RequestedKeyInfoHelper { algorithm: self.algorithm(), other };
267
268 helper.serialize(serializer)
269 }
270}
271
272impl TryFrom<RequestedKeyInfoHelper> for RequestedKeyInfo {
273 type Error = serde_json::Error;
274
275 fn try_from(value: RequestedKeyInfoHelper) -> Result<Self, Self::Error> {
276 Ok(match value.algorithm {
277 EventEncryptionAlgorithm::MegolmV1AesSha2 => {
278 Self::MegolmV1AesSha2(serde_json::from_value(value.other)?)
279 }
280 #[cfg(feature = "experimental-algorithms")]
281 EventEncryptionAlgorithm::MegolmV2AesSha2 => {
282 Self::MegolmV2AesSha2(serde_json::from_value(value.other)?)
283 }
284 _ => Self::Unknown(UnknownRoomKeyRequest {
285 algorithm: value.algorithm,
286 other: serde_json::from_value(value.other)?,
287 }),
288 })
289 }
290}
291
292#[cfg(test)]
293mod tests {
294 use assert_matches::assert_matches;
295 use serde_json::{json, Value};
296
297 use super::{Action, RequestedKeyInfo, RoomKeyRequestEvent};
298
299 pub fn json() -> Value {
300 json!({
301 "sender": "@alice:example.org",
302 "content": {
303 "action": "request",
304 "body": {
305 "algorithm": "m.megolm.v1.aes-sha2",
306 "room_id": "!Cuyf34gef24t:localhost",
307 "sender_key": "RF3s+E7RkTQTGF2d8Deol0FkQvgII2aJDf3/Jp5mxVU",
308 "session_id": "X3lUlvLELLYxeTx4yOVu6UDpasGEVO0Jbu+QFnm0cKQ"
309 },
310 "request_id": "1495474790150.19",
311 "requesting_device_id": "RJYKSTBOIE"
312 },
313 "type": "m.room_key_request"
314 })
315 }
316
317 pub fn json_cancellation() -> Value {
318 json!({
319 "sender": "@alice:example.org",
320 "content": {
321 "action": "request_cancellation",
322 "request_id": "1495474790150.19",
323 "requesting_device_id": "RJYKSTBOIE"
324 },
325 "type": "m.room_key_request"
326 })
327 }
328
329 #[cfg(feature = "experimental-algorithms")]
330 pub fn json_megolm_v2() -> Value {
331 json!({
332 "sender": "@alice:example.org",
333 "content": {
334 "action": "request",
335 "body": {
336 "algorithm": "m.megolm.v2.aes-sha2",
337 "room_id": "!Cuyf34gef24t:localhost",
338 "session_id": "X3lUlvLELLYxeTx4yOVu6UDpasGEVO0Jbu+QFnm0cKQ"
339 },
340 "request_id": "1495474790150.19",
341 "requesting_device_id": "RJYKSTBOIE"
342 },
343 "type": "m.room_key_request"
344 })
345 }
346
347 #[test]
348 fn deserialization() -> Result<(), serde_json::Error> {
349 let json = json();
350 let event: RoomKeyRequestEvent = serde_json::from_value(json.clone())?;
351
352 assert_matches!(
353 event.content.action,
354 Action::Request(RequestedKeyInfo::MegolmV1AesSha2(_))
355 );
356 let serialized = serde_json::to_value(event)?;
357 assert_eq!(json, serialized);
358
359 let json = json_cancellation();
360 let event: RoomKeyRequestEvent = serde_json::from_value(json.clone())?;
361
362 assert_matches!(event.content.action, Action::Cancellation);
363 let serialized = serde_json::to_value(event)?;
364 assert_eq!(json, serialized);
365
366 Ok(())
367 }
368
369 #[test]
370 #[cfg(feature = "experimental-algorithms")]
371 fn deserialization_megolm_v2() -> Result<(), serde_json::Error> {
372 let json = json_megolm_v2();
373 let event: RoomKeyRequestEvent = serde_json::from_value(json.clone())?;
374
375 assert_matches!(
376 event.content.action,
377 Action::Request(RequestedKeyInfo::MegolmV2AesSha2(_))
378 );
379
380 let serialized = serde_json::to_value(event)?;
381 assert_eq!(json, serialized);
382
383 Ok(())
384 }
385
386 #[test]
387 fn deserialization_missing_sender_key() -> Result<(), serde_json::Error> {
388 let json = json!({
389 "sender": "@alice:example.org",
390 "content": {
391 "action": "request",
392 "body": {
393 "algorithm": "m.megolm.v1.aes-sha2",
394 "room_id": "!Cuyf34gef24t:localhost",
395 "session_id": "X3lUlvLELLYxeTx4yOVu6UDpasGEVO0Jbu+QFnm0cKQ"
396 },
397 "request_id": "1495474790150.19",
398 "requesting_device_id": "RJYKSTBOIE"
399 },
400 "type": "m.room_key_request"
401 });
402
403 let event: RoomKeyRequestEvent = serde_json::from_value(json.clone())?;
404
405 assert_matches!(
406 event.content.action,
407 Action::Request(RequestedKeyInfo::MegolmV1AesSha2(_))
408 );
409 let serialized = serde_json::to_value(event)?;
410 assert_eq!(json, serialized);
411
412 Ok(())
413 }
414}