Skip to content

Commit 35f354b

Browse files
committed
crypto: fall back to sender_device_keys for encrypted to-device messages
When receiving an encrypted to-device message, if the sender device is not in the store, but the event includes `sender_device_keys`, use `sender_device_keys` to do the verification checks etc. Fixes: #5768
1 parent e7fa8a4 commit 35f354b

File tree

4 files changed

+84
-28
lines changed

4 files changed

+84
-28
lines changed

crates/matrix-sdk-crypto/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ All notable changes to this project will be documented in this file.
88

99
### Bug Fixes
1010

11+
- Fix a bug which caused encrypted to-device messages from unknown devices to be ignored.
12+
([#5763](https://github.com/matrix-org/matrix-rust-sdk/pull/5763))
1113
- Fix a bug which caused history shared on invite to be ignored when "exclude insecure devices" was enabled.
1214
([#5763](https://github.com/matrix-org/matrix-rust-sdk/pull/5763))
1315
- Fix a bug introduced in 0.14.0 which meant that the serialization of the value returned by `OtherUserIdentity::verification_request_content` did not include a `msgtype` field.

crates/matrix-sdk-crypto/src/machine/test_helpers.rs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,22 @@ pub async fn send_and_receive_encrypted_to_device_test_helper(
197197
.await
198198
.expect("Should have encrypted the content");
199199

200-
let event = ToDeviceEvent::new(sender.user_id().to_owned(), raw_encrypted);
200+
receive_encrypted_to_device_test_helper(
201+
sender.user_id(),
202+
recipient,
203+
decryption_settings,
204+
raw_encrypted,
205+
)
206+
.await
207+
}
208+
209+
pub async fn receive_encrypted_to_device_test_helper(
210+
sender: &UserId,
211+
recipient: &OlmMachine,
212+
decryption_settings: &DecryptionSettings,
213+
raw_encrypted: Raw<ToDeviceEncryptedEventContent>,
214+
) -> ProcessedToDeviceEvent {
215+
let event = ToDeviceEvent::new(sender.to_owned(), raw_encrypted);
201216

202217
let event = json_convert(&event).unwrap();
203218

crates/matrix-sdk-crypto/src/machine/tests/send_encrypted_to_device.rs

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ use crate::{
3030
test_helpers::{
3131
build_encrypted_to_device_content_without_sender_data, build_session_for_pair,
3232
get_machine_pair, get_machine_pair_with_session, get_prepared_machine_test_helper,
33+
receive_encrypted_to_device_test_helper,
3334
send_and_receive_encrypted_to_device_test_helper,
3435
},
3536
tests::{self, decryption_verification_state::mark_alice_identity_as_verified_test_helper},
@@ -49,6 +50,8 @@ use crate::{
4950
EncryptionSyncChanges, LocalTrust, OlmError, OlmMachine, TrustRequirement,
5051
};
5152

53+
/// Happy path test: encrypt a to-device message, and check it is successfully
54+
/// decrypted by the recipient, and that all the metadata is set as expected.
5255
#[async_test]
5356
async fn test_send_encrypted_to_device() {
5457
let (alice, bob) =
@@ -114,14 +117,13 @@ async fn test_send_encrypted_to_device() {
114117
);
115118
}
116119

117-
#[async_test]
118-
async fn test_receive_custom_encrypted_to_device_fails_if_device_unknown() {
119-
// When decrypting a custom to device, we expect the recipient to know the
120-
// sending device. If the device is not known decryption will fail (see
121-
// `EventError(MissingSigningKey)`). The only exception is room keys where
122-
// this check can be delayed. This is a reason why there is no test for
123-
// verification_state `DeviceLinkProblem::MissingDevice`
124120

121+
/// If the sender device is genuinely unknown (it is not in the store, nor does
122+
/// the to-device message contain `sender_device_keys`), decryption will fail,
123+
/// with `EventError::MissingSigningKey`.
124+
#[async_test]
125+
async fn test_receive_custom_encrypted_to_device_with_no_sender_device_keys_fails_if_device_unknown(
126+
) {
125127
let (bob, otk) = get_prepared_machine_test_helper(bob_id(), false).await;
126128

127129
let alice = OlmMachine::new(tests::alice_id(), tests::alice_device_id()).await;
@@ -141,12 +143,22 @@ async fn test_receive_custom_encrypted_to_device_fails_if_device_unknown() {
141143
let decryption_settings =
142144
DecryptionSettings { sender_device_trust_requirement: TrustRequirement::Untrusted };
143145

144-
let processed_event = send_and_receive_encrypted_to_device_test_helper(
146+
// We need to suppress the sender_data field to correctly emulate an unknown
147+
// device
148+
let bob_device = alice.get_device(bob.user_id(), bob.device_id(), None).await.unwrap().unwrap();
149+
let raw_encrypted = build_encrypted_to_device_content_without_sender_data(
145150
&alice,
146-
&bob,
151+
&bob_device.device_keys,
147152
custom_event_type,
148153
&custom_content,
154+
)
155+
.await;
156+
157+
let processed_event = receive_encrypted_to_device_test_helper(
158+
alice.user_id(),
159+
&bob,
149160
&decryption_settings,
161+
Raw::new(&raw_encrypted).unwrap(),
150162
)
151163
.await;
152164

crates/matrix-sdk-crypto/src/olm/account.rs

Lines changed: 45 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1541,13 +1541,17 @@ impl Account {
15411541

15421542
/// Look up the [`Device`] that sent us a successfully-decrypted event.
15431543
///
1544-
/// Also validates the `sender_device_keys` field, if present.
1544+
/// We first look for the sender device in our store; if it is found then we
1545+
/// return that (having checked that the keys match). If the device is
1546+
/// not found in the store, we return the details
1547+
/// from `sender_device_keys`, if present. If the device is not in the
1548+
/// store, and the event lacks `sender_device_keys`, an error is returned.
1549+
///
1550+
/// Also validates the `sender_device_keys` field, if present, regardless of
1551+
/// whether it is used.
15451552
///
15461553
/// `m.room_key` events are special-cased and return `None`: we look up
15471554
/// their devices later on.
1548-
///
1549-
/// For other events, we look up the device in the store, and return the
1550-
/// details.
15511555
async fn get_event_sender_device(
15521556
store: &Store,
15531557
sender_key: Curve25519PublicKey,
@@ -1557,7 +1561,7 @@ impl Account {
15571561
// WARN: If you move or modify this check, ensure that the code below is still
15581562
// valid. The processing of the historic room key bundle depends on this being
15591563
// here.
1560-
Self::check_sender_device_keys(event, sender_key)?;
1564+
let sender_device_keys = Self::check_sender_device_keys(event, sender_key)?;
15611565
if let AnyDecryptedOlmEvent::RoomKey(_) = event {
15621566
// If this event is an `m.room_key` event, defer the check for
15631567
// the Ed25519 key of the sender until we decrypt room events.
@@ -1569,23 +1573,41 @@ impl Account {
15691573
// MSC4268 requires room key bundle events to have a `sender_device_keys` field.
15701574
// Enforce that now.
15711575
if let AnyDecryptedOlmEvent::RoomKeyBundle(_) = event {
1572-
event.sender_device_keys().ok_or(EventError::MissingSigningKey).inspect_err(|_| {
1576+
sender_device_keys.ok_or(EventError::MissingSigningKey).inspect_err(|_| {
15731577
warn!("The room key bundle was missing the sender device keys in the event")
15741578
})?;
15751579
}
15761580

1577-
let device = store
1578-
.get_device_from_curve_key(event.sender(), sender_key)
1579-
.await?
1580-
.ok_or(EventError::MissingSigningKey)?;
1581+
// For event types other than `m.room_key`, we need to look up the device in the
1582+
// database irrespective of whether the `sender_device_keys` field is
1583+
// present in the event, because it may have been marked as "locally
1584+
// trusted" in the database.
1585+
let store_device = store.get_device_from_curve_key(event.sender(), sender_key).await?;
1586+
1587+
match (store_device, sender_device_keys) {
1588+
// If the device is in the database, it had better have an Ed25519 key which
1589+
// matches that in the event.
1590+
(Some(device), _) => {
1591+
let key = device.ed25519_key().ok_or(EventError::MissingSigningKey)?;
1592+
if key != event.keys().ed25519 {
1593+
return Err(EventError::MismatchedKeys(
1594+
key.into(),
1595+
event.keys().ed25519.into(),
1596+
)
1597+
.into());
1598+
}
1599+
Ok(Some(device))
1600+
}
15811601

1582-
let key = device.ed25519_key().ok_or(EventError::MissingSigningKey)?;
1602+
(None, Some(sender_device_keys)) => {
1603+
// We have already validated the signature on `sender_device_keys`, so this
1604+
// try_into cannot fail.
1605+
let sender_device_data = sender_device_keys.try_into().unwrap();
1606+
Ok(Some(store.wrap_device_data(sender_device_data).await?))
1607+
}
15831608

1584-
if key != event.keys().ed25519 {
1585-
return Err(EventError::MismatchedKeys(key.into(), event.keys().ed25519.into()).into());
1609+
(None, None) => Err(OlmError::EventError(EventError::MissingSigningKey)),
15861610
}
1587-
1588-
Ok(Some(device))
15891611
}
15901612

15911613
/// Return true if:
@@ -1733,13 +1755,18 @@ impl Account {
17331755
/// * `sender_key` - The Curve25519 key that the sender used to establish
17341756
/// the Olm session that was used to decrypt the event.
17351757
///
1758+
/// # Returns
1759+
///
1760+
/// A reference to the `sender_device_keys` in the event, if it exists and
1761+
/// is valid.
1762+
///
17361763
/// [MSC4147]: https://github.com/matrix-org/matrix-spec-proposals/pull/4147
17371764
fn check_sender_device_keys(
17381765
event: &AnyDecryptedOlmEvent,
17391766
sender_key: Curve25519PublicKey,
1740-
) -> OlmResult<()> {
1767+
) -> OlmResult<Option<&DeviceKeys>> {
17411768
let Some(sender_device_keys) = event.sender_device_keys() else {
1742-
return Ok(());
1769+
return Ok(None);
17431770
};
17441771

17451772
// Check the signature within the device_keys structure
@@ -1774,7 +1801,7 @@ impl Account {
17741801
return Err(OlmError::EventError(EventError::InvalidSenderDeviceKeys));
17751802
}
17761803

1777-
Ok(())
1804+
Ok(Some(sender_device_keys))
17781805
}
17791806

17801807
/// Internal use only.

0 commit comments

Comments
 (0)