Skip to content

Commit

Permalink
crypto: new method OlmMachine::try_decrypt_room_event
Browse files Browse the repository at this point in the history
  • Loading branch information
richvdh committed Oct 4, 2024
1 parent b642cb8 commit 53678c7
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 24 deletions.
11 changes: 11 additions & 0 deletions crates/matrix-sdk-common/src/deserialized_responses.rs
Original file line number Diff line number Diff line change
Expand Up @@ -585,6 +585,17 @@ impl fmt::Debug for TimelineEventInner {
}
}

/// The result of an attempt to decrypt a room event: either a successful
/// decryption, or information on a failure.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum RoomEventDecryptionResult {
/// A successfully-decrypted encrypted event.
Decrypted(DecryptedRoomEvent),

/// We were unable to decrypt the event
UnableToDecrypt(UnableToDecryptInfo),
}

#[derive(Clone, Serialize, Deserialize)]
/// A successfully-decrypted encrypted event.
pub struct DecryptedRoomEvent {
Expand Down
65 changes: 50 additions & 15 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, DecryptedRoomEvent, DeviceLinkProblem, EncryptionInfo, UnableToDecryptInfo,
UnsignedDecryptionResult, UnsignedEventLocation, VerificationLevel, VerificationState,
AlgorithmInfo, DecryptedRoomEvent, DeviceLinkProblem, EncryptionInfo,
RoomEventDecryptionResult, UnableToDecryptInfo, UnsignedDecryptionResult,
UnsignedEventLocation, VerificationLevel, VerificationState,
},
BoxFuture,
};
Expand Down Expand Up @@ -1735,6 +1736,40 @@ impl OlmMachine {
}
}

/// Attempt to decrypt an event from a room timeline, returning information
/// on the failure if it fails.
///
/// # Arguments
///
/// * `event` - The event that should be decrypted.
///
/// * `room_id` - The ID of the room where the event was sent to.
///
/// # Returns
///
/// The decrypted event, if it was successfully decrypted. Otherwise,
/// information on the failure, unless the failure was due to an
/// internal error, in which case, an `Err` result.
pub async fn try_decrypt_room_event(
&self,
raw_event: &Raw<EncryptedEvent>,
room_id: &RoomId,
decryption_settings: &DecryptionSettings,
) -> Result<RoomEventDecryptionResult, CryptoStoreError> {
match self.decrypt_room_event_inner(raw_event, room_id, true, decryption_settings).await {
Ok(decrypted) => Ok(RoomEventDecryptionResult::Decrypted(decrypted)),
Err(MegolmError::Store(e)) => {
// Pass through crypto store errors, which indicate a problem with our
// application, rather than a UTD.
Err(e)
}
Err(_) => Ok(RoomEventDecryptionResult::UnableToDecrypt(UnableToDecryptInfo {
session_id: session_id_from_event(raw_event),
// TODO: add an error code
})),
}
}

/// Decrypt an event from a room timeline.
///
/// # Arguments
Expand Down Expand Up @@ -1903,19 +1938,9 @@ impl OlmMachine {
*event = serde_json::to_value(decrypted_event.event).ok()?;
Some(UnsignedDecryptionResult::Decrypted(decrypted_event.encryption_info))
}
Err(_) => {
let session_id =
raw_event.deserialize().ok().and_then(|ev| match ev.content.scheme {
RoomEventEncryptionScheme::MegolmV1AesSha2(s) => Some(s.session_id),
#[cfg(feature = "experimental-algorithms")]
RoomEventEncryptionScheme::MegolmV2AesSha2(s) => Some(s.session_id),
RoomEventEncryptionScheme::Unknown(_) => None,
});

Some(UnsignedDecryptionResult::UnableToDecrypt(UnableToDecryptInfo {
session_id,
}))
}
Err(_) => Some(UnsignedDecryptionResult::UnableToDecrypt(UnableToDecryptInfo {
session_id: session_id_from_event(&raw_event),
})),
}
})
}
Expand Down Expand Up @@ -2541,6 +2566,16 @@ pub struct EncryptionSyncChanges<'a> {
pub next_batch_token: Option<String>,
}

/// Attempt to extract a megolm session ID from an encrypted room event.
fn session_id_from_event(raw_event: &Raw<EncryptedEvent>) -> Option<String> {
raw_event.deserialize().ok().and_then(|ev| match ev.content.scheme {
RoomEventEncryptionScheme::MegolmV1AesSha2(s) => Some(s.session_id),
#[cfg(feature = "experimental-algorithms")]
RoomEventEncryptionScheme::MegolmV2AesSha2(s) => Some(s.session_id),
RoomEventEncryptionScheme::Unknown(_) => None,
})
}

#[cfg(test)]
pub(crate) mod test_helpers;

Expand Down
22 changes: 13 additions & 9 deletions crates/matrix-sdk-crypto/src/machine/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@

use std::{collections::BTreeMap, iter, ops::Not, sync::Arc, time::Duration};

use assert_matches2::assert_matches;
use assert_matches2::{assert_let, assert_matches};
use futures_util::{pin_mut, FutureExt, StreamExt};
use itertools::Itertools;
use matrix_sdk_common::deserialized_responses::{
UnableToDecryptInfo, UnsignedDecryptionResult, UnsignedEventLocation,
RoomEventDecryptionResult, 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 @@ -555,13 +555,11 @@ async fn test_megolm_encryption() {

let decryption_settings =
DecryptionSettings { sender_device_trust_requirement: TrustRequirement::Untrusted };
let decrypted_event = bob
.decrypt_room_event(&event, room_id, &decryption_settings)
.await
.unwrap()
.event
.deserialize()
.unwrap();

let decryption_result =
bob.try_decrypt_room_event(&event, room_id, &decryption_settings).await.unwrap();
assert_let!(RoomEventDecryptionResult::Decrypted(decrypted_event) = decryption_result);
let decrypted_event = decrypted_event.event.deserialize().unwrap();

if let AnyMessageLikeEvent::RoomMessage(MessageLikeEvent::Original(
OriginalMessageLikeEvent { sender, content, .. },
Expand Down Expand Up @@ -678,6 +676,12 @@ async fn test_withheld_unverified() {

let err = decrypt_result.err().unwrap();
assert_matches!(err, MegolmError::MissingRoomKey(Some(WithheldCode::Unverified)));

// Also check `try_decrypt_room_event`.
let decrypt_result =
bob.try_decrypt_room_event(&room_event, room_id, &decryption_settings).await.unwrap();
assert_let!(RoomEventDecryptionResult::UnableToDecrypt(utd_info) = decrypt_result);
assert!(utd_info.session_id.is_some());
}

/// Test what happens when we feed an unencrypted event into the decryption
Expand Down

0 comments on commit 53678c7

Please sign in to comment.