Skip to content

Commit

Permalink
crypto: Provide EncryptionState to hold reasons why events were UTD
Browse files Browse the repository at this point in the history
  • Loading branch information
andybalaam committed Sep 30, 2024
1 parent 866b6e5 commit 23f7827
Show file tree
Hide file tree
Showing 17 changed files with 159 additions and 68 deletions.
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::Utd(_) => {
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
59 changes: 45 additions & 14 deletions crates/matrix-sdk-common/src/deserialized_responses.rs
Original file line number Diff line number Diff line change
Expand Up @@ -296,15 +296,31 @@ pub struct EncryptionInfo {
pub verification_state: VerificationState,
}

#[derive(Clone, Debug, Deserialize, Serialize)]
pub enum EncryptionState {
Unencrypted,
Decrypted(EncryptionInfo),
Utd(UtdReason),
}

#[derive(Clone, Debug, Deserialize, Serialize)]
pub enum UtdReason {
FromInsecureDevice,
IdentityViolation,
/// The key was deliberately withheld. Contains the witheld code e.g.

Check warning on line 310 in crates/matrix-sdk-common/src/deserialized_responses.rs

View workflow job for this annotation

GitHub Actions / Spell Check with Typos

"witheld" should be "withheld".
/// "m.unverified" or "m.unavailable".
KeyWitheld(String),

Check warning on line 312 in crates/matrix-sdk-common/src/deserialized_responses.rs

View workflow job for this annotation

GitHub Actions / Spell Check with Typos

"Witheld" should be "Withheld".
Other,
}

/// 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 @@ -321,7 +337,12 @@ impl SyncTimelineEvent {
/// 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<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
Expand All @@ -333,7 +354,12 @@ impl SyncTimelineEvent {
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 +372,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 +399,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 +410,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 +427,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
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::Utd(_) => 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::Utd(_) => panic!("Unexpectedely 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
10 changes: 8 additions & 2 deletions crates/matrix-sdk-ui/src/timeline/controller/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ use std::{
use eyeball_im::{ObservableVector, ObservableVectorTransaction, ObservableVectorTransactionEntry};
use itertools::Itertools as _;
use matrix_sdk::{
deserialized_responses::SyncTimelineEvent, ring_buffer::RingBuffer, send_queue::SendHandle,
deserialized_responses::{EncryptionState, SyncTimelineEvent},
ring_buffer::RingBuffer,
send_queue::SendHandle,
};
use matrix_sdk_base::deserialized_responses::TimelineEvent;
#[cfg(test)]
Expand Down Expand Up @@ -584,7 +586,11 @@ impl TimelineStateTransaction<'_> {
flow: Flow::Remote {
event_id: event_id.clone(),
raw_event: raw.clone(),
encryption_info: event.encryption_info,
encryption_info: match event.encryption_state {
EncryptionState::Unencrypted => None,
EncryptionState::Decrypted(encryption_info) => Some(encryption_info),
EncryptionState::Utd(_) => None,
},
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
18 changes: 15 additions & 3 deletions crates/matrix-sdk-ui/src/timeline/event_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ use as_variant::as_variant;
use eyeball_im::{ObservableVectorTransaction, ObservableVectorTransactionEntry};
use indexmap::IndexMap;
use matrix_sdk::{
crypto::types::events::UtdCause, deserialized_responses::EncryptionInfo, send_queue::SendHandle,
crypto::types::events::UtdCause,
deserialized_responses::{EncryptionInfo, EncryptionState},
send_queue::SendHandle,
};
use ruma::{
events::{
Expand Down Expand Up @@ -598,7 +600,13 @@ impl<'a, 'o> TimelineEventHandler<'a, 'o> {
if let EventTimelineItemKind::Remote(remote_event) = &item.kind {
if let Flow::Remote { encryption_info, .. } = &self.ctx.flow {
new_item = new_item.with_kind(EventTimelineItemKind::Remote(
remote_event.with_encryption_info(encryption_info.clone()),
remote_event.with_encryption_state(
if let Some(encryption_info) = encryption_info {
EncryptionState::Decrypted(encryption_info.clone())
} else {
EncryptionState::Unencrypted
},
),
));
}
}
Expand Down Expand Up @@ -948,7 +956,11 @@ impl<'a, 'o> TimelineEventHandler<'a, 'o> {
read_receipts: self.ctx.read_receipts.clone(),
is_own: self.ctx.is_own_event,
is_highlighted: self.ctx.is_highlighted,
encryption_info: encryption_info.clone(),
encryption_state: if let Some(encryption_info) = encryption_info {
EncryptionState::Decrypted(encryption_info.clone())
} else {
EncryptionState::Unencrypted
},
original_json: Some(raw_event.clone()),
latest_edit_json: None,
origin,
Expand Down
Loading

0 comments on commit 23f7827

Please sign in to comment.