crypto: update senderdata integration tests

Extend the integration tests for megolm sender data to check that we update
existing inbound group sessions when we get a `/keys/query` response.
This commit is contained in:
Richard van der Hoff
2024-08-30 17:03:26 +01:00
committed by Richard van der Hoff
parent 3c27f83857
commit 73486b2b7b
3 changed files with 150 additions and 5 deletions
@@ -1122,11 +1122,14 @@ impl IdentityManager {
session: &mut InboundGroupSession,
device: &DeviceData,
) -> Result<(), CryptoStoreError> {
debug!("Updating existing InboundGroupSession with new SenderData");
use crate::olm::sender_data_finder::SessionDeviceCheckError::*;
match SenderDataFinder::find_using_device_data(&self.store, device.clone(), session).await {
Ok(sender_data) => {
debug!(
"Updating existing InboundGroupSession with new SenderData {:?}",
sender_data
);
session.sender_data = sender_data;
}
Err(CryptoStoreError(e)) => {
@@ -17,17 +17,24 @@
use std::collections::BTreeMap;
use as_variant::as_variant;
use matrix_sdk_test::{ruma_response_from_json, test_json};
use ruma::{
api::client::keys::{claim_keys, get_keys, upload_keys},
api::client::keys::{
claim_keys, get_keys, get_keys::v3::Response as KeysQueryResponse, upload_keys,
},
device_id,
encryption::OneTimeKey,
events::dummy::ToDeviceDummyEventContent,
serde::Raw,
user_id, DeviceId, OwnedDeviceKeyId, TransactionId, UserId,
};
use serde_json::json;
use crate::{store::Changes, types::events::ToDeviceEvent, DeviceData, OlmMachine};
use crate::{
store::Changes, types::events::ToDeviceEvent, CrossSigningBootstrapRequests, DeviceData,
OlmMachine, OutgoingRequests,
};
/// These keys need to be periodically uploaded to the server.
type OneTimeKeys = BTreeMap<OwnedDeviceKeyId, Raw<OneTimeKey>>;
@@ -182,3 +189,36 @@ pub async fn create_session(
let response = claim_keys::v3::Response::new(one_time_keys);
machine.inner.session_manager.create_sessions(&response).await.unwrap();
}
/// Given a set of requests returned by `bootstrap_cross_signing` for one user,
/// return a `/keys/query` response which might be returned to another user/
pub fn bootstrap_requests_to_keys_query_response(
bootstrap_requests: CrossSigningBootstrapRequests,
) -> KeysQueryResponse {
let mut kq_response = json!({});
// If we have a master key, add that to the response
if let Some(key) = bootstrap_requests.upload_signing_keys_req.master_key {
let user_id = key.user_id.clone();
kq_response["master_keys"] = json!({user_id: key});
}
// If we have a self-signing key, add that
if let Some(key) = bootstrap_requests.upload_signing_keys_req.self_signing_key {
let user_id = key.user_id.clone();
kq_response["self_signing_keys"] = json!({user_id: key});
}
// And if we have a device, add that
if let Some(dk) = bootstrap_requests
.upload_keys_req
.and_then(|req| as_variant!(req.request.as_ref(), OutgoingRequests::KeysUpload).cloned())
.and_then(|keys_upload_request| keys_upload_request.device_keys)
{
let user_id: String = dk.get_field("user_id").unwrap().unwrap();
let device_id: String = dk.get_field("device_id").unwrap().unwrap();
kq_response["device_keys"] = json!({user_id: { device_id: dk }});
}
ruma_response_from_json(&kq_response)
}
@@ -26,13 +26,16 @@ use serde_json::json;
use crate::{
machine::{
test_helpers::get_machine_pair_with_setup_sessions_test_helper,
test_helpers::{
bootstrap_requests_to_keys_query_response,
get_machine_pair_with_setup_sessions_test_helper,
},
tests::to_device_requests_to_content,
},
olm::{InboundGroupSession, SenderData},
store::RoomKeyInfo,
types::events::{room::encrypted::ToDeviceEncryptedEventContent, EventType, ToDeviceEvent},
EncryptionSettings, EncryptionSyncChanges, OlmMachine, Session,
DeviceData, EncryptionSettings, EncryptionSyncChanges, OlmMachine, Session,
};
/// Test the behaviour when a megolm session is received from an unknown device,
@@ -105,6 +108,105 @@ async fn test_receive_megolm_session_from_known_device() {
);
}
/// If we have a megolm session from an unknown device, test what happens when
/// we get a /keys/query response that includes that device.
#[async_test]
async fn test_update_unknown_device_senderdata_on_keys_query() {
// Given we have a megolm session from an unknown device
let (alice, bob) = get_machine_pair().await;
let mut bob_room_keys_received_stream = Box::pin(bob.store().room_keys_received_stream());
// `get_machine_pair_with_setup_sessions_test_helper` tells Bob about Alice's
// device keys, so to run this test, we need to make him forget them.
forget_devices_for_user(&bob, alice.user_id()).await;
// Alice starts a megolm session and shares the key with Bob, *without* sending
// the sender data.
let room_id = room_id!("!test:example.org");
let event = create_and_share_session_without_sender_data(&alice, &bob, room_id).await;
// Bob receives the to-device message
receive_to_device_event(&bob, &event).await;
// and now Bob should know about the session.
let room_key_info = get_room_key_received_update(&mut bob_room_keys_received_stream);
let session = get_inbound_group_session_or_panic(&bob, &room_key_info).await;
// Double-check that it is, in fact, an unknown device session.
assert_matches!(session.sender_data, SenderData::UnknownDevice { .. });
// When Bob gets a /keys/query response for Alice, that includes the
// sending device...
let alice_device = DeviceData::from_machine_test_helper(&alice).await.unwrap();
let kq_response = json!({
"device_keys": { alice.user_id() : { alice.device_id(): alice_device.as_device_keys()}}
});
bob.receive_keys_query_response(
&TransactionId::new(),
&matrix_sdk_test::ruma_response_from_json(&kq_response),
)
.await
.unwrap();
// Then Bob should have received an update about the session, and it should now
// be `SenderData::DeviceInfo`
let room_key_info = get_room_key_received_update(&mut bob_room_keys_received_stream);
let session = get_inbound_group_session_or_panic(&bob, &room_key_info).await;
assert_matches!(
session.sender_data,
SenderData::DeviceInfo {legacy_session, ..} => {
assert!(legacy_session); // TODO: change when https://github.com/matrix-org/matrix-rust-sdk/pull/3785 lands
}
);
}
/// If we have a megolm session from an unsigned device, test what happens when
/// we get a /keys/query response that includes that device.
#[async_test]
async fn test_update_device_info_senderdata_on_keys_query() {
// Given we have a megolm session from an unsigned device
let (alice, bob) = get_machine_pair().await;
let mut bob_room_keys_received_stream = Box::pin(bob.store().room_keys_received_stream());
// Alice starts a megolm session and shares the key with Bob
let room_id = room_id!("!test:example.org");
let to_device_requests = alice
.share_room_key(room_id, iter::once(bob.user_id()), EncryptionSettings::default())
.await
.unwrap();
let event = ToDeviceEvent::new(
alice.user_id().to_owned(),
to_device_requests_to_content(to_device_requests),
);
// Bob receives the to-device message
receive_to_device_event(&bob, &event).await;
// and now Bob should know about the session.
let room_key_info = get_room_key_received_update(&mut bob_room_keys_received_stream);
let session = get_inbound_group_session_or_panic(&bob, &room_key_info).await;
// Double-check that it is, in fact, an unverified device session.
assert_matches!(session.sender_data, SenderData::DeviceInfo { .. });
// When Bob receives a /keys/query response for Alice that includes a verifiable
// signature for her device
let bootstrap_requests = alice.bootstrap_cross_signing(false).await.unwrap();
let kq_response = bootstrap_requests_to_keys_query_response(bootstrap_requests);
bob.receive_keys_query_response(&TransactionId::new(), &kq_response).await.unwrap();
// Then Bob should have received an update about the session, and it should now
// be `SenderData::SenderUnverified`
let room_key_info = get_room_key_received_update(&mut bob_room_keys_received_stream);
let session = get_inbound_group_session_or_panic(&bob, &room_key_info).await;
assert_matches!(session.sender_data, SenderData::SenderUnverified(_));
}
/// Convenience wrapper for [`get_machine_pair_with_setup_sessions_test_helper`]
/// using standard user ids.
async fn get_machine_pair() -> (OlmMachine, OlmMachine) {