ruwuma/src/room/encrypted.rs
2019-06-16 16:16:37 -07:00

186 lines
6.3 KiB
Rust

//! Types for the *m.room.encrypted* event.
use js_int::UInt;
use ruma_identifiers::DeviceId;
use serde::{de::Error, Deserialize, Deserializer, Serialize, Serializer};
use serde_json::{from_value, Value};
use crate::Algorithm;
room_event! {
/// This event type is used when sending encrypted events.
///
/// This type is to be used within a room. For a to-device event, use `EncryptedEventContent`
/// directly.
pub struct EncryptedEvent(EncryptedEventContent) {}
}
/// The payload of an *m.room.encrypted* event.
#[derive(Clone, Debug, PartialEq)]
pub enum EncryptedEventContent {
/// An event encrypted with *m.olm.v1.curve25519-aes-sha2*.
OlmV1Curve25519AesSha2(OlmV1Curve25519AesSha2Content),
/// An event encrypted with *m.megolm.v1.aes-sha2*.
MegolmV1AesSha2(MegolmV1AesSha2Content),
/// Additional variants may be added in the future and will not be considered breaking changes
/// to ruma-events.
#[doc(hidden)]
__Nonexhaustive,
}
/// The payload of an *m.room.encrypted* event using the *m.olm.v1.curve25519-aes-sha2* algorithm.
#[derive(Clone, Debug, Serialize, PartialEq, Deserialize)]
pub struct OlmV1Curve25519AesSha2Content {
/// The encryption algorithm used to encrypt this event.
pub algorithm: Algorithm,
/// The encrypted content of the event.
pub ciphertext: CiphertextInfo,
/// The Curve25519 key of the sender.
pub sender_key: String,
}
/// A map from the recipient Curve25519 identity key to ciphertext information.
///
/// Used for messages encrypted with the *m.olm.v1.curve25519-aes-sha2* algorithm.
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
pub struct CiphertextInfo {
/// The encrypted payload.
pub body: String,
/// The Olm message type.
#[serde(rename = "type")]
pub message_type: UInt,
}
/// The payload of an *m.room.encrypted* event using the *m.megolm.v1.aes-sha2* algorithm.
#[derive(Clone, Debug, Serialize, PartialEq, Deserialize)]
pub struct MegolmV1AesSha2Content {
/// The encryption algorithm used to encrypt this event.
pub algorithm: Algorithm,
/// The encrypted content of the event.
pub ciphertext: String,
/// The Curve25519 key of the sender.
pub sender_key: String,
/// The ID of the sending device.
pub device_id: DeviceId,
/// The ID of the session used to encrypt the message.
pub session_id: String,
}
impl Serialize for EncryptedEventContent {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match *self {
EncryptedEventContent::OlmV1Curve25519AesSha2(ref content) => {
content.serialize(serializer)
}
EncryptedEventContent::MegolmV1AesSha2(ref content) => content.serialize(serializer),
_ => panic!("Attempted to serialize __Nonexhaustive variant."),
}
}
}
impl<'de> Deserialize<'de> for EncryptedEventContent {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let value: Value = Deserialize::deserialize(deserializer)?;
let method_value = match value.get("algorithm") {
Some(value) => value.clone(),
None => return Err(D::Error::missing_field("algorithm")),
};
let method = match from_value::<Algorithm>(method_value.clone()) {
Ok(method) => method,
Err(error) => return Err(D::Error::custom(error.to_string())),
};
match method {
Algorithm::OlmV1Curve25519AesSha2 => {
let content = match from_value::<OlmV1Curve25519AesSha2Content>(value) {
Ok(content) => content,
Err(error) => return Err(D::Error::custom(error.to_string())),
};
Ok(EncryptedEventContent::OlmV1Curve25519AesSha2(content))
}
Algorithm::MegolmV1AesSha2 => {
let content = match from_value::<MegolmV1AesSha2Content>(value) {
Ok(content) => content,
Err(error) => return Err(D::Error::custom(error.to_string())),
};
Ok(EncryptedEventContent::MegolmV1AesSha2(content))
}
Algorithm::Custom(_) => Err(D::Error::custom(
"Custom algorithms are not supported by `EncryptedEventContent`.",
)),
Algorithm::__Nonexhaustive => Err(D::Error::custom(
"Attempted to deserialize __Nonexhaustive variant.",
)),
}
}
}
#[cfg(test)]
mod tests {
use serde_json::{from_str, to_string};
use super::{Algorithm, EncryptedEventContent, MegolmV1AesSha2Content};
#[test]
fn serializtion() {
let key_verification_start_content =
EncryptedEventContent::MegolmV1AesSha2(MegolmV1AesSha2Content {
algorithm: Algorithm::MegolmV1AesSha2,
ciphertext: "ciphertext".to_string(),
sender_key: "sender_key".to_string(),
device_id: "device_id".to_string(),
session_id: "session_id".to_string(),
});
assert_eq!(
to_string(&key_verification_start_content).unwrap(),
r#"{"algorithm":"m.megolm.v1.aes-sha2","ciphertext":"ciphertext","sender_key":"sender_key","device_id":"device_id","session_id":"session_id"}"#
);
}
#[test]
fn deserialization() {
let key_verification_start_content =
EncryptedEventContent::MegolmV1AesSha2(MegolmV1AesSha2Content {
algorithm: Algorithm::MegolmV1AesSha2,
ciphertext: "ciphertext".to_string(),
sender_key: "sender_key".to_string(),
device_id: "device_id".to_string(),
session_id: "session_id".to_string(),
});
assert_eq!(
from_str::<EncryptedEventContent>(
r#"{"algorithm":"m.megolm.v1.aes-sha2","ciphertext":"ciphertext","sender_key":"sender_key","device_id":"device_id","session_id":"session_id"}"#
)
.unwrap(),
key_verification_start_content
);
}
#[test]
fn deserialization_failure() {
assert!(
from_str::<EncryptedEventContent>(r#"{"algorithm":"m.megolm.v1.aes-sha2"}"#).is_err()
);
}
}