From e1c220e2f7c2e34d8a90256ba9515b8c894b732e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Tue, 29 Sep 2020 10:24:54 +0200 Subject: [PATCH] crypto: Test a key share cycle. --- matrix_sdk_crypto/src/key_request.rs | 155 ++++++++++++++++++++++++++- matrix_sdk_crypto/src/olm/account.rs | 57 ++++++++++ matrix_sdk_crypto/src/requests.rs | 10 ++ 3 files changed, 221 insertions(+), 1 deletion(-) diff --git a/matrix_sdk_crypto/src/key_request.rs b/matrix_sdk_crypto/src/key_request.rs index a222f424..2d326279 100644 --- a/matrix_sdk_crypto/src/key_request.rs +++ b/matrix_sdk_crypto/src/key_request.rs @@ -586,7 +586,12 @@ impl KeyRequestMachine { mod test { use dashmap::DashMap; use matrix_sdk_common::{ - events::{forwarded_room_key::ForwardedRoomKeyEventContent, ToDeviceEvent}, + api::r0::to_device::DeviceIdOrAllDevices, + events::{ + forwarded_room_key::ForwardedRoomKeyEventContent, + room::encrypted::EncryptedEventContent, room_key_request::RoomKeyRequestEventContent, + ToDeviceEvent, + }, identifiers::{room_id, user_id, DeviceIdBox, RoomId, UserId}, }; use matrix_sdk_test::async_test; @@ -628,6 +633,18 @@ mod test { Account::new(&bob_id(), &bob_device_id()) } + fn bob_machine() -> KeyRequestMachine { + let user_id = Arc::new(bob_id()); + let store = Store::new(user_id.clone(), Box::new(MemoryStore::new())); + + KeyRequestMachine::new( + user_id, + Arc::new(bob_device_id()), + store, + Arc::new(DashMap::new()), + ) + } + fn get_machine() -> KeyRequestMachine { let user_id = Arc::new(alice_id()); let store = Store::new(user_id.clone(), Box::new(MemoryStore::new())); @@ -884,4 +901,140 @@ mod test { .should_share_session(&bob_device, Some(&session)) .is_ok()); } + + #[async_test] + async fn key_share_cycle() { + let alice_machine = get_machine(); + let alice_account = account(); + + let bob_machine = bob_machine(); + let bob_account = bob_account(); + + // Create Olm sessions for our two accounts. + let (alice_session, bob_session) = alice_account.create_session_for(&bob_account).await; + + let alice_device = ReadOnlyDevice::from_account(&alice_account).await; + let bob_device = ReadOnlyDevice::from_account(&bob_account).await; + + // Populate our stores with Olm sessions and a Megolm session. + + alice_machine + .store + .save_sessions(&[alice_session]) + .await + .unwrap(); + alice_machine + .store + .save_devices(&[bob_device]) + .await + .unwrap(); + bob_machine + .store + .save_sessions(&[bob_session]) + .await + .unwrap(); + bob_machine + .store + .save_devices(&[alice_device]) + .await + .unwrap(); + + let (group_session, inbound_group_session) = bob_account + .create_group_session_pair_with_defaults(&room_id()) + .await + .unwrap(); + + bob_machine + .store + .save_inbound_group_sessions(&[inbound_group_session]) + .await + .unwrap(); + + // Alice wants to request the outbound group session from bob. + alice_machine + .create_outgoing_key_request( + &room_id(), + bob_account.identity_keys.curve25519(), + group_session.session_id(), + ) + .await + .unwrap(); + group_session.mark_shared_with(&alice_id(), &alice_device_id()); + + // Put the outbound session into bobs store. + bob_machine + .outbound_group_sessions + .insert(room_id(), group_session); + + // Get the request and convert it into a event. + let request = alice_machine + .outgoing_to_device_requests + .iter() + .next() + .unwrap(); + let id = request.request_id; + let content = request + .request + .to_device() + .unwrap() + .messages + .get(&alice_id()) + .unwrap() + .get(&DeviceIdOrAllDevices::AllDevices) + .unwrap(); + let content: RoomKeyRequestEventContent = serde_json::from_str(content.get()).unwrap(); + + drop(request); + alice_machine + .mark_outgoing_request_as_sent(&id) + .await + .unwrap(); + + let event = ToDeviceEvent { + sender: alice_id(), + content, + }; + + // Bob doesn't have any outgoing requests. + assert!(bob_machine.outgoing_to_device_requests.is_empty()); + + // Receive the room key request from alice. + bob_machine.receive_incoming_key_request(&event); + bob_machine.collect_incoming_key_requests().await.unwrap(); + // Now bob does have an outgoing request. + assert!(!bob_machine.outgoing_to_device_requests.is_empty()); + + // Get the request and convert it to a encrypted to-device event. + let request = bob_machine + .outgoing_to_device_requests + .iter() + .next() + .unwrap(); + + let id = request.request_id; + let content = request + .request + .to_device() + .unwrap() + .messages + .get(&alice_id()) + .unwrap() + .get(&DeviceIdOrAllDevices::DeviceId(alice_device_id())) + .unwrap(); + let content: EncryptedEventContent = serde_json::from_str(content.get()).unwrap(); + + drop(request); + bob_machine + .mark_outgoing_request_as_sent(&id) + .await + .unwrap(); + + let _event = ToDeviceEvent { + sender: bob_id(), + content, + }; + + // TODO test that alice can receive, decrypt and add the requested key + // to the store. + } } diff --git a/matrix_sdk_crypto/src/olm/account.rs b/matrix_sdk_crypto/src/olm/account.rs index 8be10151..3c39da04 100644 --- a/matrix_sdk_crypto/src/olm/account.rs +++ b/matrix_sdk_crypto/src/olm/account.rs @@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +#[cfg(test)] +use matrix_sdk_common::events::{room::encrypted::EncryptedEventContent, EventType}; use std::{ collections::BTreeMap, convert::{TryFrom, TryInto}, @@ -617,6 +619,61 @@ impl Account { self.create_group_session_pair(room_id, EncryptionSettings::default(), [].iter()) .await } + + #[cfg(test)] + pub(crate) async fn create_session_for(&self, other: &Account) -> (Session, Session) { + other.generate_one_time_keys_helper(1).await; + let one_time = other.signed_one_time_keys().await.unwrap(); + + let device = ReadOnlyDevice::from_account(other).await; + + let mut our_session = self + .create_outbound_session(device.clone(), &one_time) + .await + .unwrap(); + + let message = our_session + .encrypt(&device, EventType::Dummy, json!({})) + .await + .unwrap(); + let content = if let EncryptedEventContent::OlmV1Curve25519AesSha2(c) = message { + c + } else { + panic!("Invalid encrypted event algorithm"); + }; + + let own_ciphertext = content + .ciphertext + .get(other.identity_keys.curve25519()) + .unwrap(); + let message_type: u8 = own_ciphertext.message_type.try_into().unwrap(); + + let message = + OlmMessage::from_type_and_ciphertext(message_type.into(), own_ciphertext.body.clone()) + .unwrap(); + let message = if let OlmMessage::PreKey(m) = message { + m + } else { + panic!("Wrong Olm message type"); + }; + + let our_device = ReadOnlyDevice::from_account(self).await; + let other_session = other + .create_inbound_session( + our_device + .keys() + .get(&DeviceKeyId::from_parts( + DeviceKeyAlgorithm::Curve25519, + our_device.device_id(), + )) + .unwrap(), + message, + ) + .await + .unwrap(); + + (our_session, other_session) + } } impl PartialEq for Account { diff --git a/matrix_sdk_crypto/src/requests.rs b/matrix_sdk_crypto/src/requests.rs index f4cef39e..3a6ba4d9 100644 --- a/matrix_sdk_crypto/src/requests.rs +++ b/matrix_sdk_crypto/src/requests.rs @@ -99,6 +99,16 @@ pub enum OutgoingRequests { ToDeviceRequest(ToDeviceRequest), } +#[cfg(test)] +impl OutgoingRequests { + pub fn to_device(&self) -> Option<&ToDeviceRequest> { + match self { + OutgoingRequests::ToDeviceRequest(r) => Some(r), + _ => None, + } + } +} + impl From for OutgoingRequests { fn from(request: KeysQueryRequest) -> Self { OutgoingRequests::KeysQuery(request)