use std::fmt::Debug;
use ruma::{OwnedUserId, UserId};
use serde::{Deserialize, Serialize};
use serde_json::value::RawValue;
use vodozemac::Ed25519PublicKey;
use super::{
dummy::DummyEventContent,
forwarded_room_key::ForwardedRoomKeyContent,
room_key::RoomKeyContent,
room_key_request::{self, SupportedKeyInfo},
secret_send::SecretSendContent,
EventType,
};
use crate::types::{deserialize_ed25519_key, events::from_str, serialize_ed25519_key, DeviceKeys};
pub type DecryptedDummyEvent = DecryptedOlmV1Event<DummyEventContent>;
pub type DecryptedRoomKeyEvent = DecryptedOlmV1Event<RoomKeyContent>;
pub type DecryptedForwardedRoomKeyEvent = DecryptedOlmV1Event<ForwardedRoomKeyContent>;
impl DecryptedForwardedRoomKeyEvent {
pub fn room_key_info(&self) -> Option<SupportedKeyInfo> {
match &self.content {
ForwardedRoomKeyContent::MegolmV1AesSha2(c) => Some(
room_key_request::MegolmV1AesSha2Content {
room_id: c.room_id.to_owned(),
sender_key: c.claimed_sender_key,
session_id: c.session_id.to_owned(),
}
.into(),
),
#[cfg(feature = "experimental-algorithms")]
ForwardedRoomKeyContent::MegolmV2AesSha2(c) => Some(
room_key_request::MegolmV2AesSha2Content {
room_id: c.room_id.to_owned(),
session_id: c.session_id.to_owned(),
}
.into(),
),
ForwardedRoomKeyContent::Unknown(_) => None,
}
}
}
pub type DecryptedSecretSendEvent = DecryptedOlmV1Event<SecretSendContent>;
#[derive(Debug)]
pub enum AnyDecryptedOlmEvent {
RoomKey(DecryptedRoomKeyEvent),
ForwardedRoomKey(DecryptedForwardedRoomKeyEvent),
SecretSend(DecryptedSecretSendEvent),
Dummy(DecryptedDummyEvent),
Custom(Box<ToDeviceCustomEvent>),
}
impl AnyDecryptedOlmEvent {
pub fn sender(&self) -> &UserId {
match self {
AnyDecryptedOlmEvent::RoomKey(e) => &e.sender,
AnyDecryptedOlmEvent::ForwardedRoomKey(e) => &e.sender,
AnyDecryptedOlmEvent::SecretSend(e) => &e.sender,
AnyDecryptedOlmEvent::Custom(e) => &e.sender,
AnyDecryptedOlmEvent::Dummy(e) => &e.sender,
}
}
pub fn recipient(&self) -> &UserId {
match self {
AnyDecryptedOlmEvent::RoomKey(e) => &e.recipient,
AnyDecryptedOlmEvent::ForwardedRoomKey(e) => &e.recipient,
AnyDecryptedOlmEvent::SecretSend(e) => &e.recipient,
AnyDecryptedOlmEvent::Custom(e) => &e.recipient,
AnyDecryptedOlmEvent::Dummy(e) => &e.recipient,
}
}
pub fn keys(&self) -> &OlmV1Keys {
match self {
AnyDecryptedOlmEvent::RoomKey(e) => &e.keys,
AnyDecryptedOlmEvent::ForwardedRoomKey(e) => &e.keys,
AnyDecryptedOlmEvent::SecretSend(e) => &e.keys,
AnyDecryptedOlmEvent::Custom(e) => &e.keys,
AnyDecryptedOlmEvent::Dummy(e) => &e.keys,
}
}
pub fn recipient_keys(&self) -> &OlmV1Keys {
match self {
AnyDecryptedOlmEvent::RoomKey(e) => &e.recipient_keys,
AnyDecryptedOlmEvent::ForwardedRoomKey(e) => &e.recipient_keys,
AnyDecryptedOlmEvent::SecretSend(e) => &e.recipient_keys,
AnyDecryptedOlmEvent::Custom(e) => &e.recipient_keys,
AnyDecryptedOlmEvent::Dummy(e) => &e.recipient_keys,
}
}
pub fn event_type(&self) -> &str {
match self {
AnyDecryptedOlmEvent::Custom(e) => &e.event_type,
AnyDecryptedOlmEvent::RoomKey(e) => e.content.event_type(),
AnyDecryptedOlmEvent::ForwardedRoomKey(e) => e.content.event_type(),
AnyDecryptedOlmEvent::SecretSend(e) => e.content.event_type(),
AnyDecryptedOlmEvent::Dummy(e) => e.content.event_type(),
}
}
pub fn sender_device_keys(&self) -> Option<&DeviceKeys> {
match self {
AnyDecryptedOlmEvent::Custom(_) => None,
AnyDecryptedOlmEvent::RoomKey(e) => e.sender_device_keys.as_ref(),
AnyDecryptedOlmEvent::ForwardedRoomKey(e) => e.sender_device_keys.as_ref(),
AnyDecryptedOlmEvent::SecretSend(e) => e.sender_device_keys.as_ref(),
AnyDecryptedOlmEvent::Dummy(e) => e.sender_device_keys.as_ref(),
}
}
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct DecryptedOlmV1Event<C>
where
C: EventType + Debug + Sized + Serialize,
{
pub sender: OwnedUserId,
pub recipient: OwnedUserId,
pub keys: OlmV1Keys,
pub recipient_keys: OlmV1Keys,
#[serde(alias = "org.matrix.msc4147.device_keys")]
pub sender_device_keys: Option<DeviceKeys>,
pub content: C,
}
impl<C: EventType + Debug + Sized + Serialize> DecryptedOlmV1Event<C> {
#[cfg(test)]
pub fn new(
sender: &UserId,
recipient: &UserId,
key: Ed25519PublicKey,
device_keys: Option<DeviceKeys>,
content: C,
) -> Self {
Self {
sender: sender.to_owned(),
recipient: recipient.to_owned(),
keys: OlmV1Keys { ed25519: key },
recipient_keys: OlmV1Keys { ed25519: key },
sender_device_keys: device_keys,
content,
}
}
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct ToDeviceCustomEvent {
pub sender: OwnedUserId,
pub recipient: OwnedUserId,
pub keys: OlmV1Keys,
pub recipient_keys: OlmV1Keys,
#[serde(rename = "type")]
pub event_type: String,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct OlmV1Keys {
#[serde(
deserialize_with = "deserialize_ed25519_key",
serialize_with = "serialize_ed25519_key"
)]
pub ed25519: Ed25519PublicKey,
}
impl<'de> Deserialize<'de> for AnyDecryptedOlmEvent {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
#[derive(Debug, Deserialize)]
struct Helper<'a> {
#[serde(rename = "type")]
event_type: &'a str,
}
let json = Box::<RawValue>::deserialize(deserializer)?;
let helper: Helper<'_> =
serde_json::from_str(json.get()).map_err(serde::de::Error::custom)?;
let json = json.get();
Ok(match helper.event_type {
"m.room_key" => AnyDecryptedOlmEvent::RoomKey(from_str(json)?),
"m.forwarded_room_key" => AnyDecryptedOlmEvent::ForwardedRoomKey(from_str(json)?),
"m.secret.send" => AnyDecryptedOlmEvent::SecretSend(from_str(json)?),
"m.dummy" => AnyDecryptedOlmEvent::Dummy(from_str(json)?),
_ => AnyDecryptedOlmEvent::Custom(from_str(json)?),
})
}
}
#[cfg(test)]
mod tests {
use std::collections::BTreeMap;
use assert_matches::assert_matches;
use ruma::{device_id, owned_user_id, KeyId};
use serde_json::{json, Value};
use vodozemac::{Curve25519PublicKey, Ed25519PublicKey, Ed25519Signature};
use super::AnyDecryptedOlmEvent;
use crate::types::{
events::olm_v1::DecryptedRoomKeyEvent, DeviceKey, DeviceKeys, EventEncryptionAlgorithm,
Signatures,
};
const ED25519_KEY: &str = "aOfOnlaeMb5GW1TxkZ8pXnblkGMgAvps+lAukrdYaZk";
fn dummy_event() -> Value {
json!({
"sender": "@alice:example.org",
"sender_device": "DEVICEID",
"keys": {
"ed25519": ED25519_KEY,
},
"recipient": "@bob:example.org",
"recipient_keys": {
"ed25519": ED25519_KEY,
},
"content": {},
"type": "m.dummy"
})
}
fn room_key_event() -> Value {
json!({
"sender": "@alice:example.org",
"sender_device": "DEVICEID",
"keys": {
"ed25519": ED25519_KEY,
},
"recipient": "@bob:example.org",
"recipient_keys": {
"ed25519": ED25519_KEY,
},
"content": {
"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"
})
}
fn forwarded_room_key_event() -> Value {
json!({
"sender": "@alice:example.org",
"sender_device": "DEVICEID",
"keys": {
"ed25519": ED25519_KEY,
},
"recipient": "@bob:example.org",
"recipient_keys": {
"ed25519": ED25519_KEY,
},
"content": {
"algorithm": "m.megolm.v1.aes-sha2",
"forwarding_curve25519_key_chain": [
"hPQNcabIABgGnx3/ACv/jmMmiQHoeFfuLB17tzWp6Hw"
],
"room_id": "!Cuyf34gef24t:localhost",
"sender_claimed_ed25519_key": "aj40p+aw64yPIdsxoog8jhPu9i7l7NcFRecuOQblE3Y",
"sender_key": "RF3s+E7RkTQTGF2d8Deol0FkQvgII2aJDf3/Jp5mxVU",
"session_id": "X3lUlvLELLYxeTx4yOVu6UDpasGEVO0Jbu+QFnm0cKQ",
"session_key": "AQAAAAq2JpkMceK5f6JrZPJWwzQTn59zliuIv0F7apVLXDcZCCT\
3LqBjD21sULYEO5YTKdpMVhi9i6ZSZhdvZvp//tzRpDT7wpWVWI\
00Y3EPEjmpm/HfZ4MMAKpk+tzJVuuvfAcHBZgpnxBGzYOc/DAqa\
pK7Tk3t3QJ1UMSD94HfAqlb1JF5QBPwoh0fOvD8pJdanB8zxz05\
tKFdR73/vo2Q/zE3"
},
"type": "m.forwarded_room_key"
})
}
fn secret_send_event() -> Value {
json!({
"sender": "@alice:example.org",
"sender_device": "DEVICEID",
"keys": {
"ed25519": ED25519_KEY,
},
"recipient": "@bob:example.org",
"recipient_keys": {
"ed25519": ED25519_KEY,
},
"content": {
"request_id": "randomly_generated_id_9573",
"secret": "ThisIsASecretDon'tTellAnyone"
},
"type": "m.secret.send"
})
}
fn sender_device_keys() -> (Value, DeviceKeys) {
let sender_device_keys_json = json!({
"user_id": "@u:s.co",
"device_id": "DEV",
"algorithms": [
"m.olm.v1.curve25519-aes-sha2"
],
"keys": {
"curve25519:DEV": "c29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb28",
"ed25519:DEV": "b29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb28"
},
"signatures": {
"@u:s.co": {
"ed25519:DEV": "mia28GKixFzOWKJ0h7Bdrdy2fjxiHCsst1qpe467FbW85H61UlshtKBoAXfTLlVfi0FX+/noJ8B3noQPnY+9Cg",
"ed25519:ssk": "mia28GKixFzOWKJ0h7Bdrdy2fjxiHCsst1qpe467FbW85H61UlshtKBoAXfTLlVfi0FX+/noJ8B3noQPnY+9Cg"
}
}
}
);
let user_id = owned_user_id!("@u:s.co");
let device_id = device_id!("DEV");
let ssk_id = device_id!("ssk");
let ed25519_device_key_id = KeyId::from_parts(ruma::DeviceKeyAlgorithm::Ed25519, device_id);
let curve25519_device_key_id =
KeyId::from_parts(ruma::DeviceKeyAlgorithm::Curve25519, device_id);
let ed25519_ssk_id = KeyId::from_parts(ruma::DeviceKeyAlgorithm::Ed25519, ssk_id);
let mut keys = BTreeMap::new();
keys.insert(
ed25519_device_key_id.clone(),
DeviceKey::Ed25519(
Ed25519PublicKey::from_base64("b29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb28")
.unwrap(),
),
);
keys.insert(
curve25519_device_key_id,
DeviceKey::Curve25519(
Curve25519PublicKey::from_base64("c29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb28")
.unwrap(),
),
);
let mut signatures = Signatures::new();
signatures.add_signature(
user_id.clone(),
ed25519_device_key_id,
Ed25519Signature::from_base64(
"mia28GKixFzOWKJ0h7Bdrdy2fjxiHCsst1qpe467FbW85H61UlshtKBoAXfTLlVfi0FX+/noJ8B3noQPnY+9Cg"
).unwrap()
);
signatures. add_signature(
user_id.clone(),
ed25519_ssk_id,
Ed25519Signature::from_base64(
"mia28GKixFzOWKJ0h7Bdrdy2fjxiHCsst1qpe467FbW85H61UlshtKBoAXfTLlVfi0FX+/noJ8B3noQPnY+9Cg"
).unwrap()
);
let sender_device_keys = DeviceKeys::new(
user_id,
device_id.to_owned(),
vec![EventEncryptionAlgorithm::OlmV1Curve25519AesSha2],
keys,
signatures,
);
(sender_device_keys_json, sender_device_keys)
}
#[test]
fn deserialization() -> Result<(), serde_json::Error> {
macro_rules! assert_deserialization_result {
( $( $json:path => $to_device_events:ident ),* $(,)? ) => {
$(
let json = $json();
let event: AnyDecryptedOlmEvent = serde_json::from_value(json)?;
assert_matches!(event, AnyDecryptedOlmEvent::$to_device_events(_));
)*
}
}
assert_deserialization_result!(
room_key_event => RoomKey,
forwarded_room_key_event => ForwardedRoomKey,
secret_send_event => SecretSend,
dummy_event => Dummy,
);
Ok(())
}
#[test]
fn sender_device_keys_are_deserialized_unstable() {
let (sender_device_keys_json, sender_device_keys) = sender_device_keys();
let mut event_json = room_key_event();
event_json
.as_object_mut()
.unwrap()
.insert("org.matrix.msc4147.device_keys".to_owned(), sender_device_keys_json);
let event: DecryptedRoomKeyEvent = serde_json::from_value(event_json)
.expect("JSON should deserialize to the right event type");
assert_eq!(event.sender_device_keys, Some(sender_device_keys));
}
#[test]
fn sender_device_keys_are_deserialized() {
let (sender_device_keys_json, sender_device_keys) = sender_device_keys();
let mut event_json = room_key_event();
event_json
.as_object_mut()
.unwrap()
.insert("sender_device_keys".to_owned(), sender_device_keys_json);
let event: DecryptedRoomKeyEvent = serde_json::from_value(event_json)
.expect("JSON should deserialize to the right event type");
assert_eq!(event.sender_device_keys, Some(sender_device_keys));
}
}