matrix_sdk_crypto/types/events/
room_key.rsuse std::collections::BTreeMap;
use ruma::{serde::Raw, OwnedRoomId, RoomId};
use serde::{Deserialize, Serialize};
use serde_json::{value::to_raw_value, Value};
use vodozemac::megolm::SessionKey;
use super::{EventType, ToDeviceEvent};
use crate::types::EventEncryptionAlgorithm;
pub type RoomKeyEvent = ToDeviceEvent<RoomKeyContent>;
impl RoomKeyEvent {
pub fn algorithm(&self) -> EventEncryptionAlgorithm {
self.content.algorithm()
}
}
impl EventType for RoomKeyContent {
const EVENT_TYPE: &'static str = "m.room_key";
}
#[derive(Debug, Deserialize)]
#[serde(try_from = "RoomKeyHelper")]
pub enum RoomKeyContent {
MegolmV1AesSha2(Box<MegolmV1AesSha2Content>),
#[cfg(feature = "experimental-algorithms")]
MegolmV2AesSha2(Box<MegolmV1AesSha2Content>),
Unknown(UnknownRoomKey),
}
impl RoomKeyContent {
pub fn algorithm(&self) -> EventEncryptionAlgorithm {
match &self {
RoomKeyContent::MegolmV1AesSha2(_) => EventEncryptionAlgorithm::MegolmV1AesSha2,
#[cfg(feature = "experimental-algorithms")]
RoomKeyContent::MegolmV2AesSha2(_) => EventEncryptionAlgorithm::MegolmV2AesSha2,
RoomKeyContent::Unknown(c) => c.algorithm.to_owned(),
}
}
pub(super) fn serialize_zeroized(&self) -> Result<Raw<RoomKeyContent>, serde_json::Error> {
#[derive(Serialize)]
struct Helper<'a> {
pub room_id: &'a RoomId,
pub session_id: &'a str,
pub session_key: &'a str,
#[serde(flatten)]
other: &'a BTreeMap<String, Value>,
}
let serialize_helper = |content: &MegolmV1AesSha2Content| {
let helper = Helper {
room_id: &content.room_id,
session_id: &content.session_id,
session_key: "",
other: &content.other,
};
let helper =
RoomKeyHelper { algorithm: self.algorithm(), other: serde_json::to_value(helper)? };
Ok(Raw::from_json(to_raw_value(&helper)?))
};
match self {
RoomKeyContent::MegolmV1AesSha2(c) => serialize_helper(c),
#[cfg(feature = "experimental-algorithms")]
RoomKeyContent::MegolmV2AesSha2(c) => serialize_helper(c),
RoomKeyContent::Unknown(c) => Ok(Raw::from_json(to_raw_value(&c)?)),
}
}
}
#[derive(Deserialize, Serialize)]
pub struct MegolmV1AesSha2Content {
pub room_id: OwnedRoomId,
pub session_id: String,
pub session_key: SessionKey,
#[serde(flatten)]
other: BTreeMap<String, Value>,
}
impl MegolmV1AesSha2Content {
pub fn new(room_id: OwnedRoomId, session_id: String, session_key: SessionKey) -> Self {
Self { room_id, session_id, session_key, other: Default::default() }
}
}
#[cfg(not(tarpaulin_include))]
impl std::fmt::Debug for MegolmV1AesSha2Content {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("MegolmV1AesSha2Content")
.field("room_id", &self.room_id)
.field("session_id", &self.session_id)
.finish_non_exhaustive()
}
}
pub type MegolmV2AesSha2Content = MegolmV1AesSha2Content;
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct UnknownRoomKey {
pub algorithm: EventEncryptionAlgorithm,
#[serde(flatten)]
other: BTreeMap<String, Value>,
}
#[derive(Deserialize, Serialize)]
struct RoomKeyHelper {
algorithm: EventEncryptionAlgorithm,
#[serde(flatten)]
other: Value,
}
impl TryFrom<RoomKeyHelper> for RoomKeyContent {
type Error = serde_json::Error;
fn try_from(value: RoomKeyHelper) -> Result<Self, Self::Error> {
Ok(match value.algorithm {
EventEncryptionAlgorithm::MegolmV1AesSha2 => {
let content: MegolmV1AesSha2Content = serde_json::from_value(value.other)?;
Self::MegolmV1AesSha2(content.into())
}
#[cfg(feature = "experimental-algorithms")]
EventEncryptionAlgorithm::MegolmV2AesSha2 => {
let content: MegolmV2AesSha2Content = serde_json::from_value(value.other)?;
Self::MegolmV2AesSha2(content.into())
}
_ => Self::Unknown(UnknownRoomKey {
algorithm: value.algorithm,
other: serde_json::from_value(value.other)?,
}),
})
}
}
impl Serialize for RoomKeyContent {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let helper = match self {
Self::MegolmV1AesSha2(r) => RoomKeyHelper {
algorithm: EventEncryptionAlgorithm::MegolmV1AesSha2,
other: serde_json::to_value(r).map_err(serde::ser::Error::custom)?,
},
#[cfg(feature = "experimental-algorithms")]
Self::MegolmV2AesSha2(r) => RoomKeyHelper {
algorithm: EventEncryptionAlgorithm::MegolmV2AesSha2,
other: serde_json::to_value(r).map_err(serde::ser::Error::custom)?,
},
Self::Unknown(r) => RoomKeyHelper {
algorithm: r.algorithm.clone(),
other: serde_json::to_value(r.other.clone()).map_err(serde::ser::Error::custom)?,
},
};
helper.serialize(serializer)
}
}
#[cfg(test)]
pub(super) mod tests {
use assert_matches::assert_matches;
use serde_json::{json, Value};
use super::RoomKeyEvent;
use crate::types::events::room_key::RoomKeyContent;
pub fn json() -> Value {
json!({
"sender": "@alice:example.org",
"content": {
"m.custom": "something custom",
"algorithm": "m.megolm.v1.aes-sha2",
"room_id": "!Cuyf34gef24t:localhost",
"session_id": "ZFD6+OmV7fVCsJ7Gap8UnORH8EnmiAkes8FAvQuCw/I",
"session_key": "AgAAAADNp1EbxXYOGmJtyX4AkD1bvJvAUyPkbIaKxtnGKjv\
SQ3E/4mnuqdM4vsmNzpO1EeWzz1rDkUpYhYE9kP7sJhgLXi\
jVv80fMPHfGc49hPdu8A+xnwD4SQiYdFmSWJOIqsxeo/fiH\
tino//CDQENtcKuEt0I9s0+Kk4YSH310Szse2RQ+vjple31\
QrCexmqfFJzkR/BJ5ogJHrPBQL0LgsPyglIbMTLg7qygIaY\
U5Fe2QdKMH7nTZPNIRHh1RaMfHVETAUJBax88EWZBoifk80\
gdHUwHSgMk77vCc2a5KHKLDA",
},
"type": "m.room_key",
"m.custom.top": "something custom in the top",
})
}
#[test]
fn deserialization() -> Result<(), serde_json::Error> {
let json = json();
let event: RoomKeyEvent = serde_json::from_value(json.clone())?;
assert_matches!(event.content, RoomKeyContent::MegolmV1AesSha2(_));
let serialized = serde_json::to_value(event)?;
assert_eq!(json, serialized);
Ok(())
}
}