Skip to content

Commit b29886c

Browse files
committed
test(crypto): Add a test that we refuse bundles if the sender isn't trusted enough
1 parent 360c2d7 commit b29886c

File tree

1 file changed

+208
-6
lines changed

1 file changed

+208
-6
lines changed

testing/matrix-sdk-integration-testing/src/tests/e2ee/shared_history.rs

Lines changed: 208 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,28 @@
1-
use std::ops::Deref;
1+
use std::{ops::Deref, time::Duration};
22

33
use anyhow::Result;
4-
use assert_matches2::assert_let;
4+
use assert_matches2::{assert_let, assert_matches};
55
use assign::assign;
6-
use futures::{FutureExt, StreamExt, pin_mut};
6+
use futures::{FutureExt, StreamExt, future, pin_mut};
77
use matrix_sdk::{
8-
assert_decrypted_message_eq,
8+
assert_decrypted_message_eq, assert_next_with_timeout,
9+
deserialized_responses::TimelineEventKind,
910
encryption::EncryptionSettings,
1011
ruma::{
11-
api::client::room::create_room::v3::{Request as CreateRoomRequest, RoomPreset},
12+
api::client::{
13+
room::create_room::v3::{Request as CreateRoomRequest, RoomPreset},
14+
uiaa::Password,
15+
},
1216
events::room::message::RoomMessageEventContent,
1317
},
18+
timeout::timeout,
1419
};
1520
use matrix_sdk_common::deserialized_responses::ProcessedToDeviceEvent;
1621
use matrix_sdk_ui::sync_service::SyncService;
1722
use similar_asserts::assert_eq;
1823
use tracing::{Instrument, info};
1924

20-
use crate::helpers::{SyncTokenAwareClient, TestClientBuilder};
25+
use crate::helpers::{SyncTokenAwareClient, TestClientBuilder, wait_for_room};
2126

2227
/// When we invite another user to a room with "joined" history visibility, we
2328
/// share the encryption history.
@@ -128,3 +133,200 @@ async fn test_history_share_on_invite() -> Result<()> {
128133

129134
Ok(())
130135
}
136+
137+
/// When we invite another user to a room with "joined" history visibility, we
138+
/// share the encryption history.
139+
#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
140+
async fn test_history_share_on_invite_pin_violation() -> Result<()> {
141+
let alice_span = tracing::info_span!("alice");
142+
let bob_span = tracing::info_span!("bob");
143+
144+
let encryption_settings =
145+
EncryptionSettings { auto_enable_cross_signing: true, ..Default::default() };
146+
147+
let alice = TestClientBuilder::new("alice")
148+
.use_sqlite()
149+
.encryption_settings(encryption_settings)
150+
.enable_share_history_on_invite(true)
151+
.build()
152+
.await?;
153+
154+
let alice_user_id = alice.user_id().expect("We should have access to Alice's user id");
155+
156+
let sync_service_span = tracing::info_span!(parent: &alice_span, "sync_service");
157+
let alice_sync_service = SyncService::builder(alice.clone())
158+
.with_parent_span(sync_service_span)
159+
.build()
160+
.await
161+
.expect("Could not build alice sync service");
162+
163+
alice.encryption().wait_for_e2ee_initialization_tasks().await;
164+
alice_sync_service.start().await;
165+
166+
// Bob first creates a room so we can get hold of Alice's identity and verify
167+
// it.
168+
169+
let bob = TestClientBuilder::new("bob")
170+
.encryption_settings(encryption_settings)
171+
.enable_share_history_on_invite(true)
172+
.build()
173+
.await?;
174+
175+
let bob_sync_service_span = tracing::info_span!(parent: &bob_span, "sync_service");
176+
let bob_sync_service = SyncService::builder(bob.clone())
177+
.with_parent_span(bob_sync_service_span)
178+
.build()
179+
.await
180+
.expect("Could not build alice sync service");
181+
182+
bob_sync_service.start().await;
183+
184+
let bob_room = bob
185+
.create_room(assign!(CreateRoomRequest::new(), {
186+
preset: Some(RoomPreset::PublicChat),
187+
}))
188+
.await?;
189+
bob_room.enable_encryption().await?;
190+
191+
// We invite Alice and try to send a message. This will force a /keys/query to
192+
// fetch the identity.
193+
bob_room.invite_user_by_id(alice_user_id).await?;
194+
bob_room.send(RoomMessageEventContent::text_plain("Hello Alice")).await?;
195+
196+
let alice_identity = bob
197+
.encryption()
198+
.get_user_identity(alice.user_id().unwrap())
199+
.await?
200+
.expect("Bob should have access to Alice's identity");
201+
202+
info!("Bob is verifying Alice");
203+
204+
alice_identity.verify().await?;
205+
206+
alice
207+
.get_room(bob_room.room_id())
208+
.expect("Alice should have access to bob's room")
209+
.join()
210+
.instrument(alice_span.clone())
211+
.await?;
212+
213+
// Alice creates a room ...
214+
let alice_room = alice
215+
.create_room(assign!(CreateRoomRequest::new(), {
216+
preset: Some(RoomPreset::PublicChat),
217+
}))
218+
.await?;
219+
alice_room.enable_encryption().await?;
220+
221+
info!(room_id = ?alice_room.room_id(), "Alice has created and enabled encryption in the room");
222+
223+
// ... and sends a message
224+
let event_id = alice_room
225+
.send(RoomMessageEventContent::text_plain("Hello Bob"))
226+
.await
227+
.expect("We should be able to send a message to the room")
228+
.event_id;
229+
230+
// Let us create some streams to get notified about a received bundle and a
231+
// changed identity. We need to create now, before any action happens, so we
232+
// don't miss the updates.
233+
let bundle_stream = bob
234+
.encryption()
235+
.historic_room_key_stream()
236+
.await
237+
.expect("We should be able to get the bundle stream");
238+
239+
let identity_stream = bob
240+
.encryption()
241+
.user_identities_stream()
242+
.await
243+
.expect("We should be able to get the identity stream");
244+
245+
// Alice will reset her identity, once Bob sees that a reset happened, Bob will
246+
// mark the identity to be in a pin violation.
247+
info!("Alice is resetting the identity");
248+
249+
let reset_future = async {
250+
if let Some(handle) = alice
251+
.encryption()
252+
.recovery()
253+
.reset_identity()
254+
.instrument(alice_span.clone())
255+
.await
256+
.expect("Resetting the identity should work")
257+
{
258+
handle
259+
.reset(Some(matrix_sdk::ruma::api::client::uiaa::AuthData::Password(
260+
Password::new(
261+
matrix_sdk::ruma::api::client::uiaa::UserIdentifier::UserIdOrLocalpart(
262+
alice_user_id.localpart().to_owned(),
263+
),
264+
alice_user_id.localpart().to_owned(),
265+
),
266+
)))
267+
.instrument(alice_span.clone())
268+
.await
269+
.expect("Providing the password to finalize the identity reset should work");
270+
}
271+
};
272+
273+
timeout(reset_future, Duration::from_secs(2))
274+
.await
275+
.expect("We should be able to reset our identity");
276+
277+
info!("Alice is inviting Bob");
278+
279+
// Alice invites Bob to the room
280+
alice_room.invite_user_by_id(bob.user_id().unwrap()).instrument(alice_span.clone()).await?;
281+
282+
// Alice is done. Bob has been invited and the room key bundle should have been
283+
// sent out. Let's stop syncing so the logs contain less noise.
284+
alice_sync_service.stop().await;
285+
286+
// Let's wait for the bundle to arrive.
287+
pin_mut!(bundle_stream);
288+
assert_next_with_timeout!(bundle_stream, 3000);
289+
290+
// Let us now wait till Alice's identity gets updated.
291+
info!("Bob is checking if alice's identity has changed");
292+
pin_mut!(identity_stream);
293+
let mut identity_stream = identity_stream
294+
.filter(|updates| future::ready(updates.changed.contains_key(alice_user_id)));
295+
assert_next_with_timeout!(identity_stream, 2000);
296+
297+
// Let's make sure that the pin violation was recorded.
298+
let alice_identity = bob
299+
.encryption()
300+
.get_user_identity(alice.user_id().unwrap())
301+
.await?
302+
.expect("Bob should have access to Alice's identity");
303+
304+
assert!(
305+
alice_identity.has_verification_violation(),
306+
"Alice should be in a pin violation scenario"
307+
);
308+
309+
// Now Bob can accept the invitation.
310+
let room = wait_for_room(&bob, alice_room.room_id()).await;
311+
room.join()
312+
.instrument(bob_span.clone())
313+
.await
314+
.expect("Bob should be able to accept the invitation from Alice");
315+
316+
let event = room
317+
.event(&event_id, None)
318+
.instrument(bob_span.clone())
319+
.await
320+
.expect("Bob should be able to fetch the historic event");
321+
322+
// Even though we received the bundle and got notified by the stream that we
323+
// received it, the event should not be decryptable as we should not have
324+
// accepted the bundle.
325+
assert_matches!(
326+
event.kind,
327+
TimelineEventKind::UnableToDecrypt { .. },
328+
"The event should not have been decrypted, we should not receive the historic room key bundle"
329+
);
330+
331+
Ok(())
332+
}

0 commit comments

Comments
 (0)