Skip to content

Commit 86dcedf

Browse files
uhoregpoljar
authored andcommitted
Check the user ID in the embedded device keys
1 parent 777ce05 commit 86dcedf

2 files changed

Lines changed: 146 additions & 11 deletions

File tree

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

Lines changed: 134 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,20 @@ use futures_core::Stream;
1919
use futures_util::{FutureExt, StreamExt};
2020
use matrix_sdk_common::deserialized_responses::ProcessedToDeviceEvent;
2121
use matrix_sdk_test::async_test;
22-
use ruma::{RoomId, TransactionId, UserId, room_id, user_id};
22+
use ruma::{
23+
DeviceKeyId, RoomId, TransactionId, UserId, canonical_json::to_canonical_value, owned_user_id,
24+
room_id, user_id,
25+
};
2326
use serde::Serialize;
2427
use serde_json::json;
2528
use tokio_stream::wrappers::errors::BroadcastStreamRecvError;
29+
use vodozemac::Curve25519SecretKey;
2630

2731
use crate::{
28-
DecryptionSettings, DeviceData, EncryptionSettings, EncryptionSyncChanges, OlmMachine, Session,
29-
TrustRequirement,
32+
Account, DecryptionSettings, DeviceData, EncryptionSettings, EncryptionSyncChanges, EventError,
33+
OlmError, OlmMachine, Session, TrustRequirement,
3034
machine::{
35+
DeviceKeyAlgorithm,
3136
test_helpers::{
3237
bootstrap_requests_to_keys_query_response,
3338
get_machine_pair_with_setup_sessions_test_helper,
@@ -36,7 +41,10 @@ use crate::{
3641
},
3742
olm::{InboundGroupSession, SenderData},
3843
store::types::RoomKeyInfo,
39-
types::events::{EventType, ToDeviceEvent, room::encrypted::ToDeviceEncryptedEventContent},
44+
types::{
45+
DeviceKey, DeviceKeys,
46+
events::{EventType, ToDeviceEvent, room::encrypted::ToDeviceEncryptedEventContent},
47+
},
4048
};
4149

4250
/// Test the behaviour when a megolm session is received from an unknown device,
@@ -54,7 +62,7 @@ async fn test_receive_megolm_session_from_unknown_device() {
5462
// When Alice starts a megolm session and shares the key with Bob, *without*
5563
// sending the sender data.
5664
let room_id = room_id!("!test:example.org");
57-
let event = create_and_share_session_without_sender_data(&alice, &bob, room_id).await;
65+
let event = create_and_share_session_with_custom_sender_data(&alice, &bob, room_id, None).await;
5866

5967
let decryption_settings =
6068
DecryptionSettings { sender_device_trust_requirement: TrustRequirement::Untrusted };
@@ -76,6 +84,112 @@ async fn test_receive_megolm_session_from_unknown_device() {
7684
);
7785
}
7886

87+
/// Test the behaviour when a megolm session is received, and the device keys in
88+
/// the to-device message are incorrect.
89+
#[async_test]
90+
async fn test_receive_megolm_session_with_bad_device_keys() {
91+
// In this test, Alice sends Bob several encrypted events, with various
92+
// contents for the device keys embedded in the encrypted data.
93+
let (alice, bob) = get_machine_pair().await;
94+
95+
let room_id = room_id!("!test:example.org");
96+
let decryption_settings =
97+
DecryptionSettings { sender_device_trust_requirement: TrustRequirement::Untrusted };
98+
99+
let alice_account = alice.store().load_account().await.unwrap().unwrap();
100+
let alice_device_keys = alice_account.device_keys();
101+
let alice_unsigned_device_keys = {
102+
let mut device_keys = alice_device_keys.clone();
103+
device_keys.signatures.clear();
104+
device_keys
105+
};
106+
let bob_store = bob.store();
107+
let mut bob_account = bob_store.load_account().await.unwrap().unwrap();
108+
109+
// Using Alice's correct device keys should succeed
110+
let sender_device_keys = serde_json::to_value(&alice_device_keys).unwrap();
111+
let event = create_and_share_session_with_custom_sender_data(
112+
&alice,
113+
&bob,
114+
room_id,
115+
Some(sender_device_keys),
116+
)
117+
.await;
118+
bob_account
119+
.decrypt_to_device_event(bob_store, &event, &decryption_settings)
120+
.await
121+
.expect("using Alice's correct device keys should succeed");
122+
123+
// An unsigned device key should error.
124+
let sender_device_keys = serde_json::to_value(&alice_unsigned_device_keys).unwrap();
125+
let event = create_and_share_session_with_custom_sender_data(
126+
&alice,
127+
&bob,
128+
room_id,
129+
Some(sender_device_keys),
130+
)
131+
.await;
132+
assert_matches!(
133+
bob_account.decrypt_to_device_event(bob_store, &event, &decryption_settings).await,
134+
Err(OlmError::EventError(EventError::InvalidSenderDeviceKeys))
135+
);
136+
137+
// Having the wrong user ID in the device key should error.
138+
let wrong_user_device_keys = {
139+
let mut device_keys = alice_unsigned_device_keys.clone();
140+
device_keys.user_id = owned_user_id!("@wrong_user:example.org");
141+
sign_device_keys(&alice_account, &mut device_keys);
142+
device_keys
143+
};
144+
let sender_device_keys = serde_json::to_value(&wrong_user_device_keys).unwrap();
145+
let event = create_and_share_session_with_custom_sender_data(
146+
&alice,
147+
&bob,
148+
room_id,
149+
Some(sender_device_keys),
150+
)
151+
.await;
152+
assert_matches!(
153+
bob_account.decrypt_to_device_event(bob_store, &event, &decryption_settings).await,
154+
Err(OlmError::EventError(EventError::InvalidSenderDeviceKeys))
155+
);
156+
157+
// Having the wrong ed25519 key in the device key should error.
158+
let wrong_ed25519_device_keys = {
159+
let mut device_keys = alice_unsigned_device_keys.clone();
160+
let secret_key = Curve25519SecretKey::new();
161+
device_keys.keys.insert(
162+
DeviceKeyId::from_parts(DeviceKeyAlgorithm::Curve25519, &device_keys.device_id),
163+
DeviceKey::Curve25519((&secret_key).into()),
164+
);
165+
sign_device_keys(&alice_account, &mut device_keys);
166+
device_keys
167+
};
168+
let sender_device_keys = serde_json::to_value(&wrong_ed25519_device_keys).unwrap();
169+
let event = create_and_share_session_with_custom_sender_data(
170+
&alice,
171+
&bob,
172+
room_id,
173+
Some(sender_device_keys),
174+
)
175+
.await;
176+
assert_matches!(
177+
bob_account.decrypt_to_device_event(bob_store, &event, &decryption_settings).await,
178+
Err(OlmError::EventError(EventError::InvalidSenderDeviceKeys))
179+
);
180+
}
181+
182+
fn sign_device_keys(account: &Account, device_keys: &mut DeviceKeys) {
183+
let json_device_keys = to_canonical_value(&device_keys).unwrap();
184+
let signature = account.sign_json(json_device_keys).unwrap();
185+
186+
device_keys.signatures.add_signature(
187+
device_keys.user_id.to_owned(),
188+
DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, &device_keys.device_id),
189+
signature,
190+
);
191+
}
192+
79193
/// Test the behaviour when a megolm session is received from a known, but
80194
/// unsigned, device.
81195
#[async_test]
@@ -131,7 +245,7 @@ async fn test_update_unknown_device_senderdata_on_keys_query() {
131245
// Alice starts a megolm session and shares the key with Bob, *without* sending
132246
// the sender data.
133247
let room_id = room_id!("!test:example.org");
134-
let event = create_and_share_session_without_sender_data(&alice, &bob, room_id).await;
248+
let event = create_and_share_session_with_custom_sender_data(&alice, &bob, room_id, None).await;
135249

136250
let decryption_settings =
137251
DecryptionSettings { sender_device_trust_requirement: TrustRequirement::Untrusted };
@@ -241,17 +355,20 @@ async fn forget_devices_for_user(machine: &OlmMachine, other_user: &UserId) {
241355
}
242356

243357
/// Create a new [`OutboundGroupSession`], and build a to-device event to share
244-
/// it with another [`OlmMachine`], *without* sending the MSC4147 sender data.
358+
/// it with another [`OlmMachine`], using custom MSC4147 sender data.
245359
///
246360
/// # Arguments
247361
///
248362
/// * `alice` - sending device.
249363
/// * `bob` - receiving device.
250364
/// * `room_id` - room to create a session for.
251-
async fn create_and_share_session_without_sender_data(
365+
/// * `sender_device_keys` - the MSC4147 sender data to include, or `None` to
366+
/// omit the sender data
367+
async fn create_and_share_session_with_custom_sender_data(
252368
alice: &OlmMachine,
253369
bob: &OlmMachine,
254370
room_id: &RoomId,
371+
sender_device_keys: Option<serde_json::Value>,
255372
) -> ToDeviceEvent<ToDeviceEncryptedEventContent> {
256373
let (outbound_session, _) = alice
257374
.inner
@@ -277,7 +394,7 @@ async fn create_and_share_session_without_sender_data(
277394
let mut olm_session: Session = olm_sessions.lock().await[0].clone();
278395

279396
let room_key_content = outbound_session.as_content().await;
280-
let plaintext = serde_json::to_string(&json!({
397+
let mut plaintext_value = json!({
281398
"sender": alice.user_id(),
282399
"sender_device": alice.device_id(),
283400
"keys": { "ed25519": alice.identity_keys().ed25519.to_base64() },
@@ -287,8 +404,14 @@ async fn create_and_share_session_without_sender_data(
287404
"recipient_keys": { "ed25519": bob.identity_keys().ed25519.to_base64() },
288405
"type": room_key_content.event_type(),
289406
"content": room_key_content,
290-
}))
291-
.unwrap();
407+
});
408+
if let Some(sender_device_keys) = sender_device_keys {
409+
plaintext_value
410+
.as_object_mut()
411+
.unwrap()
412+
.insert("org.matrix.msc4147.device_keys".to_owned(), sender_device_keys);
413+
}
414+
let plaintext = serde_json::to_string(&plaintext_value).unwrap();
292415

293416
let ciphertext = olm_session.encrypt_helper(&plaintext).await.unwrap();
294417
ToDeviceEvent::new(

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1728,6 +1728,9 @@ impl Account {
17281728
/// * The Ed25519 key in the device data matches that in the `keys` field
17291729
/// in the event, for consistency and sanity.
17301730
///
1731+
/// * The `user_id` property in the `sender_device_keys` matches the event
1732+
/// sender.
1733+
///
17311734
/// The first two checks are sufficient to bind together the Ed25519 and
17321735
/// Curve25519 keys:
17331736
///
@@ -1766,6 +1769,15 @@ impl Account {
17661769
return Ok(None);
17671770
};
17681771

1772+
if sender_device_keys.user_id != event.sender() {
1773+
warn!(
1774+
"Received a to-device message with sender_device_keys with incorrect user_id: expected {:?}, got {:?}",
1775+
event.sender(),
1776+
sender_device_keys.user_id
1777+
);
1778+
return Err(OlmError::EventError(EventError::InvalidSenderDeviceKeys));
1779+
}
1780+
17691781
// Check the signature within the device_keys structure
17701782
sender_device_keys.check_self_signature().map_err(|err| {
17711783
warn!(

0 commit comments

Comments
 (0)