matrix_sdk_crypto/types/events/
room_key.rs1use std::collections::BTreeMap;
18
19use ruma::{serde::Raw, OwnedRoomId, RoomId};
20use serde::{Deserialize, Serialize};
21use serde_json::{value::to_raw_value, Value};
22use vodozemac::megolm::SessionKey;
23
24use super::{EventType, ToDeviceEvent};
25#[cfg(doc)]
26use crate::olm::InboundGroupSession;
27use crate::types::EventEncryptionAlgorithm;
28
29pub type RoomKeyEvent = ToDeviceEvent<RoomKeyContent>;
31
32impl RoomKeyEvent {
33 pub fn algorithm(&self) -> EventEncryptionAlgorithm {
35 self.content.algorithm()
36 }
37}
38
39impl EventType for RoomKeyContent {
40 const EVENT_TYPE: &'static str = "m.room_key";
41}
42
43#[derive(Debug, Deserialize)]
55#[serde(try_from = "RoomKeyHelper")]
56pub enum RoomKeyContent {
57 MegolmV1AesSha2(Box<MegolmV1AesSha2Content>),
59 #[cfg(feature = "experimental-algorithms")]
61 MegolmV2AesSha2(Box<MegolmV1AesSha2Content>),
62 Unknown(UnknownRoomKey),
64}
65
66impl RoomKeyContent {
67 pub fn algorithm(&self) -> EventEncryptionAlgorithm {
69 match &self {
70 RoomKeyContent::MegolmV1AesSha2(_) => EventEncryptionAlgorithm::MegolmV1AesSha2,
71 #[cfg(feature = "experimental-algorithms")]
72 RoomKeyContent::MegolmV2AesSha2(_) => EventEncryptionAlgorithm::MegolmV2AesSha2,
73 RoomKeyContent::Unknown(c) => c.algorithm.to_owned(),
74 }
75 }
76
77 pub(super) fn serialize_zeroized(&self) -> Result<Raw<RoomKeyContent>, serde_json::Error> {
78 #[derive(Serialize)]
79 struct Helper<'a> {
80 pub room_id: &'a RoomId,
81 pub session_id: &'a str,
82 pub session_key: &'a str,
83 #[serde(flatten)]
84 other: &'a BTreeMap<String, Value>,
85 }
86
87 let serialize_helper = |content: &MegolmV1AesSha2Content| {
88 let helper = Helper {
89 room_id: &content.room_id,
90 session_id: &content.session_id,
91 session_key: "",
92 other: &content.other,
93 };
94
95 let helper =
96 RoomKeyHelper { algorithm: self.algorithm(), other: serde_json::to_value(helper)? };
97
98 Ok(Raw::from_json(to_raw_value(&helper)?))
99 };
100
101 match self {
102 RoomKeyContent::MegolmV1AesSha2(c) => serialize_helper(c),
103 #[cfg(feature = "experimental-algorithms")]
104 RoomKeyContent::MegolmV2AesSha2(c) => serialize_helper(c),
105 RoomKeyContent::Unknown(c) => Ok(Raw::from_json(to_raw_value(&c)?)),
106 }
107 }
108}
109
110#[derive(Deserialize, Serialize)]
112pub struct MegolmV1AesSha2Content {
113 pub room_id: OwnedRoomId,
115 pub session_id: String,
117 pub session_key: SessionKey,
122 #[serde(default, rename = "org.matrix.msc3061.shared_history")]
128 pub shared_history: bool,
129 #[serde(flatten)]
131 other: BTreeMap<String, Value>,
132}
133
134impl MegolmV1AesSha2Content {
135 pub fn new(
137 room_id: OwnedRoomId,
138 session_id: String,
139 session_key: SessionKey,
140 shared_history: bool,
141 ) -> Self {
142 Self { room_id, session_id, session_key, other: Default::default(), shared_history }
143 }
144}
145
146#[cfg(not(tarpaulin_include))]
147impl std::fmt::Debug for MegolmV1AesSha2Content {
148 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
149 f.debug_struct("MegolmV1AesSha2Content")
150 .field("room_id", &self.room_id)
151 .field("session_id", &self.session_id)
152 .finish_non_exhaustive()
153 }
154}
155
156pub type MegolmV2AesSha2Content = MegolmV1AesSha2Content;
158
159#[derive(Clone, Debug, Serialize, Deserialize)]
161pub struct UnknownRoomKey {
162 pub algorithm: EventEncryptionAlgorithm,
164 #[serde(flatten)]
166 other: BTreeMap<String, Value>,
167}
168
169#[derive(Deserialize, Serialize)]
170struct RoomKeyHelper {
171 algorithm: EventEncryptionAlgorithm,
172 #[serde(flatten)]
173 other: Value,
174}
175
176impl TryFrom<RoomKeyHelper> for RoomKeyContent {
177 type Error = serde_json::Error;
178
179 fn try_from(value: RoomKeyHelper) -> Result<Self, Self::Error> {
180 Ok(match value.algorithm {
181 EventEncryptionAlgorithm::MegolmV1AesSha2 => {
182 let content: MegolmV1AesSha2Content = serde_json::from_value(value.other)?;
183 Self::MegolmV1AesSha2(content.into())
184 }
185 #[cfg(feature = "experimental-algorithms")]
186 EventEncryptionAlgorithm::MegolmV2AesSha2 => {
187 let content: MegolmV2AesSha2Content = serde_json::from_value(value.other)?;
188 Self::MegolmV2AesSha2(content.into())
189 }
190 _ => Self::Unknown(UnknownRoomKey {
191 algorithm: value.algorithm,
192 other: serde_json::from_value(value.other)?,
193 }),
194 })
195 }
196}
197
198impl Serialize for RoomKeyContent {
199 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
200 where
201 S: serde::Serializer,
202 {
203 let helper = match self {
204 Self::MegolmV1AesSha2(r) => RoomKeyHelper {
205 algorithm: EventEncryptionAlgorithm::MegolmV1AesSha2,
206 other: serde_json::to_value(r).map_err(serde::ser::Error::custom)?,
207 },
208 #[cfg(feature = "experimental-algorithms")]
209 Self::MegolmV2AesSha2(r) => RoomKeyHelper {
210 algorithm: EventEncryptionAlgorithm::MegolmV2AesSha2,
211 other: serde_json::to_value(r).map_err(serde::ser::Error::custom)?,
212 },
213 Self::Unknown(r) => RoomKeyHelper {
214 algorithm: r.algorithm.clone(),
215 other: serde_json::to_value(r.other.clone()).map_err(serde::ser::Error::custom)?,
216 },
217 };
218
219 helper.serialize(serializer)
220 }
221}
222
223#[cfg(test)]
224pub(super) mod tests {
225 use assert_matches::assert_matches;
226 use serde_json::{json, Value};
227 use similar_asserts::assert_eq;
228
229 use super::RoomKeyEvent;
230 use crate::types::events::room_key::RoomKeyContent;
231
232 pub fn json() -> Value {
233 json!({
234 "sender": "@alice:example.org",
235 "content": {
236 "m.custom": "something custom",
237 "algorithm": "m.megolm.v1.aes-sha2",
238 "room_id": "!Cuyf34gef24t:localhost",
239 "org.matrix.msc3061.shared_history": false,
240 "session_id": "ZFD6+OmV7fVCsJ7Gap8UnORH8EnmiAkes8FAvQuCw/I",
241 "session_key": "AgAAAADNp1EbxXYOGmJtyX4AkD1bvJvAUyPkbIaKxtnGKjv\
242 SQ3E/4mnuqdM4vsmNzpO1EeWzz1rDkUpYhYE9kP7sJhgLXi\
243 jVv80fMPHfGc49hPdu8A+xnwD4SQiYdFmSWJOIqsxeo/fiH\
244 tino//CDQENtcKuEt0I9s0+Kk4YSH310Szse2RQ+vjple31\
245 QrCexmqfFJzkR/BJ5ogJHrPBQL0LgsPyglIbMTLg7qygIaY\
246 U5Fe2QdKMH7nTZPNIRHh1RaMfHVETAUJBax88EWZBoifk80\
247 gdHUwHSgMk77vCc2a5KHKLDA",
248 },
249 "type": "m.room_key",
250 "m.custom.top": "something custom in the top",
251 })
252 }
253
254 #[test]
255 fn deserialization() -> Result<(), serde_json::Error> {
256 let json = json();
257 let event: RoomKeyEvent = serde_json::from_value(json.clone())?;
258
259 assert_matches!(event.content, RoomKeyContent::MegolmV1AesSha2(_));
260 let serialized = serde_json::to_value(event)?;
261 assert_eq!(json, serialized);
262
263 Ok(())
264 }
265}