From e5575861feef5a70c99edd7a2cf5c5819552b671 Mon Sep 17 00:00:00 2001 From: Andy Balaam Date: Mon, 16 Dec 2024 17:17:56 +0000 Subject: [PATCH] task(crypto): Support receiving stable identifier for MSC4147 --- crates/matrix-sdk-crypto/CHANGELOG.md | 4 + .../olm/group_sessions/sender_data_finder.rs | 2 +- crates/matrix-sdk-crypto/src/olm/session.rs | 2 +- .../src/store/integration_tests.rs | 2 +- .../src/types/events/olm_v1.rs | 137 +++++++++++++++++- 5 files changed, 136 insertions(+), 11 deletions(-) diff --git a/crates/matrix-sdk-crypto/CHANGELOG.md b/crates/matrix-sdk-crypto/CHANGELOG.md index 5ddf3a801ad..b241e0f816d 100644 --- a/crates/matrix-sdk-crypto/CHANGELOG.md +++ b/crates/matrix-sdk-crypto/CHANGELOG.md @@ -6,6 +6,10 @@ All notable changes to this project will be documented in this file. ## [Unreleased] - ReleaseDate +- Accept stable identifier `sender_device_keys` for MSC4147 (Including device + keys with Olm-encrypted events). + ([#4420](https://github.com/matrix-org/matrix-rust-sdk/pull/4420)) + ## [0.9.0] - 2024-12-18 - Expose new API `DehydratedDevices::get_dehydrated_device_pickle_key`, `DehydratedDevices::save_dehydrated_device_pickle_key` diff --git a/crates/matrix-sdk-crypto/src/olm/group_sessions/sender_data_finder.rs b/crates/matrix-sdk-crypto/src/olm/group_sessions/sender_data_finder.rs index fcf0394a81b..0f23f013f48 100644 --- a/crates/matrix-sdk-crypto/src/olm/group_sessions/sender_data_finder.rs +++ b/crates/matrix-sdk-crypto/src/olm/group_sessions/sender_data_finder.rs @@ -171,7 +171,7 @@ impl<'a> SenderDataFinder<'a> { room_key_event: &'a DecryptedRoomKeyEvent, ) -> Result { // Does the to-device message contain the device_keys property from MSC4147? - if let Some(sender_device_keys) = &room_key_event.device_keys { + if let Some(sender_device_keys) = &room_key_event.sender_device_keys { // Yes: use the device keys to continue. // Validate the signature of the DeviceKeys supplied. diff --git a/crates/matrix-sdk-crypto/src/olm/session.rs b/crates/matrix-sdk-crypto/src/olm/session.rs index 939c488e74d..a51fbaa9dfe 100644 --- a/crates/matrix-sdk-crypto/src/olm/session.rs +++ b/crates/matrix-sdk-crypto/src/olm/session.rs @@ -380,6 +380,6 @@ mod tests { // DecryptedOlmV1Event let event: DecryptedOlmV1Event = serde_json::from_str(&bob_session_result.plaintext).unwrap(); - assert_eq!(event.device_keys.unwrap(), alice.device_keys()); + assert_eq!(event.sender_device_keys.unwrap(), alice.device_keys()); } } diff --git a/crates/matrix-sdk-crypto/src/store/integration_tests.rs b/crates/matrix-sdk-crypto/src/store/integration_tests.rs index 410d8dbde04..908415db347 100644 --- a/crates/matrix-sdk-crypto/src/store/integration_tests.rs +++ b/crates/matrix-sdk-crypto/src/store/integration_tests.rs @@ -1027,7 +1027,7 @@ macro_rules! cryptostore_integration_tests { recipient_keys: OlmV1Keys { ed25519: account.identity_keys().ed25519, }, - device_keys: None, + sender_device_keys: None, content: SecretSendContent::new(id.to_owned(), secret.to_owned()), }; diff --git a/crates/matrix-sdk-crypto/src/types/events/olm_v1.rs b/crates/matrix-sdk-crypto/src/types/events/olm_v1.rs index c9a04910b3f..06c8342106e 100644 --- a/crates/matrix-sdk-crypto/src/types/events/olm_v1.rs +++ b/crates/matrix-sdk-crypto/src/types/events/olm_v1.rs @@ -153,10 +153,10 @@ impl AnyDecryptedOlmEvent { pub fn sender_device_keys(&self) -> Option<&DeviceKeys> { match self { AnyDecryptedOlmEvent::Custom(_) => None, - AnyDecryptedOlmEvent::RoomKey(e) => e.device_keys.as_ref(), - AnyDecryptedOlmEvent::ForwardedRoomKey(e) => e.device_keys.as_ref(), - AnyDecryptedOlmEvent::SecretSend(e) => e.device_keys.as_ref(), - AnyDecryptedOlmEvent::Dummy(e) => e.device_keys.as_ref(), + AnyDecryptedOlmEvent::RoomKey(e) => e.sender_device_keys.as_ref(), + AnyDecryptedOlmEvent::ForwardedRoomKey(e) => e.sender_device_keys.as_ref(), + AnyDecryptedOlmEvent::SecretSend(e) => e.sender_device_keys.as_ref(), + AnyDecryptedOlmEvent::Dummy(e) => e.sender_device_keys.as_ref(), } } } @@ -176,8 +176,8 @@ where /// The recipient's signing keys of the encrypted event. pub recipient_keys: OlmV1Keys, /// The device keys if supplied as per MSC4147 - #[serde(rename = "org.matrix.msc4147.device_keys")] - pub device_keys: Option, + #[serde(alias = "org.matrix.msc4147.device_keys")] + pub sender_device_keys: Option, /// The type of the event. pub content: C, } @@ -201,7 +201,7 @@ impl DecryptedOlmV1Event { recipient: recipient.to_owned(), keys: OlmV1Keys { ed25519: key }, recipient_keys: OlmV1Keys { ed25519: key }, - device_keys, + sender_device_keys: device_keys, content, } } @@ -264,10 +264,18 @@ impl<'de> Deserialize<'de> for AnyDecryptedOlmEvent { #[cfg(test)] mod tests { + use std::collections::BTreeMap; + use assert_matches::assert_matches; + use ruma::{device_id, owned_user_id, KeyId}; use serde_json::{json, Value}; + use vodozemac::{Curve25519PublicKey, Ed25519PublicKey, Ed25519Signature}; use super::AnyDecryptedOlmEvent; + use crate::types::{ + events::olm_v1::DecryptedRoomKeyEvent, DeviceKey, DeviceKeys, EventEncryptionAlgorithm, + Signatures, + }; const ED25519_KEY: &str = "aOfOnlaeMb5GW1TxkZ8pXnblkGMgAvps+lAukrdYaZk"; @@ -363,6 +371,80 @@ mod tests { }) } + /// Return the JSON for creating sender device keys, and the matching + /// `DeviceKeys` object that should be created when the JSON is + /// deserialized. + fn sender_device_keys() -> (Value, DeviceKeys) { + let sender_device_keys_json = json!({ + "user_id": "@u:s.co", + "device_id": "DEV", + "algorithms": [ + "m.olm.v1.curve25519-aes-sha2" + ], + "keys": { + "curve25519:DEV": "c29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb28", + "ed25519:DEV": "b29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb28" + }, + "signatures": { + "@u:s.co": { + "ed25519:DEV": "mia28GKixFzOWKJ0h7Bdrdy2fjxiHCsst1qpe467FbW85H61UlshtKBoAXfTLlVfi0FX+/noJ8B3noQPnY+9Cg", + "ed25519:ssk": "mia28GKixFzOWKJ0h7Bdrdy2fjxiHCsst1qpe467FbW85H61UlshtKBoAXfTLlVfi0FX+/noJ8B3noQPnY+9Cg" + } + } + } + ); + + let user_id = owned_user_id!("@u:s.co"); + let device_id = device_id!("DEV"); + let ssk_id = device_id!("ssk"); + + let ed25519_device_key_id = KeyId::from_parts(ruma::DeviceKeyAlgorithm::Ed25519, device_id); + let curve25519_device_key_id = + KeyId::from_parts(ruma::DeviceKeyAlgorithm::Curve25519, device_id); + let ed25519_ssk_id = KeyId::from_parts(ruma::DeviceKeyAlgorithm::Ed25519, ssk_id); + + let mut keys = BTreeMap::new(); + keys.insert( + ed25519_device_key_id.clone(), + DeviceKey::Ed25519( + Ed25519PublicKey::from_base64("b29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb28") + .unwrap(), + ), + ); + keys.insert( + curve25519_device_key_id, + DeviceKey::Curve25519( + Curve25519PublicKey::from_base64("c29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb28") + .unwrap(), + ), + ); + + let mut signatures = Signatures::new(); + signatures.add_signature( + user_id.clone(), + ed25519_device_key_id, + Ed25519Signature::from_base64( + "mia28GKixFzOWKJ0h7Bdrdy2fjxiHCsst1qpe467FbW85H61UlshtKBoAXfTLlVfi0FX+/noJ8B3noQPnY+9Cg" + ).unwrap() + ); + signatures. add_signature( + user_id.clone(), + ed25519_ssk_id, + Ed25519Signature::from_base64( + "mia28GKixFzOWKJ0h7Bdrdy2fjxiHCsst1qpe467FbW85H61UlshtKBoAXfTLlVfi0FX+/noJ8B3noQPnY+9Cg" + ).unwrap() + ); + let sender_device_keys = DeviceKeys::new( + user_id, + device_id.to_owned(), + vec![EventEncryptionAlgorithm::OlmV1Curve25519AesSha2], + keys, + signatures, + ); + + (sender_device_keys_json, sender_device_keys) + } + #[test] fn deserialization() -> Result<(), serde_json::Error> { macro_rules! assert_deserialization_result { @@ -377,7 +459,7 @@ mod tests { } assert_deserialization_result!( - // `m.room_key + // `m.room_key` room_key_event => RoomKey, // `m.forwarded_room_key` @@ -392,4 +474,43 @@ mod tests { Ok(()) } + + #[test] + fn sender_device_keys_are_deserialized_unstable() { + let (sender_device_keys_json, sender_device_keys) = sender_device_keys(); + + // Given JSON for a room key event with sender_device_keys using the unstable + // prefix + let mut event_json = room_key_event(); + event_json + .as_object_mut() + .unwrap() + .insert("org.matrix.msc4147.device_keys".to_owned(), sender_device_keys_json); + + // When we deserialize it + let event: DecryptedRoomKeyEvent = serde_json::from_value(event_json) + .expect("JSON should deserialize to the right event type"); + + // Then it contains the sender_device_keys + assert_eq!(event.sender_device_keys, Some(sender_device_keys)); + } + + #[test] + fn sender_device_keys_are_deserialized() { + let (sender_device_keys_json, sender_device_keys) = sender_device_keys(); + + // Given JSON for a room key event with sender_device_keys + let mut event_json = room_key_event(); + event_json + .as_object_mut() + .unwrap() + .insert("sender_device_keys".to_owned(), sender_device_keys_json); + + // When we deserialize it + let event: DecryptedRoomKeyEvent = serde_json::from_value(event_json) + .expect("JSON should deserialize to the right event type"); + + // Then it contains the sender_device_keys + assert_eq!(event.sender_device_keys, Some(sender_device_keys)); + } }