Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions bindings/matrix-sdk-crypto-ffi/src/machine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use std::{
};

use js_int::UInt;
use matrix_sdk_common::deserialized_responses::AlgorithmInfo;
use matrix_sdk_common::deserialized_responses::{AlgorithmInfo, EncryptionState};
use matrix_sdk_crypto::{
backups::{
MegolmV1BackupKey as RustBackupKey, SignatureState,
Expand Down Expand Up @@ -909,8 +909,15 @@ impl OlmMachine {
}
}

let encryption_info =
decrypted.encryption_info.expect("Decrypted event didn't contain any encryption info");
let encryption_info = match decrypted.encryption_state {
EncryptionState::Unencrypted => {
panic!("Decrypted event didn't contain any encryption info")
}
EncryptionState::Decrypted(encryption_info) => encryption_info,
EncryptionState::UnableToDecrypt(_) => {
panic!("Apparently-decrypted event was unable to decrypt")
}
};

let event_json: Event<'_> = serde_json::from_str(decrypted.event.json().get())?;

Expand Down
2 changes: 1 addition & 1 deletion crates/matrix-sdk-base/src/latest_event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -562,7 +562,7 @@ mod tests {
json!({
"latest_event": {
"event": {
"encryption_info": null,
"encryption_state": "Unencrypted",
"event": {
"event_id": "$1"
}
Expand Down
2 changes: 1 addition & 1 deletion crates/matrix-sdk-base/src/rooms/normal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1805,7 +1805,7 @@ mod tests {
"encryption_state_synced": true,
"latest_event": {
"event": {
"encryption_info": null,
"encryption_state": "Unencrypted",
"event": {
"sender": "@u:i.uk",
},
Expand Down
108 changes: 73 additions & 35 deletions crates/matrix-sdk-common/src/deserialized_responses.rs
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ pub enum AlgorithmInfo {
},
}

/// Struct containing information on how an event was decrypted.
/// Struct containing information on the successful decryption of an event.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct EncryptionInfo {
/// The user ID of the event sender, note this is untrusted data unless the
Expand All @@ -296,15 +296,56 @@ pub struct EncryptionInfo {
pub verification_state: VerificationState,
}

/// Metadata about an event that could not be decrypted.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UnableToDecryptInfo {
/// The ID of the session used to encrypt the message, if it used the
/// `m.megolm.v1.aes-sha2` algorithm.
#[serde(skip_serializing_if = "Option::is_none")]
pub session_id: Option<String>,
}

/// Information on how an event was decrypted, if at all.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub enum EncryptionState {
/// The event was not encrypted.
Unencrypted,
/// The event was decrypted successfully.
Decrypted(EncryptionInfo),
/// The event could not be decrypted.
UnableToDecrypt(UnableToDecryptInfo),
}

impl EncryptionState {
/// If the event was successfully decrypted, the encryption info.
/// If the event was not encrypted, or there was an error during decryption,
/// `None`.
pub fn as_encryption_info(&self) -> Option<&EncryptionInfo> {
match self {
Self::Unencrypted => None,
Self::Decrypted(encryption_info) => Some(encryption_info),
Self::UnableToDecrypt(_) => None,
}
}
}

/// The result of the decryption of an event bundled in an `unsigned` object.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum UnsignedDecryptionResult {
/// The event was successfully decrypted.
Decrypted(EncryptionInfo),
/// The event failed to be decrypted.
UnableToDecrypt(UnableToDecryptInfo),
}

/// A customized version of a room event coming from a sync that holds optional
/// encryption info.
#[derive(Clone, Deserialize, Serialize)]
pub struct SyncTimelineEvent {
/// The actual event.
pub event: Raw<AnySyncTimelineEvent>,
/// The encryption info about the event. Will be `None` if the event was not
/// encrypted.
pub encryption_info: Option<EncryptionInfo>,
/// Whether the event was encrypted, and why it failed to decrypt if it did.
pub encryption_state: EncryptionState,
/// The push actions associated with this event.
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub push_actions: Vec<Action>,
Expand All @@ -319,21 +360,31 @@ impl SyncTimelineEvent {
/// Create a new `SyncTimelineEvent` from the given raw event.
///
/// This is a convenience constructor for when you don't need to set
/// `encryption_info` or `push_action`, for example inside a test.
/// `encryption_state` or `push_action`, for example inside a test.
pub fn new(event: Raw<AnySyncTimelineEvent>) -> Self {
Self { event, encryption_info: None, push_actions: vec![], unsigned_encryption_info: None }
Self {
event,
encryption_state: EncryptionState::Unencrypted,
push_actions: vec![],
unsigned_encryption_info: None,
}
}

/// Create a new `SyncTimelineEvent` from the given raw event and push
/// actions.
///
/// This is a convenience constructor for when you don't need to set
/// `encryption_info`, for example inside a test.
/// `encryption_state`, for example inside a test.
pub fn new_with_push_actions(
event: Raw<AnySyncTimelineEvent>,
push_actions: Vec<Action>,
) -> Self {
Self { event, encryption_info: None, push_actions, unsigned_encryption_info: None }
Self {
event,
encryption_state: EncryptionState::Unencrypted,
push_actions,
unsigned_encryption_info: None,
}
}

/// Get the event id of this `SyncTimelineEvent` if the event has any valid
Expand All @@ -346,11 +397,11 @@ impl SyncTimelineEvent {
#[cfg(not(tarpaulin_include))]
impl fmt::Debug for SyncTimelineEvent {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let SyncTimelineEvent { event, encryption_info, push_actions, unsigned_encryption_info } =
let SyncTimelineEvent { event, encryption_state, push_actions, unsigned_encryption_info } =
self;
let mut s = f.debug_struct("SyncTimelineEvent");
s.field("event", &DebugRawEvent(event));
s.maybe_field("encryption_info", encryption_info);
s.field("encryption_state", encryption_state);
if !push_actions.is_empty() {
s.field("push_actions", push_actions);
}
Expand All @@ -373,7 +424,7 @@ impl From<TimelineEvent> for SyncTimelineEvent {
// ignored by a subsequent deserialization.
Self {
event: o.event.cast(),
encryption_info: o.encryption_info,
encryption_state: o.encryption_state,
push_actions: o.push_actions.unwrap_or_default(),
unsigned_encryption_info: o.unsigned_encryption_info,
}
Expand All @@ -384,9 +435,8 @@ impl From<TimelineEvent> for SyncTimelineEvent {
pub struct TimelineEvent {
/// The actual event.
pub event: Raw<AnyTimelineEvent>,
/// The encryption info about the event. Will be `None` if the event was not
/// encrypted.
pub encryption_info: Option<EncryptionInfo>,
/// Whether the event was encrypted, and why it failed to decrypt if it did.
pub encryption_state: EncryptionState,
/// The push actions associated with this event, if we had sufficient
/// context to compute them.
pub push_actions: Option<Vec<Action>>,
Expand All @@ -402,17 +452,23 @@ impl TimelineEvent {
/// This is a convenience constructor for when you don't need to set
/// `encryption_info` or `push_action`, for example inside a test.
pub fn new(event: Raw<AnyTimelineEvent>) -> Self {
Self { event, encryption_info: None, push_actions: None, unsigned_encryption_info: None }
Self {
event,
encryption_state: EncryptionState::Unencrypted,
push_actions: None,
unsigned_encryption_info: None,
}
}
}

#[cfg(not(tarpaulin_include))]
impl fmt::Debug for TimelineEvent {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let TimelineEvent { event, encryption_info, push_actions, unsigned_encryption_info } = self;
let TimelineEvent { event, encryption_state, push_actions, unsigned_encryption_info } =
self;
let mut s = f.debug_struct("TimelineEvent");
s.field("event", &DebugRawEvent(event));
s.maybe_field("encryption_info", encryption_info);
s.field("encryption_state", encryption_state);
if let Some(push_actions) = &push_actions {
if !push_actions.is_empty() {
s.field("push_actions", push_actions);
Expand Down Expand Up @@ -453,24 +509,6 @@ impl UnsignedEventLocation {
}
}

/// The result of the decryption of an event bundled in an `unsigned` object.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum UnsignedDecryptionResult {
/// The event was successfully decrypted.
Decrypted(EncryptionInfo),
/// The event failed to be decrypted.
UnableToDecrypt(UnableToDecryptInfo),
}

/// Metadata about an event that could not be decrypted.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UnableToDecryptInfo {
/// The ID of the session used to encrypt the message, if it used the
/// `m.megolm.v1.aes-sha2` algorithm.
#[serde(skip_serializing_if = "Option::is_none")]
pub session_id: Option<String>,
}

#[cfg(test)]
mod tests {
use ruma::{
Expand Down
14 changes: 10 additions & 4 deletions crates/matrix-sdk-crypto/src/machine/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,9 @@ use std::{
use itertools::Itertools;
use matrix_sdk_common::{
deserialized_responses::{
AlgorithmInfo, DeviceLinkProblem, EncryptionInfo, TimelineEvent, UnableToDecryptInfo,
UnsignedDecryptionResult, UnsignedEventLocation, VerificationLevel, VerificationState,
AlgorithmInfo, DeviceLinkProblem, EncryptionInfo, EncryptionState, TimelineEvent,
UnableToDecryptInfo, UnsignedDecryptionResult, UnsignedEventLocation, VerificationLevel,
VerificationState,
},
BoxFuture,
};
Expand Down Expand Up @@ -1818,7 +1819,7 @@ impl OlmMachine {

Ok(TimelineEvent {
event,
encryption_info: Some(encryption_info),
encryption_state: EncryptionState::Decrypted(encryption_info),
push_actions: None,
unsigned_encryption_info,
})
Expand Down Expand Up @@ -1902,7 +1903,12 @@ impl OlmMachine {
Ok(decrypted_event) => {
// Replace the encrypted event.
*event = serde_json::to_value(decrypted_event.event).ok()?;
Some(UnsignedDecryptionResult::Decrypted(decrypted_event.encryption_info?))
let encryption_info = match decrypted_event.encryption_state {
EncryptionState::Unencrypted => None,
EncryptionState::Decrypted(encryption_info) => Some(encryption_info),
EncryptionState::UnableToDecrypt(_) => None,
}?;
Some(UnsignedDecryptionResult::Decrypted(encryption_info))
}
Err(_) => {
let session_id =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use std::{iter, sync::Arc};

use assert_matches2::{assert_let, assert_matches};
use matrix_sdk_common::deserialized_responses::{
DeviceLinkProblem, ShieldState, VerificationLevel, VerificationState,
DeviceLinkProblem, EncryptionState, ShieldState, VerificationLevel, VerificationState,
};
use matrix_sdk_test::{async_test, ruma_response_from_json, test_json};
use ruma::{
Expand Down Expand Up @@ -113,12 +113,16 @@ async fn test_decryption_verification_state() {

let decryption_settings =
DecryptionSettings { sender_device_trust_requirement: TrustRequirement::Untrusted };
let encryption_info = bob
let encryption_info = match bob
.decrypt_room_event(&event, room_id, &decryption_settings)
.await
.unwrap()
.encryption_info
.unwrap();
.encryption_state
{
EncryptionState::Unencrypted => panic!("Event was unexpectedly unencrypted"),
EncryptionState::Decrypted(encryption_info) => encryption_info,
EncryptionState::UnableToDecrypt(_) => panic!("Unexpectedly failed to decrypt"),
};

assert_eq!(
VerificationState::Unverified(VerificationLevel::UnsignedDevice),
Expand Down
12 changes: 6 additions & 6 deletions crates/matrix-sdk-crypto/src/machine/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use assert_matches2::assert_matches;
use futures_util::{pin_mut, FutureExt, StreamExt};
use itertools::Itertools;
use matrix_sdk_common::deserialized_responses::{
UnableToDecryptInfo, UnsignedDecryptionResult, UnsignedEventLocation,
EncryptionState, UnableToDecryptInfo, UnsignedDecryptionResult, UnsignedEventLocation,
};
use matrix_sdk_test::{async_test, message_like_event_content, ruma_response_from_json, test_json};
use ruma::{
Expand Down Expand Up @@ -1300,7 +1300,7 @@ async fn test_unsigned_decryption() {
assert_eq!(first_message.content.body(), first_message_text);
assert!(first_message.unsigned.relations.is_empty());

assert!(raw_decrypted_event.encryption_info.is_some());
assert_matches!(raw_decrypted_event.encryption_state, EncryptionState::Decrypted(_));
assert!(raw_decrypted_event.unsigned_encryption_info.is_none());

// Get a new room key, but don't give it to Bob yet.
Expand Down Expand Up @@ -1355,7 +1355,7 @@ async fn test_unsigned_decryption() {
assert!(first_message.unsigned.relations.replace.is_none());
assert!(first_message.unsigned.relations.has_replacement());

assert!(raw_decrypted_event.encryption_info.is_some());
assert_matches!(raw_decrypted_event.encryption_state, EncryptionState::Decrypted(_));
let unsigned_encryption_info = raw_decrypted_event.unsigned_encryption_info.unwrap();
assert_eq!(unsigned_encryption_info.len(), 1);
let replace_encryption_result =
Expand Down Expand Up @@ -1399,7 +1399,7 @@ async fn test_unsigned_decryption() {
assert_matches!(&replace.content.relates_to, Some(Relation::Replacement(replace_content)));
assert_eq!(replace_content.new_content.msgtype.body(), second_message_text);

assert!(raw_decrypted_event.encryption_info.is_some());
assert_matches!(raw_decrypted_event.encryption_state, EncryptionState::Decrypted(_));
let unsigned_encryption_info = raw_decrypted_event.unsigned_encryption_info.unwrap();
assert_eq!(unsigned_encryption_info.len(), 1);
let replace_encryption_result =
Expand Down Expand Up @@ -1465,7 +1465,7 @@ async fn test_unsigned_decryption() {
let thread = first_message.unsigned.relations.thread.as_ref().unwrap();
assert_matches!(thread.latest_event.deserialize(), Ok(AnyMessageLikeEvent::RoomEncrypted(_)));

assert!(raw_decrypted_event.encryption_info.is_some());
assert_matches!(raw_decrypted_event.encryption_state, EncryptionState::Decrypted(_));
let unsigned_encryption_info = raw_decrypted_event.unsigned_encryption_info.unwrap();
assert_eq!(unsigned_encryption_info.len(), 2);
let replace_encryption_result =
Expand Down Expand Up @@ -1517,7 +1517,7 @@ async fn test_unsigned_decryption() {
let third_message = third_message.as_original().unwrap();
assert_eq!(third_message.content.body(), third_message_text);

assert!(raw_decrypted_event.encryption_info.is_some());
assert_matches!(raw_decrypted_event.encryption_state, EncryptionState::Decrypted(_));
let unsigned_encryption_info = raw_decrypted_event.unsigned_encryption_info.unwrap();
assert_eq!(unsigned_encryption_info.len(), 2);
let replace_encryption_result =
Expand Down
2 changes: 1 addition & 1 deletion crates/matrix-sdk-ui/src/timeline/controller/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -584,7 +584,7 @@ impl TimelineStateTransaction<'_> {
flow: Flow::Remote {
event_id: event_id.clone(),
raw_event: raw.clone(),
encryption_info: event.encryption_info,
encryption_state: event.encryption_state.clone(),
txn_id,
position,
},
Expand Down
3 changes: 2 additions & 1 deletion crates/matrix-sdk-ui/src/timeline/day_dividers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -609,6 +609,7 @@ enum DayDividerInsertError {
mod tests {
use assert_matches2::assert_let;
use eyeball_im::ObservableVector;
use matrix_sdk::deserialized_responses::EncryptionState;
use ruma::{owned_event_id, owned_user_id, uint, MilliSecondsSinceUnixEpoch};

use super::DayDividerAdjuster;
Expand All @@ -626,7 +627,7 @@ mod tests {
read_receipts: Default::default(),
is_own: false,
is_highlighted: false,
encryption_info: None,
encryption_state: EncryptionState::Unencrypted,
original_json: None,
latest_edit_json: None,
origin: crate::timeline::event_item::RemoteEventOrigin::Sync,
Expand Down
Loading