Restructure the withheld code types

This commit is contained in:
Damir Jelić
2023-03-29 10:45:26 +02:00
parent d5fba19655
commit cf17a05ecc
9 changed files with 399 additions and 330 deletions
+8 -7
View File
@@ -1289,15 +1289,15 @@ impl OlmMachine {
if let MegolmError::Decryption(DecryptionError::UnknownMessageIndex(_, _)) =
error
{
let withheld_info = self
let withheld_code = self
.store
.get_withheld_info(room_id, content.session_id())
.await?
.map(|i| i.withheld_code);
.map(|i| i.withheld_code());
if withheld_info.is_some() {
if withheld_code.is_some() {
// Partially withheld, report with a withheld code if we have one.
MegolmError::MissingRoomKey(withheld_info)
MegolmError::MissingRoomKey(withheld_code)
} else {
error
}
@@ -1307,12 +1307,13 @@ impl OlmMachine {
),
}
} else {
let withheld_info = self
let withheld_code = self
.store
.get_withheld_info(room_id, content.session_id())
.await?
.map(|i| i.withheld_code);
Err(MegolmError::MissingRoomKey(withheld_info))
.map(|i| i.withheld_code());
Err(MegolmError::MissingRoomKey(withheld_code))
}
}
@@ -51,7 +51,7 @@ use crate::{
MegolmV1AesSha2Content, RoomEncryptedEventContent, RoomEventEncryptionScheme,
},
room_key::{MegolmV1AesSha2Content as MegolmV1AesSha2RoomKeyContent, RoomKeyContent},
room_key_withheld::WithheldCode,
room_key_withheld::{RoomKeyWithheldContent, WithheldCode},
},
EventEncryptionAlgorithm,
},
@@ -242,6 +242,19 @@ impl OutboundGroupSession {
self.to_share_with_set.insert(request_id, (request, share_infos));
}
/// Create a new `m.room_key.withheld` event content with the given code for
/// this outbound group session.
pub fn withheld_code(&self, code: WithheldCode) -> RoomKeyWithheldContent {
RoomKeyWithheldContent::new(
self.settings().algorithm.to_owned(),
code,
self.room_id().to_owned(),
self.session_id().to_owned(),
self.sender_key().to_owned(),
(*self.device_id).to_owned(),
)
}
/// This should be called if an the user wishes to rotate this session.
pub fn invalidate_session(&self) {
self.invalidated.store(true, Ordering::Relaxed)
@@ -586,15 +586,7 @@ impl GroupSessionManager {
) -> OlmResult<()> {
// Convert a withheld code for the group session into a to-device event content.
let to_content = |code| {
let content = RoomKeyWithheldContent::create(
group_session.settings().algorithm.to_owned(),
code,
group_session.room_id().to_owned(),
group_session.session_id().to_owned(),
group_session.sender_key(),
Some(self.account.device_id().to_owned()),
);
let content = group_session.withheld_code(code);
Raw::new(&content).expect("We can always serialize a withheld content info").cast()
};
@@ -8,7 +8,7 @@ macro_rules! cryptostore_integration_tests {
use matrix_sdk_test::async_test;
use ruma::{
device_id, encryption::SignedKey, room_id, serde::Base64, user_id, DeviceId,
OwnedDeviceId, OwnedUserId, TransactionId, UserId,
JsOption, OwnedDeviceId, OwnedUserId, TransactionId, UserId,
};
use $crate::{
olm::{
@@ -22,7 +22,8 @@ macro_rules! cryptostore_integration_tests {
testing::{get_device, get_other_identity, get_own_identity},
types::{
events::{
room_key_request::MegolmV1AesSha2Content, room_key_withheld::WithheldCode,
room_key_request::MegolmV1AesSha2Content,
room_key_withheld::{CommonWithheldCodeContent, WithheldCode},
},
EventEncryptionAlgorithm,
},
@@ -629,30 +630,28 @@ macro_rules! cryptostore_integration_tests {
let session_id_1 = "GBnDxGP9i3IkPsz3/ihNr6P7qjIXxSRVWZ1MYmSn09w";
let session_id_2 = "IDLtnNCH2kIr3xIf1B7JFkGpQmTjyMca2jww+X6zeOE";
let info = DirectWithheldInfo {
let content = CommonWithheldCodeContent {
room_id: room_id.to_owned(),
algorithm: EventEncryptionAlgorithm::MegolmV1AesSha2,
session_id: session_id_1.into(),
claimed_sender_key: Curve25519PublicKey::from_base64(
from_device: JsOption::Undefined,
other: Default::default(),
sender_key: Curve25519PublicKey::from_base64(
"9n7mdWKOjr9c4NTlG6zV8dbFtNK79q9vZADoh7nMUwA",
)
.unwrap(),
withheld_code: WithheldCode::Unverified,
};
let info = DirectWithheldInfo::new(
EventEncryptionAlgorithm::MegolmV1AesSha2,
WithheldCode::Unverified,
content.to_owned(),
);
info_list.push(info);
let info = DirectWithheldInfo {
room_id: room_id.to_owned(),
algorithm: EventEncryptionAlgorithm::MegolmV1AesSha2,
session_id: session_id_2.into(),
claimed_sender_key: Curve25519PublicKey::from_base64(
"9n7mdWKOjr9c4NTlG6zV8dbFtNK79q9vZADoh7nMUwA",
)
.unwrap(),
withheld_code: WithheldCode::Blacklisted,
};
let info = DirectWithheldInfo::new(
EventEncryptionAlgorithm::MegolmV1AesSha2,
WithheldCode::Blacklisted,
content,
);
info_list.push(info);
let changes = Changes { withheld_session_info: info_list, ..Default::default() };
@@ -662,9 +661,9 @@ macro_rules! cryptostore_integration_tests {
let is_withheld = store.get_withheld_info(room_id, session_id_1).await.unwrap();
if let Some(info) = is_withheld {
let actual_code = info.withheld_code;
assert_eq!(EventEncryptionAlgorithm::MegolmV1AesSha2, info.algorithm);
assert_eq!(room_id, info.room_id);
let actual_code = info.withheld_code();
assert_eq!(EventEncryptionAlgorithm::MegolmV1AesSha2, info.algorithm());
assert_eq!(room_id, info.room_id());
assert_eq!(WithheldCode::Unverified, actual_code);
} else {
panic!();
@@ -673,7 +672,7 @@ macro_rules! cryptostore_integration_tests {
let is_withheld = store.get_withheld_info(room_id, session_id_2).await.unwrap();
if let Some(info) = is_withheld {
let actual_code = info.withheld_code;
let actual_code = info.withheld_code();
assert_eq!(WithheldCode::Blacklisted, actual_code);
} else {
panic!();
@@ -150,7 +150,7 @@ impl CryptoStore for MemoryStore {
}
for info in changes.withheld_session_info {
self.direct_withheld_info.insert(info.session_id.to_owned(), info.to_owned());
self.direct_withheld_info.insert(info.session_id().to_owned(), info);
}
Ok(())
@@ -285,19 +285,11 @@ impl CryptoStore for MemoryStore {
room_id: &RoomId,
session_id: &str,
) -> Result<Option<DirectWithheldInfo>> {
let entry = self.direct_withheld_info.entry(session_id.to_owned());
match entry {
Entry::Occupied(e) => {
let found = e.get();
if found.room_id == room_id {
Ok(Some(found.to_owned()))
} else {
Ok(None)
}
}
Entry::Vacant(_) => Ok(None),
}
Ok(self
.direct_withheld_info
.get(session_id)
.filter(|e| e.value().room_id() == room_id)
.map(|e| e.value().to_owned()))
}
async fn get_room_settings(&self, _room_id: &RoomId) -> Result<Option<RoomSettings>> {
+117 -25
View File
@@ -16,33 +16,126 @@
//! to all devices present in the room. Sometimes this may be inadvertent (for
//! example, if the sending device is not aware of some devices that have
//! joined), but some times, this may be purposeful.
use ruma::OwnedRoomId;
use ruma::RoomId;
use serde::{Deserialize, Serialize};
use vodozemac::Curve25519PublicKey;
use crate::types::{
events::room_key_withheld::{MegolmV1AesSha2WithheldContent, WithheldCode},
events::room_key_withheld::{
CommonWithheldCodeContent, MegolmV1AesSha2WithheldContent, WithheldCode,
},
EventEncryptionAlgorithm,
};
///
/// We want to store when the owner of the group session sent us a withheld
/// code. It's not storing withheld code that can be sent in a
/// `m.forwarded_room_key`, it's another sort of relation as the same key can be
/// withheld for several reasons by different devices.
#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq)]
pub struct DirectWithheldInfo {
/// The room of the group key.
pub room_id: OwnedRoomId,
/// The key algorithm
pub algorithm: EventEncryptionAlgorithm,
/// The group session id.
pub session_id: String,
/// The session creator device identity.
/// Claimed because withheld codes message are sent in clear
pub claimed_sender_key: Curve25519PublicKey,
/// The reason why the key was not distributed
pub withheld_code: WithheldCode,
pub enum DirectWithheldInfo {
MegolmV1(MegolmV1WithheldInfo),
#[cfg(feature = "experimental-algorithms")]
MegolmV2(MegolmV2WithheldInfo),
}
#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq)]
pub enum MegolmV1WithheldInfo {
BlackListed(Box<CommonWithheldCodeContent>),
Unverified(Box<CommonWithheldCodeContent>),
}
#[cfg(feature = "experimental-algorithms")]
#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq)]
pub enum MegolmV2WithheldInfo {
BlackListed(Box<CommonWithheldCodeContent>),
Unverified(Box<CommonWithheldCodeContent>),
}
impl DirectWithheldInfo {
pub fn new(
algorithm: EventEncryptionAlgorithm,
code: WithheldCode,
content: CommonWithheldCodeContent,
) -> Self {
let content = content.into();
match algorithm {
EventEncryptionAlgorithm::MegolmV1AesSha2 => match code {
WithheldCode::Blacklisted => {
Self::MegolmV1(MegolmV1WithheldInfo::BlackListed(content))
}
WithheldCode::Unverified => {
Self::MegolmV1(MegolmV1WithheldInfo::Unverified(content))
}
_ => panic!("Unsupported direct withheld code"),
},
#[cfg(feature = "experimental-algorithms")]
EventEncryptionAlgorithm::MegolmV1AesSha2 => match code {
WithheldCode::Blacklisted => {
Self::MegolmV2(MegolmV2WithheldInfo::BlackListed(content))
}
WithheldCode::Unverified => {
Self::MegolmV2(MegolmV2WithheldInfo::Unverified(content))
}
_ => panic!("Unsupported direct withheld code"),
},
_ => todo!(),
}
}
pub fn algorithm(&self) -> EventEncryptionAlgorithm {
match self {
DirectWithheldInfo::MegolmV1(_) => EventEncryptionAlgorithm::MegolmV1AesSha2,
#[cfg(feature = "experimental-algorithms")]
DirectWithheldInfo::MegolmV2(_) => EventEncryptionAlgorithm::MegolmV2AesSha2,
}
}
pub fn withheld_code(&self) -> WithheldCode {
match self {
DirectWithheldInfo::MegolmV1(c) => match c {
MegolmV1WithheldInfo::BlackListed(_) => WithheldCode::Blacklisted,
MegolmV1WithheldInfo::Unverified(_) => WithheldCode::Unverified,
},
#[cfg(feature = "experimental-algorithms")]
DirectWithheldInfo::MegolmV2(c) => match c {
MegolmV2WithheldInfo::BlackListed(_) => WithheldCode::Blacklisted,
MegolmV2WithheldInfo::Unverified(_) => WithheldCode::Unverified,
},
}
}
pub fn room_id(&self) -> &RoomId {
match self {
DirectWithheldInfo::MegolmV1(c) => match c {
MegolmV1WithheldInfo::BlackListed(c) | MegolmV1WithheldInfo::Unverified(c) => {
&c.room_id
}
},
#[cfg(feature = "experimental-algorithms")]
DirectWithheldInfo::MegolmV2(c) => match c {
MegolmV2WithheldInfo::BlackListed(c) | MegolmV2WithheldInfo::Unverified(c) => {
&c.room_id
}
},
}
}
pub fn session_id(&self) -> &str {
match self {
DirectWithheldInfo::MegolmV1(c) => match c {
MegolmV1WithheldInfo::BlackListed(c) | MegolmV1WithheldInfo::Unverified(c) => {
&c.session_id
}
},
#[cfg(feature = "experimental-algorithms")]
DirectWithheldInfo::MegolmV2(c) => match c {
MegolmV2WithheldInfo::BlackListed(c) | MegolmV2WithheldInfo::Unverified(c) => {
&c.session_id
}
},
}
}
}
impl TryFrom<&MegolmV1AesSha2WithheldContent> for DirectWithheldInfo {
@@ -50,16 +143,15 @@ impl TryFrom<&MegolmV1AesSha2WithheldContent> for DirectWithheldInfo {
fn try_from(value: &MegolmV1AesSha2WithheldContent) -> Result<Self, Self::Error> {
match value {
MegolmV1AesSha2WithheldContent::AnyContent((code, content)) => Ok(DirectWithheldInfo {
room_id: content.room_id.to_owned(),
algorithm: EventEncryptionAlgorithm::MegolmV1AesSha2,
session_id: content.session_id.to_owned(),
claimed_sender_key: content.sender_key,
withheld_code: code.clone(),
}),
MegolmV1AesSha2WithheldContent::NoOlmContent(_) => {
Err("No Olm can't be converted to direct info")
MegolmV1AesSha2WithheldContent::BlackListed(c) => {
Ok(Self::MegolmV1(MegolmV1WithheldInfo::BlackListed(c.to_owned())))
}
MegolmV1AesSha2WithheldContent::Unverified(c) => {
Ok(Self::MegolmV1(MegolmV1WithheldInfo::Unverified(c.to_owned())))
}
MegolmV1AesSha2WithheldContent::Unauthorised(_) => todo!(),
MegolmV1AesSha2WithheldContent::Unavailable(_) => todo!(),
MegolmV1AesSha2WithheldContent::NoOlm(_) => todo!(),
}
}
}
@@ -13,6 +13,7 @@
// limitations under the License.
//! Types for the `m.room_key.withheld` events.
use std::collections::BTreeMap;
use ruma::{
@@ -44,97 +45,78 @@ pub type RoomKeyWithheldEvent = ToDeviceEvent<RoomKeyWithheldContent>;
pub enum RoomKeyWithheldContent {
/// The `m.megolm.v1.aes-sha2` variant of the `m.room_key.withheld` content.
MegolmV1AesSha2(MegolmV1AesSha2WithheldContent),
/// The `m.megolm.v2.aes-sha2` variant of the `m.room_key.withheld` content.
// The `m.megolm.v2.aes-sha2` variant of the `m.room_key.withheld` content.
#[cfg(feature = "experimental-algorithms")]
MegolmV2AesSha2(MegolmV2AesSha2WithheldContent),
/// An unknown and unsupported variant of the `m.room_key.withheld` content.
Unknown(UnknownRoomKeyWithHeld),
}
macro_rules! construct_withheld_content {
($algorithm:ident, $code:ident, $room_id:ident, $session_id:ident, $sender_key:ident, $from_device:ident) => {
match $code {
WithheldCode::Blacklisted
| WithheldCode::Unverified
| WithheldCode::Unauthorised
| WithheldCode::Unavailable => {
let content = CommonWithheldCodeContent {
$room_id,
$session_id,
$sender_key,
$from_device,
other: Default::default(),
};
RoomKeyWithheldContent::$algorithm(
MegolmV1AesSha2WithheldContent::from_code_and_content($code, content),
)
}
WithheldCode::NoOlm => {
RoomKeyWithheldContent::$algorithm(MegolmV1AesSha2WithheldContent::NoOlm(
NoOlmWithheldContent { $sender_key, $from_device, other: Default::default() }
.into(),
))
}
_ => unreachable!("Can't create an unknown withheld code content"),
}
};
}
impl RoomKeyWithheldContent {
/// Creates a withheld content from the given info
pub fn create(
pub fn new(
algorithm: EventEncryptionAlgorithm,
code: WithheldCode,
room_id: OwnedRoomId,
session_id: String,
sender_key: Curve25519PublicKey,
from_device: Option<OwnedDeviceId>,
from_device: OwnedDeviceId,
) -> Self {
let from_device = JsOption::Some(from_device);
match algorithm {
EventEncryptionAlgorithm::MegolmV1AesSha2 => {
let other = match code {
WithheldCode::NoOlm => {
let content = NoOlmWithheldContent {
sender_key,
reason: JsOption::Some(code.to_string()),
from_device: JsOption::from_implicit_option(from_device),
other: Default::default(),
};
serde_json::to_value(content)
.expect("We can always serialize this construction")
}
_ => {
let content = AnyWithheldContent {
room_id,
session_id,
sender_key,
// code: code.clone(),
reason: JsOption::Some(code.to_string()),
from_device: JsOption::from_implicit_option(from_device),
other: Default::default(),
};
serde_json::to_value(content)
.expect("We can always serialize this construction")
}
};
let helper = WithheldHelper {
algorithm: EventEncryptionAlgorithm::MegolmV1AesSha2,
construct_withheld_content!(
MegolmV1AesSha2,
code,
other,
};
RoomKeyWithheldContent::try_from(helper)
.expect("We can always serialize this construction")
room_id,
session_id,
sender_key,
from_device
)
}
#[cfg(feature = "experimental-algorithms")]
EventEncryptionAlgorithm::MegolmV2AesSha2 => {
let other = match code {
WithheldCode::NoOlm => {
let content = NoOlmWithheldContent {
sender_key,
reason: JsOption::Some(code.to_string()),
from_device: JsOption::from_implicit_option(from_device),
other: Default::default(),
};
serde_json::to_value(content)
.expect("We can always serialize this construction")
}
_ => {
let content = AnyWithheldContent {
room_id,
session_id,
sender_key,
reason: JsOption::Some(code.to_string()),
from_device: JsOption::from_implicit_option(from_device),
other: Default::default(),
};
serde_json::to_value(content)
.expect("We can always serialize this construction")
}
};
let helper = WithheldHelper {
algorithm: EventEncryptionAlgorithm::MegolmV1AesSha2,
construct_withheld_content!(
MegolmV2AesSha2,
code,
other,
};
RoomKeyWithheldContent::try_from(helper)
.expect("We can always serialize this construction")
room_id,
session_id,
sender_key,
from_device
)
}
_ => RoomKeyWithheldContent::Unknown(UnknownRoomKeyWithHeld {
algorithm,
code,
other: Default::default(),
}),
_ => todo!(),
}
}
@@ -215,70 +197,74 @@ impl std::fmt::Display for WithheldCode {
#[derive(Deserialize, Serialize, Debug)]
struct WithheldHelper {
pub algorithm: EventEncryptionAlgorithm,
pub reason: JsOption<String>,
pub code: WithheldCode,
#[serde(flatten)]
other: Value,
}
/// The content of a withheld event depends on the
/// value of the code. This enum reflect that for better type safety
#[derive(Clone, Debug)]
pub enum MegolmV1AesSha2WithheldContent {
/// Content for regular withheld content
AnyContent((WithheldCode, Box<AnyWithheldContent>)),
/// Content for no_olm withheld content
NoOlmContent(Box<NoOlmWithheldContent>),
BlackListed(Box<CommonWithheldCodeContent>),
Unverified(Box<CommonWithheldCodeContent>),
Unauthorised(Box<CommonWithheldCodeContent>),
Unavailable(Box<CommonWithheldCodeContent>),
NoOlm(Box<NoOlmWithheldContent>),
}
#[derive(Clone, PartialEq, Eq, Deserialize, Serialize)]
pub struct CommonWithheldCodeContent {
/// The room where the key is used.
pub room_id: OwnedRoomId,
/// The ID of the session.
pub session_id: String,
/// The device curve25519 key of the session creator.
#[serde(deserialize_with = "deserialize_curve_key", serialize_with = "serialize_curve_key")]
pub sender_key: Curve25519PublicKey,
/// The device ID of the device sending the m.room_key.withheld message
/// MSC3735.
#[serde(default, skip_serializing_if = "JsOption::is_undefined")]
pub from_device: JsOption<OwnedDeviceId>,
#[serde(flatten)]
pub other: BTreeMap<String, Value>,
}
impl MegolmV1AesSha2WithheldContent {
/// Get the withheld code for this content
pub fn withheld_code(&self) -> WithheldCode {
match self {
MegolmV1AesSha2WithheldContent::AnyContent((code, _)) => code.clone(),
MegolmV1AesSha2WithheldContent::NoOlmContent(_) => WithheldCode::NoOlm,
MegolmV1AesSha2WithheldContent::BlackListed(_) => WithheldCode::Blacklisted,
MegolmV1AesSha2WithheldContent::Unverified(_) => WithheldCode::Unverified,
MegolmV1AesSha2WithheldContent::Unauthorised(_) => WithheldCode::Unauthorised,
MegolmV1AesSha2WithheldContent::Unavailable(_) => WithheldCode::Unavailable,
MegolmV1AesSha2WithheldContent::NoOlm(_) => WithheldCode::NoOlm,
}
}
fn from_code_and_content(code: WithheldCode, content: CommonWithheldCodeContent) -> Self {
let content = content.into();
match code {
WithheldCode::Blacklisted => Self::BlackListed(content),
WithheldCode::Unverified => Self::Unverified(content),
WithheldCode::Unauthorised => Self::Unauthorised(content),
WithheldCode::Unavailable => Self::Unavailable(content),
_ => unreachable!("This constructor requires one of the common withheld codes"),
}
}
}
/// Devices that purposely do not send megolm keys to a device may instead send
/// an m.room_key.withheld event as a to-device message to the device to
/// indicate that it should not expect to receive keys for the message.
#[derive(Clone, Deserialize, Serialize)]
pub struct AnyWithheldContent {
/// The room where the key is used.
pub room_id: OwnedRoomId,
/// The ID of the session.
pub session_id: String,
#[serde(deserialize_with = "deserialize_curve_key", serialize_with = "serialize_curve_key")]
/// The device curve25519 key of the session creator.
pub sender_key: Curve25519PublicKey,
/// A human-readable reason for why the key was not sent. The receiving
/// client should only use this string if it does not understand the code
#[serde(default, skip_serializing_if = "JsOption::is_undefined")]
pub reason: JsOption<String>,
/// The device ID of the device sending the m.room_key.withheld message
/// MSC3735.
#[serde(default, skip_serializing_if = "JsOption::is_undefined")]
pub from_device: JsOption<OwnedDeviceId>,
#[serde(flatten)]
other: BTreeMap<String, Value>,
}
#[derive(Clone, Deserialize, Serialize)]
/// No olm content (no room_id nor session_id)
#[derive(Clone, Deserialize, Serialize)]
pub struct NoOlmWithheldContent {
#[serde(deserialize_with = "deserialize_curve_key", serialize_with = "serialize_curve_key")]
/// The device curve25519 key of the session creator.
pub sender_key: Curve25519PublicKey,
/// A human-readable reason for why the key was not sent. The receiving
/// client should only use this string if it does not understand the code
#[serde(default, skip_serializing_if = "JsOption::is_undefined")]
pub reason: JsOption<String>,
/// The device ID of the device sending the m.room_key.withheld message
/// MSC3735.
#[serde(default, skip_serializing_if = "JsOption::is_undefined")]
@@ -288,7 +274,7 @@ pub struct NoOlmWithheldContent {
other: BTreeMap<String, Value>,
}
impl std::fmt::Debug for AnyWithheldContent {
impl std::fmt::Debug for CommonWithheldCodeContent {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("AnyWithheldContent")
.field("room_id", &self.room_id)
@@ -318,6 +304,8 @@ pub struct UnknownRoomKeyWithHeld {
pub algorithm: EventEncryptionAlgorithm,
/// The withheld code
pub code: WithheldCode,
pub reason: JsOption<String>,
/// The other data of the unknown room key.
#[serde(flatten)]
other: BTreeMap<String, Value>,
@@ -327,39 +315,52 @@ impl TryFrom<WithheldHelper> for RoomKeyWithheldContent {
type Error = serde_json::Error;
fn try_from(value: WithheldHelper) -> Result<Self, Self::Error> {
Ok(match value.algorithm {
EventEncryptionAlgorithm::MegolmV1AesSha2 => {
let content = match value.code {
WithheldCode::NoOlm => {
let content: NoOlmWithheldContent = serde_json::from_value(value.other)?;
MegolmV1AesSha2WithheldContent::NoOlmContent(content.into())
}
_ => {
let content: AnyWithheldContent = serde_json::from_value(value.other)?;
MegolmV1AesSha2WithheldContent::AnyContent((value.code, content.into()))
}
};
Self::MegolmV1AesSha2(content)
}
#[cfg(feature = "experimental-algorithms")]
EventEncryptionAlgorithm::MegolmV2AesSha2 => {
let content = match value.code {
WithheldCode::NoOlm => {
let content: NoOlmWithheldContent = serde_json::from_value(value.other)?;
MegolmV1AesSha2WithheldContent::NoOlmContent(content.into())
}
_ => {
let content: AnyWithheldContent = serde_json::from_value(value.other)?;
MegolmV1AesSha2WithheldContent::AnyContent((value.code, content.into()))
}
};
Self::MegolmV2AesSha2(content)
}
_ => Self::Unknown(UnknownRoomKeyWithHeld {
let unknown = |value: WithheldHelper| -> Result<RoomKeyWithheldContent, _> {
Ok(Self::Unknown(UnknownRoomKeyWithHeld {
algorithm: value.algorithm,
code: value.code,
reason: value.reason,
other: serde_json::from_value(value.other)?,
}),
}))
};
Ok(match value.algorithm {
EventEncryptionAlgorithm::MegolmV1AesSha2 => match value.code {
WithheldCode::NoOlm => {
let content: NoOlmWithheldContent = serde_json::from_value(value.other)?;
Self::MegolmV1AesSha2(MegolmV1AesSha2WithheldContent::NoOlm(content.into()))
}
WithheldCode::Blacklisted
| WithheldCode::Unverified
| WithheldCode::Unauthorised
| WithheldCode::Unavailable => {
let content: CommonWithheldCodeContent = serde_json::from_value(value.other)?;
Self::MegolmV1AesSha2(MegolmV1AesSha2WithheldContent::from_code_and_content(
value.code, content,
))
}
_ => unknown(value)?,
},
#[cfg(feature = "experimental-algorithms")]
EventEncryptionAlgorithm::MegolmV2AesSha2 => match value.code {
WithheldCode::NoOlm => {
let content: NoOlmWithheldContent = serde_json::from_value(value.other)?;
Self::MegolmV1AesSha2(MegolmV1AesSha2WithheldContent::NoOlm(content.into()))
}
WithheldCode::Blacklisted
| WithheldCode::Unverified
| WithheldCode::Unauthorised
| WithheldCode::Unavailable => {
let content: CommonWithheldCodeContent = serde_json::from_value(value.other)?;
Self::MegolmV1AesSha2(MegolmV1AesSha2WithheldContent::from_code_and_content(
value.code, content,
))
}
_ => unknown(value)?,
},
_ => unknown(value)?,
})
}
}
@@ -369,35 +370,58 @@ impl Serialize for RoomKeyWithheldContent {
where
S: serde::Serializer,
{
let algorithm = self.algorithm();
let helper = match self {
Self::MegolmV1AesSha2(r) => match r {
MegolmV2AesSha2WithheldContent::AnyContent((code, content)) => WithheldHelper {
algorithm: EventEncryptionAlgorithm::MegolmV1AesSha2,
code: code.clone(),
other: serde_json::to_value(content).map_err(serde::ser::Error::custom)?,
},
MegolmV2AesSha2WithheldContent::NoOlmContent(content) => WithheldHelper {
algorithm: EventEncryptionAlgorithm::MegolmV1AesSha2,
code: WithheldCode::NoOlm,
other: serde_json::to_value(content).map_err(serde::ser::Error::custom)?,
},
},
Self::MegolmV1AesSha2(r) => {
let code = r.withheld_code();
let reason = JsOption::Some(code.to_string());
match r {
MegolmV1AesSha2WithheldContent::BlackListed(content)
| MegolmV1AesSha2WithheldContent::Unverified(content)
| MegolmV1AesSha2WithheldContent::Unauthorised(content)
| MegolmV1AesSha2WithheldContent::Unavailable(content) => WithheldHelper {
algorithm,
code,
reason,
other: serde_json::to_value(content).map_err(serde::ser::Error::custom)?,
},
MegolmV1AesSha2WithheldContent::NoOlm(content) => WithheldHelper {
algorithm,
code,
reason,
other: serde_json::to_value(content).map_err(serde::ser::Error::custom)?,
},
}
}
#[cfg(feature = "experimental-algorithms")]
Self::MegolmV2AesSha2(r) => match r {
MegolmV2AesSha2WithheldContent::AnyContent((code, content)) => WithheldHelper {
algorithm: EventEncryptionAlgorithm::MegolmV1AesSha2,
code: code.clone(),
other: serde_json::to_value(content).map_err(serde::ser::Error::custom)?,
},
MegolmV2AesSha2WithheldContent::NoOlmContent(content) => WithheldHelper {
algorithm: EventEncryptionAlgorithm::MegolmV1AesSha2,
code: WithheldCode::NoOlm,
other: serde_json::to_value(content).map_err(serde::ser::Error::custom)?,
},
},
Self::MegolmV2AesSha2(r) => {
let code = r.withheld_code();
let reason = JsOption::Some(code.to_string());
match r {
MegolmV1AesSha2WithheldContent::BlackListed(content)
| MegolmV1AesSha2WithheldContent::Unverified(content)
| MegolmV1AesSha2WithheldContent::Unauthorised(content)
| MegolmV1AesSha2WithheldContent::Unavailable(content) => WithheldHelper {
algorithm,
code,
reason,
other: serde_json::to_value(content).map_err(serde::ser::Error::custom)?,
},
MegolmV1AesSha2WithheldContent::NoOlm(content) => WithheldHelper {
algorithm,
code,
reason,
other: serde_json::to_value(content).map_err(serde::ser::Error::custom)?,
},
}
}
Self::Unknown(r) => WithheldHelper {
algorithm: r.algorithm.clone(),
code: r.code.clone(),
algorithm: r.algorithm.to_owned(),
code: r.code.to_owned(),
reason: r.reason.to_owned(),
other: serde_json::to_value(r.other.clone()).map_err(serde::ser::Error::custom)?,
},
};
@@ -498,32 +522,10 @@ pub(super) mod test {
for code in codes {
let json = json(&code);
let event: RoomKeyWithheldEvent = serde_json::from_value(json.clone())?;
assert_matches!(
event.content,
RoomKeyWithheldContent::MegolmV1AesSha2(
MegolmV1AesSha2WithheldContent::AnyContent(_)
)
);
if let RoomKeyWithheldContent::MegolmV1AesSha2(content) = &event.content {
assert_eq!(content.withheld_code().to_owned(), code)
} else {
panic!()
}
assert_matches!(&event.content, RoomKeyWithheldContent::MegolmV1AesSha2(content) if code == content.withheld_code());
if let RoomKeyWithheldContent::MegolmV1AesSha2(
MegolmV1AesSha2WithheldContent::AnyContent((_, content)),
) = &event.content
{
assert_eq!(content.reason, ruma::JsOption::Some(code.to_string()))
} else {
panic!()
}
assert_eq!(
event.content.algorithm().to_owned(),
EventEncryptionAlgorithm::MegolmV1AesSha2
);
assert_eq!(event.content.algorithm(), EventEncryptionAlgorithm::MegolmV1AesSha2);
let serialized = serde_json::to_value(event)?;
assert_eq!(json, serialized);
}
@@ -536,9 +538,7 @@ pub(super) mod test {
let event: RoomKeyWithheldEvent = serde_json::from_value(json.clone())?;
assert_matches!(
event.content,
RoomKeyWithheldContent::MegolmV1AesSha2(MegolmV1AesSha2WithheldContent::NoOlmContent(
_
))
RoomKeyWithheldContent::MegolmV1AesSha2(MegolmV1AesSha2WithheldContent::NoOlm(_))
);
let serialized = serde_json::to_value(event)?;
assert_eq!(json, serialized);
@@ -550,19 +550,14 @@ pub(super) mod test {
fn deserialization_unknown_code() -> Result<(), serde_json::Error> {
let json = unknown_code_json();
let event: RoomKeyWithheldEvent = serde_json::from_value(json.clone())?;
assert_matches!(event.content, RoomKeyWithheldContent::Unknown(_));
assert_matches!(
event.content,
RoomKeyWithheldContent::MegolmV1AesSha2(MegolmV1AesSha2WithheldContent::AnyContent((
_,
_
)))
&event.content,
RoomKeyWithheldContent::Unknown(content)
if content.code.as_str() == "org.mscXXX.new_code"
);
if let RoomKeyWithheldContent::MegolmV1AesSha2(content) = &event.content {
assert_matches!(content.withheld_code(), WithheldCode::_Custom(_));
} else {
panic!()
}
let serialized = serde_json::to_value(event)?;
assert_eq!(json, serialized);
@@ -597,13 +592,13 @@ pub(super) mod test {
Curve25519PublicKey::from_base64("9n7mdWKOjr9c4NTlG6zV8dbFtNK79q9vZADoh7nMUwA")
.unwrap();
let content = RoomKeyWithheldContent::create(
let content = RoomKeyWithheldContent::new(
EventEncryptionAlgorithm::MegolmV1AesSha2,
WithheldCode::Unverified,
room_id.to_owned(),
"0ZcULv8j1nqVWx6orFjD6OW9JQHydDPXfaanA+uRyfs".to_owned(),
sender_key,
Some(device_id.to_owned()),
device_id.to_owned(),
);
let content: Raw<RoomKeyWithheldContent> =
Raw::new(&content).expect("We can always serialize a withheld content info").cast();
@@ -617,15 +612,15 @@ pub(super) mod test {
let expected: Value = json!({
"@alice:example.org":{
"DEV001":{
"algorithm":"m.megolm.v1.aes-sha2",
"code":"m.unverified",
"from_device":"DEV001",
"reason":"The sender has disabled encrypting to unverified devices.",
"room_id":"!DwLygpkclUAfQNnfva:localhost:8481",
"sender_key":"9n7mdWKOjr9c4NTlG6zV8dbFtNK79q9vZADoh7nMUwA",
"session_id":"0ZcULv8j1nqVWx6orFjD6OW9JQHydDPXfaanA+uRyfs"
}
"DEV001":{
"algorithm":"m.megolm.v1.aes-sha2",
"code":"m.unverified",
"from_device":"DEV001",
"reason":"The sender has disabled encrypting to unverified devices.",
"room_id":"!DwLygpkclUAfQNnfva:localhost:8481",
"sender_key":"9n7mdWKOjr9c4NTlG6zV8dbFtNK79q9vZADoh7nMUwA",
"session_id":"0ZcULv8j1nqVWx6orFjD6OW9JQHydDPXfaanA+uRyfs"
}
}
});
assert_eq!(serialized, expected);
@@ -639,49 +634,34 @@ pub(super) mod test {
Curve25519PublicKey::from_base64("9n7mdWKOjr9c4NTlG6zV8dbFtNK79q9vZADoh7nMUwA")
.unwrap();
let content = RoomKeyWithheldContent::create(
let content = RoomKeyWithheldContent::new(
EventEncryptionAlgorithm::MegolmV1AesSha2,
WithheldCode::NoOlm,
room_id.to_owned(),
"0ZcULv8j1nqVWx6orFjD6OW9JQHydDPXfaanA+uRyfs".to_owned(),
sender_key,
Some(device_id.to_owned()),
device_id.to_owned(),
);
assert_matches!(content, RoomKeyWithheldContent::MegolmV1AesSha2(_));
assert_matches!(
content,
RoomKeyWithheldContent::MegolmV1AesSha2(MegolmV1AesSha2WithheldContent::NoOlm(content))
if content.sender_key == sender_key
);
match content {
RoomKeyWithheldContent::MegolmV1AesSha2(content) => match content {
MegolmV1AesSha2WithheldContent::AnyContent(_) => panic!(),
MegolmV1AesSha2WithheldContent::NoOlmContent(no_olm_content) => {
assert_eq!(sender_key, no_olm_content.sender_key);
}
},
_ => panic!(),
}
let content = RoomKeyWithheldContent::create(
let content = RoomKeyWithheldContent::new(
EventEncryptionAlgorithm::MegolmV1AesSha2,
WithheldCode::Unverified,
room_id.to_owned(),
"0ZcULv8j1nqVWx6orFjD6OW9JQHydDPXfaanA+uRyfs".to_owned(),
sender_key,
Some(device_id.to_owned()),
device_id.to_owned(),
);
assert_matches!(content, RoomKeyWithheldContent::MegolmV1AesSha2(_));
match content {
RoomKeyWithheldContent::MegolmV1AesSha2(content) => match content {
MegolmV1AesSha2WithheldContent::NoOlmContent(_) => panic!(),
MegolmV1AesSha2WithheldContent::AnyContent((code, content)) => {
assert_eq!(sender_key, content.sender_key);
assert_eq!(code, WithheldCode::Unverified);
assert_eq!("0ZcULv8j1nqVWx6orFjD6OW9JQHydDPXfaanA+uRyfs", content.session_id);
assert_eq!(room_id, content.room_id);
}
},
_ => panic!(),
}
assert_matches!(
content,
RoomKeyWithheldContent::MegolmV1AesSha2(MegolmV1AesSha2WithheldContent::Unverified(content))
if content.session_id == "0ZcULv8j1nqVWx6orFjD6OW9JQHydDPXfaanA+uRyfs"
);
}
}
@@ -570,8 +570,9 @@ impl_crypto_store! {
let withhelds = tx.object_store(keys::DIRECT_WITHHELD_INFO)?;
for info in withheld_session_info {
let room_id = info.room_id.to_owned();
let session_id = info.session_id.to_owned();
let room_id = info.room_id();
let session_id = info.session_id();
let key = self.encode_key(keys::DIRECT_WITHHELD_INFO, (session_id, room_id));
withhelds.put_key_val(&key, &self.serialize_value(&info)?)?;
}
+5 -6
View File
@@ -671,12 +671,11 @@ impl SledCryptoStore {
}
for info in &changes.withheld_session_info {
let session_id = info.session_id.to_owned();
let room_id = info.room_id.to_owned();
let key = self.encode_key(
DIRECT_WITHHELD_INFO_TABLE,
(session_id.as_str(), room_id.as_str()),
);
let session_id = info.session_id();
let room_id = info.room_id();
let key =
self.encode_key(DIRECT_WITHHELD_INFO_TABLE, (session_id, room_id));
direct_withheld_info.insert(
key,
self.serialize_value(info)