Skip to content

Commit 55d475d

Browse files
authored
Merge pull request #4988 from matrix-org/rav/history_sharing/better_sender_data
crypto: improve SenderData stored with room key bundle data
2 parents b5b2450 + f349a66 commit 55d475d

File tree

4 files changed

+301
-110
lines changed

4 files changed

+301
-110
lines changed

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

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -952,9 +952,17 @@ impl OlmMachine {
952952
return Ok(());
953953
};
954954

955+
// We already checked that `sender_device_keys` matches the actual sender of the
956+
// message when we decrypted the message, which included doing
957+
// `DeviceData::try_from` on it, so it can't fail.
958+
959+
let sender_device_data =
960+
DeviceData::try_from(sender_device_keys).expect("failed to verify sender device keys");
961+
let sender_device = self.store().wrap_device_data(sender_device_data).await?;
962+
955963
changes.received_room_key_bundles.push(StoredRoomKeyBundleData {
956964
sender_user: event.sender.clone(),
957-
sender_data: SenderData::device_info(sender_device_keys.clone()),
965+
sender_data: SenderData::from_device(&sender_device),
958966
bundle_data: event.content.clone(),
959967
});
960968
Ok(())

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

Lines changed: 103 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
//! A set of helper functions for creating [`OlmMachine`]s, and pairs of
1616
//! interconnected machines.
1717
18-
use std::collections::BTreeMap;
18+
use std::{collections::BTreeMap, ops::Deref, sync::Arc};
1919

2020
use as_variant::as_variant;
2121
use matrix_sdk_test::{ruma_response_from_json, test_json};
@@ -32,11 +32,14 @@ use ruma::{
3232
user_id, DeviceId, OwnedOneTimeKeyId, TransactionId, UserId,
3333
};
3434
use serde_json::json;
35+
use tokio::sync::Mutex;
3536

3637
use crate::{
37-
store::{Changes, MemoryStore},
38-
types::{events::ToDeviceEvent, requests::AnyOutgoingRequest},
39-
CrossSigningBootstrapRequests, DeviceData, OlmMachine,
38+
olm::PrivateCrossSigningIdentity,
39+
store::{Changes, CryptoStoreWrapper, MemoryStore},
40+
types::{events::ToDeviceEvent, requests::AnyOutgoingRequest, DeviceKeys},
41+
verification::VerificationMachine,
42+
Account, CrossSigningBootstrapRequests, Device, DeviceData, OlmMachine, OtherUserIdentityData,
4043
};
4144

4245
/// These keys need to be periodically uploaded to the server.
@@ -276,3 +279,99 @@ pub fn bootstrap_requests_to_keys_query_response(
276279

277280
ruma_response_from_json(&kq_response)
278281
}
282+
283+
/// Create a [`VerificationMachine`] which won't do any useful verification.
284+
///
285+
/// Helper for [`create_signed_device_of_unverified_user`] and
286+
/// [`create_unsigned_device`].
287+
fn dummy_verification_machine() -> VerificationMachine {
288+
let account = Account::new(user_id!("@TEST_USER:example.com"));
289+
VerificationMachine::new(
290+
account.deref().clone(),
291+
Arc::new(Mutex::new(PrivateCrossSigningIdentity::new(account.user_id().to_owned()))),
292+
Arc::new(CryptoStoreWrapper::new(
293+
account.user_id(),
294+
account.device_id(),
295+
MemoryStore::new(),
296+
)),
297+
)
298+
}
299+
300+
/// Wrap the given [`DeviceKeys`] into a [`Device`], with no known owner
301+
/// identity.
302+
pub fn create_unsigned_device(device_keys: DeviceKeys) -> Device {
303+
Device {
304+
inner: DeviceData::try_from(&device_keys).unwrap(),
305+
verification_machine: dummy_verification_machine(),
306+
own_identity: None,
307+
device_owner_identity: None,
308+
}
309+
}
310+
311+
/// Sign the given [`DeviceKeys`] with a cross-signing identity, and wrap it up
312+
/// as a [`Device`] with that identity.
313+
pub async fn create_signed_device_of_unverified_user(
314+
mut device_keys: DeviceKeys,
315+
device_owner_identity: &PrivateCrossSigningIdentity,
316+
) -> Device {
317+
{
318+
let self_signing = device_owner_identity.self_signing_key.lock().await;
319+
let self_signing = self_signing.as_ref().unwrap();
320+
self_signing.sign_device(&mut device_keys).unwrap();
321+
}
322+
323+
let public_identity = OtherUserIdentityData::from_private(device_owner_identity).await;
324+
325+
let device = Device {
326+
inner: DeviceData::try_from(&device_keys).unwrap(),
327+
verification_machine: dummy_verification_machine(),
328+
own_identity: None,
329+
device_owner_identity: Some(public_identity.into()),
330+
};
331+
assert!(device.is_cross_signed_by_owner());
332+
device
333+
}
334+
335+
/// Sign the given [`DeviceKeys`] with a cross-signing identity, then sign the
336+
/// identity with our own identity, and wrap both up as a [`Device`].
337+
pub async fn create_signed_device_of_verified_user(
338+
mut device_keys: DeviceKeys,
339+
device_owner_identity: &PrivateCrossSigningIdentity,
340+
own_identity: &PrivateCrossSigningIdentity,
341+
) -> Device {
342+
// Sign the device keys with the owner's identity.
343+
{
344+
let self_signing = device_owner_identity.self_signing_key.lock().await;
345+
let self_signing = self_signing.as_ref().unwrap();
346+
self_signing.sign_device(&mut device_keys).unwrap();
347+
}
348+
349+
let mut public_identity = OtherUserIdentityData::from_private(device_owner_identity).await;
350+
351+
// Sign the public identity of the owner with our own identity.
352+
sign_user_identity_data(own_identity, &mut public_identity).await;
353+
354+
let device = Device {
355+
inner: DeviceData::try_from(&device_keys).unwrap(),
356+
verification_machine: dummy_verification_machine(),
357+
own_identity: Some(own_identity.to_public_identity().await.unwrap()),
358+
device_owner_identity: Some(public_identity.into()),
359+
};
360+
assert!(device.is_cross_signed_by_owner());
361+
assert!(device.is_cross_signing_trusted());
362+
device
363+
}
364+
365+
/// Sign a public user identity with our own user-signing key.
366+
pub async fn sign_user_identity_data(
367+
signer_private_identity: &PrivateCrossSigningIdentity,
368+
other_user_identity: &mut OtherUserIdentityData,
369+
) {
370+
let user_signing = signer_private_identity.user_signing_key.lock().await;
371+
372+
let user_signing = user_signing.as_ref().unwrap();
373+
let master = user_signing.sign_user(&*other_user_identity).unwrap();
374+
other_user_identity.master_key = Arc::new(master.try_into().unwrap());
375+
376+
user_signing.public_key().verify_master_key(other_user_identity.master_key()).unwrap();
377+
}

crates/matrix-sdk-crypto/src/olm/group_sessions/sender_data.rs

Lines changed: 169 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,13 @@ use std::{cmp::Ordering, fmt};
1616

1717
use ruma::{DeviceId, OwnedDeviceId, OwnedUserId, UserId};
1818
use serde::{de, de::Visitor, Deserialize, Deserializer, Serialize};
19+
use tracing::error;
1920
use vodozemac::Ed25519PublicKey;
2021

21-
use crate::types::{serialize_ed25519_key, DeviceKeys};
22+
use crate::{
23+
types::{serialize_ed25519_key, DeviceKeys},
24+
Device,
25+
};
2226

2327
/// Information about the sender of a megolm session where we know the
2428
/// cross-signing identity of the sender.
@@ -216,6 +220,58 @@ impl SenderData {
216220
Self::UnknownDevice { legacy_session: true, owner_check_failed: false }
217221
}
218222

223+
/// Create a [`SenderData`] representing the current verification state of
224+
/// the given device.
225+
///
226+
/// Depending on whether the device is correctly cross-signed or not, and
227+
/// whether the user has been verified or not, this can return
228+
/// [`SenderData::DeviceInfo`], [`SenderData::VerificationViolation`],
229+
/// [`SenderData::SenderUnverified`] or [`SenderData::SenderVerified`]
230+
pub fn from_device(sender_device: &Device) -> Self {
231+
// Is the device cross-signed?
232+
// Does the cross-signing key match that used to sign the device?
233+
// And is the signature in the device valid?
234+
let cross_signed = sender_device.is_cross_signed_by_owner();
235+
236+
if cross_signed {
237+
Self::from_cross_signed_device(sender_device)
238+
} else {
239+
// We have device keys, but they are not signed by the sender
240+
SenderData::device_info(sender_device.as_device_keys().clone())
241+
}
242+
}
243+
244+
fn from_cross_signed_device(sender_device: &Device) -> Self {
245+
let user_id = sender_device.user_id().to_owned();
246+
let device_id = Some(sender_device.device_id().to_owned());
247+
248+
let device_owner = sender_device.device_owner_identity.as_ref();
249+
let master_key = device_owner.and_then(|i| i.master_key().get_first_key());
250+
251+
match (device_owner, master_key) {
252+
(Some(device_owner), Some(master_key)) => {
253+
// We have user_id and master_key for the user sending the to-device message.
254+
let master_key = Box::new(master_key);
255+
let known_sender_data = KnownSenderData { user_id, device_id, master_key };
256+
if sender_device.is_cross_signing_trusted() {
257+
Self::SenderVerified(known_sender_data)
258+
} else if device_owner.was_previously_verified() {
259+
Self::VerificationViolation(known_sender_data)
260+
} else {
261+
Self::SenderUnverified(known_sender_data)
262+
}
263+
}
264+
265+
(_, _) => {
266+
// Surprisingly, there was no key in the MasterPubkey. We did not expect this:
267+
// treat it as if the device was not signed by this master key.
268+
//
269+
error!("MasterPubkey for user {user_id} does not contain any keys!");
270+
Self::device_info(sender_device.as_device_keys().clone())
271+
}
272+
}
273+
}
274+
219275
/// Returns `Greater` if this `SenderData` represents a greater level of
220276
/// trust than the supplied one, `Equal` if they have the same level, and
221277
/// `Less` if the supplied one has a greater level of trust.
@@ -344,10 +400,11 @@ pub enum SenderDataType {
344400

345401
#[cfg(test)]
346402
mod tests {
347-
use std::{cmp::Ordering, collections::BTreeMap};
403+
use std::{cmp::Ordering, collections::BTreeMap, ops::Deref};
348404

349405
use assert_matches2::assert_let;
350406
use insta::assert_json_snapshot;
407+
use matrix_sdk_test::async_test;
351408
use ruma::{
352409
device_id, owned_device_id, owned_user_id, user_id, DeviceKeyAlgorithm, DeviceKeyId,
353410
};
@@ -356,8 +413,13 @@ mod tests {
356413

357414
use super::SenderData;
358415
use crate::{
359-
olm::{KnownSenderData, PickledInboundGroupSession},
416+
machine::test_helpers::{
417+
create_signed_device_of_unverified_user, create_signed_device_of_verified_user,
418+
create_unsigned_device,
419+
},
420+
olm::{KnownSenderData, PickledInboundGroupSession, PrivateCrossSigningIdentity},
360421
types::{DeviceKey, DeviceKeys, EventEncryptionAlgorithm, Signatures},
422+
Account,
361423
};
362424

363425
#[test]
@@ -648,4 +710,108 @@ mod tests {
648710

649711
assert_eq!(master_key.to_base64(), "kOp9s4ClyQujYD7rRZA8xgE6kvYlqKSNnMrQNmSrcuE");
650712
}
713+
714+
#[async_test]
715+
async fn test_from_device_for_unsigned_device() {
716+
let bob_account =
717+
Account::with_device_id(user_id!("@bob:example.com"), device_id!("BOB_DEVICE"));
718+
let bob_device = create_unsigned_device(bob_account.device_keys());
719+
720+
let sender_data = SenderData::from_device(&bob_device);
721+
722+
assert_eq!(
723+
sender_data,
724+
SenderData::DeviceInfo {
725+
device_keys: bob_device.device_keys.deref().clone(),
726+
legacy_session: false
727+
}
728+
);
729+
}
730+
731+
#[async_test]
732+
async fn test_from_device_for_unverified_user() {
733+
let bob_identity =
734+
PrivateCrossSigningIdentity::new(user_id!("@bob:example.com").to_owned());
735+
let bob_account =
736+
Account::with_device_id(user_id!("@bob:example.com"), device_id!("BOB_DEVICE"));
737+
let bob_device = create_signed_device_of_unverified_user(
738+
bob_account.device_keys().clone(),
739+
&bob_identity,
740+
)
741+
.await;
742+
743+
let sender_data = SenderData::from_device(&bob_device);
744+
745+
assert_eq!(
746+
sender_data,
747+
SenderData::SenderUnverified(KnownSenderData {
748+
user_id: bob_account.user_id().to_owned(),
749+
device_id: Some(bob_account.device_id().to_owned()),
750+
master_key: Box::new(
751+
bob_identity.master_public_key().await.unwrap().get_first_key().unwrap()
752+
),
753+
})
754+
);
755+
}
756+
757+
#[async_test]
758+
async fn test_from_device_for_verified_user() {
759+
let alice_account =
760+
Account::with_device_id(user_id!("@alice:example.com"), device_id!("ALICE_DEVICE"));
761+
let alice_identity = PrivateCrossSigningIdentity::with_account(&alice_account).await.0;
762+
763+
let bob_identity =
764+
PrivateCrossSigningIdentity::new(user_id!("@bob:example.com").to_owned());
765+
let bob_account =
766+
Account::with_device_id(user_id!("@bob:example.com"), device_id!("BOB_DEVICE"));
767+
let bob_device = create_signed_device_of_verified_user(
768+
bob_account.device_keys().clone(),
769+
&bob_identity,
770+
&alice_identity,
771+
)
772+
.await;
773+
774+
let sender_data = SenderData::from_device(&bob_device);
775+
776+
assert_eq!(
777+
sender_data,
778+
SenderData::SenderVerified(KnownSenderData {
779+
user_id: bob_account.user_id().to_owned(),
780+
device_id: Some(bob_account.device_id().to_owned()),
781+
master_key: Box::new(
782+
bob_identity.master_public_key().await.unwrap().get_first_key().unwrap()
783+
),
784+
})
785+
);
786+
}
787+
788+
#[async_test]
789+
async fn test_from_device_for_verification_violation_user() {
790+
let bob_identity =
791+
PrivateCrossSigningIdentity::new(user_id!("@bob:example.com").to_owned());
792+
let bob_account =
793+
Account::with_device_id(user_id!("@bob:example.com"), device_id!("BOB_DEVICE"));
794+
let bob_device =
795+
create_signed_device_of_unverified_user(bob_account.device_keys(), &bob_identity).await;
796+
bob_device
797+
.device_owner_identity
798+
.as_ref()
799+
.unwrap()
800+
.other()
801+
.unwrap()
802+
.mark_as_previously_verified();
803+
804+
let sender_data = SenderData::from_device(&bob_device);
805+
806+
assert_eq!(
807+
sender_data,
808+
SenderData::VerificationViolation(KnownSenderData {
809+
user_id: bob_account.user_id().to_owned(),
810+
device_id: Some(bob_account.device_id().to_owned()),
811+
master_key: Box::new(
812+
bob_identity.master_public_key().await.unwrap().get_first_key().unwrap()
813+
),
814+
})
815+
);
816+
}
651817
}

0 commit comments

Comments
 (0)