From 2ded95dcd7b95b9a3ae6134c5b50288fde7ed364 Mon Sep 17 00:00:00 2001 From: Valere Date: Fri, 9 May 2025 19:19:49 +0200 Subject: [PATCH] Support ProcessedToDeviceEvent variants returned by receiveSyncChanges --- CHANGELOG.md | 11 ++++- Cargo.lock | 15 ++---- Cargo.toml | 9 ++-- index.d.ts | 2 +- src/machine.rs | 14 ++++-- src/types.rs | 67 +++++++++++++++++++++++++ tests/helper.js | 7 ++- tests/machine.test.ts | 110 +++++++++++++++++++++++++++++++++--------- 8 files changed, 191 insertions(+), 44 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 03e589de6..104da702d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,17 @@ # UNRELEASED +- Update matrix-rusk-sdk to `35a2ce9`, which includes: + + - Add variants for plain text and encrypted to-device events ([#4935](https://github.com/matrix-org/matrix-rust-sdk/pull/4935)) + +- **BREAKING**: `OlmMachine.receiveSyncChanges` now returns a list of + `ProcessedToDeviceEvent` instead of a json encoded list of json encoded events. + This allows to make the difference between an event that was sent in clear and + the same event successfully decrypted. + # matrix-sdk-crypto-wasm v14.1.0 -- Update matrix-rusk-sdk to `0.11.0`, which includees: +- Update matrix-rusk-sdk to `0.11.0`, which includes: - Add support for the shared history flag defined in [MSC3061](https://github.com/matrix-org/matrix-spec-proposals/pull/3061). diff --git a/Cargo.lock b/Cargo.lock index ad091d24b..ccff7d836 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1145,8 +1145,7 @@ dependencies = [ [[package]] name = "matrix-sdk-common" version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75aa988a96e4a85e2b854f4f6487d31f1833e8abf3eebc1b8c13274a68dacf45" +source = "git+https://github.com/matrix-org/matrix-rust-sdk?rev=35a2ce97d84af8cea6b9e1850a8ff4ff5c66d712#35a2ce97d84af8cea6b9e1850a8ff4ff5c66d712" dependencies = [ "async-trait", "eyeball-im", @@ -1169,8 +1168,7 @@ dependencies = [ [[package]] name = "matrix-sdk-crypto" version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe2a8a811f9809f37c360f7005350e76661cb2bed2624b87b8d772c444bf572a" +source = "git+https://github.com/matrix-org/matrix-rust-sdk?rev=35a2ce97d84af8cea6b9e1850a8ff4ff5c66d712#35a2ce97d84af8cea6b9e1850a8ff4ff5c66d712" dependencies = [ "aes", "aquamarine", @@ -1238,8 +1236,7 @@ dependencies = [ [[package]] name = "matrix-sdk-indexeddb" version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63da8893fc9062f4b7874f766fd5dcf048f8ea456b08ba89320cc68926b1112e" +source = "git+https://github.com/matrix-org/matrix-rust-sdk?rev=35a2ce97d84af8cea6b9e1850a8ff4ff5c66d712#35a2ce97d84af8cea6b9e1850a8ff4ff5c66d712" dependencies = [ "anyhow", "async-trait", @@ -1267,8 +1264,7 @@ dependencies = [ [[package]] name = "matrix-sdk-qrcode" version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5fc0b2a90a9c986d53fb772e49ec1f135748e2b4a7d38b253ee45f97ecb818e" +source = "git+https://github.com/matrix-org/matrix-rust-sdk?rev=35a2ce97d84af8cea6b9e1850a8ff4ff5c66d712#35a2ce97d84af8cea6b9e1850a8ff4ff5c66d712" dependencies = [ "byteorder", "qrcode", @@ -1280,8 +1276,7 @@ dependencies = [ [[package]] name = "matrix-sdk-store-encryption" version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b14e1db0b31db81c5d20c20d24c84421893491da30e8e0218e991c3a350188e" +source = "git+https://github.com/matrix-org/matrix-rust-sdk?rev=35a2ce97d84af8cea6b9e1850a8ff4ff5c66d712#35a2ce97d84af8cea6b9e1850a8ff4ff5c66d712" dependencies = [ "base64", "blake3", diff --git a/Cargo.toml b/Cargo.toml index b75b77458..20c4ad0bb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -65,9 +65,9 @@ futures-util = "0.3.27" getrandom = { version = "0.3.0", features = ["wasm_js"] } http = "1.1.0" js-sys = "0.3.49" -matrix-sdk-common = { version = "0.11.0", features = ["js"] } -matrix-sdk-indexeddb = { version = "0.11.0", default-features = false, features = ["e2e-encryption"] } -matrix-sdk-qrcode = { version = "0.11.0", optional = true } +matrix-sdk-common = { git = "https://github.com/matrix-org/matrix-rust-sdk", rev = "35a2ce97d84af8cea6b9e1850a8ff4ff5c66d712", features = ["js"] } +matrix-sdk-indexeddb = { git = "https://github.com/matrix-org/matrix-rust-sdk", rev = "35a2ce97d84af8cea6b9e1850a8ff4ff5c66d712", default-features = false, features = ["e2e-encryption"] } +matrix-sdk-qrcode = { git = "https://github.com/matrix-org/matrix-rust-sdk", rev = "35a2ce97d84af8cea6b9e1850a8ff4ff5c66d712", optional = true } serde = "1.0.91" serde_json = "1.0.91" serde-wasm-bindgen = "0.6.5" @@ -83,7 +83,8 @@ wasm-bindgen-test = "0.3.37" vergen-gitcl = { version = "1.0.0", features = ["build"] } [dependencies.matrix-sdk-crypto] -version = "0.11.0" +git = "https://github.com/matrix-org/matrix-rust-sdk" +rev = "35a2ce97d84af8cea6b9e1850a8ff4ff5c66d712" default-features = false features = ["js", "automatic-room-key-forwarding"] diff --git a/index.d.ts b/index.d.ts index 24b2fd4ae..97cf8981f 100644 --- a/index.d.ts +++ b/index.d.ts @@ -69,7 +69,7 @@ declare module "./pkg/matrix_sdk_crypto_wasm.js" { changed_devices: DeviceLists, one_time_keys_counts: Map, unused_fallback_keys?: Set | null, - ): Promise; + ): Promise>; outgoingRequests(): Promise>; markRequestAsSent(request_id: string, request_type: RequestType, response: string): Promise; encryptRoomEvent(room_id: RoomId, event_type: string, content: string): Promise; diff --git a/src/machine.rs b/src/machine.rs index 7a958c581..4b55d9a21 100644 --- a/src/machine.rs +++ b/src/machine.rs @@ -42,7 +42,9 @@ use crate::{ store, store::{RoomKeyInfo, RoomKeyWithheldInfo, StoreHandle}, sync_events, - types::{self, RoomKeyImportResult, RoomSettings, SignatureVerification}, + types::{ + self, ProcessedToDeviceEvent, RoomKeyImportResult, RoomSettings, SignatureVerification, + }, verification, vodozemac, }; @@ -298,7 +300,9 @@ impl OlmMachine { /// /// # Returns /// - /// A list of JSON strings, containing the decrypted to-device events. + /// A list of {@link ProcessedToDeviceEvent} object, containing the + /// serialized processed event and if it was successfully decrypted, sent in + /// clear, unable to decrypt or invalid. #[wasm_bindgen(js_name = "receiveSyncChanges")] pub fn receive_sync_changes( &self, @@ -341,7 +345,7 @@ impl OlmMachine { // we discard the list of updated room keys in the result; JS applications are // expected to use register_room_key_updated_callback to receive updated room // keys. - let (decrypted_to_device_events, _) = me + let (processed_to_device_events, _) = me .receive_sync_changes(EncryptionSyncChanges { to_device_events, changed_devices: &changed_devices, @@ -353,7 +357,9 @@ impl OlmMachine { }) .await?; - Ok(serde_json::to_string(&decrypted_to_device_events)?) + let processed_to_device_events: Vec = + processed_to_device_events.into_iter().map(ProcessedToDeviceEvent::from).collect(); + Ok(processed_to_device_events) })) } diff --git a/src/types.rs b/src/types.rs index 9117b9fc0..416a26792 100644 --- a/src/types.rs +++ b/src/types.rs @@ -362,3 +362,70 @@ impl From<&RoomSettings> for matrix_sdk_crypto::store::RoomSettings { } } } + +/// Represent the type of {@link ProcessedToDeviceEvent}. +#[wasm_bindgen] +#[derive(Debug, Clone)] +pub enum ProcessedToDeviceEventType { + /// A successfully-decrypted encrypted event. + Decrypted, + + /// An encrypted event which could not be decrypted. + UnableToDecrypt, + + /// An unencrypted event (sent in clear). + PlainText, + + /// An invalid to device event that was ignored because it is missing some + /// required information to be processed (like no event `type` for + /// example) + Invalid, +} + +/// Represent a ToDevice event after it has been processed by {@link +/// OlmMachine#receiveSyncChanges}. +#[wasm_bindgen(getter_with_clone)] +#[derive(Debug, Clone)] +pub struct ProcessedToDeviceEvent { + /// The type of processed event + #[wasm_bindgen(getter_with_clone, js_name = "type")] + pub processed_type: ProcessedToDeviceEventType, + + /// A JSON-encoded string containing the processed event. + /// For the `Decrypted` type this will be the decrypted event as if it was + /// sent in clear (For room keys or secrets some part of the content might + /// have been zeroize'd). + #[wasm_bindgen(readonly, js_name = "wireEvent")] + pub wire_event: JsString, +} + +impl From for ProcessedToDeviceEvent { + fn from(value: matrix_sdk_crypto::types::ProcessedToDeviceEvent) -> Self { + match value { + matrix_sdk_crypto::types::ProcessedToDeviceEvent::Decrypted(decrypted_event) => { + ProcessedToDeviceEvent { + processed_type: ProcessedToDeviceEventType::Decrypted, + wire_event: decrypted_event.json().get().to_owned().into(), + } + } + matrix_sdk_crypto::types::ProcessedToDeviceEvent::UnableToDecrypt(utd) => { + ProcessedToDeviceEvent { + processed_type: ProcessedToDeviceEventType::UnableToDecrypt, + wire_event: utd.json().get().to_owned().into(), + } + } + matrix_sdk_crypto::types::ProcessedToDeviceEvent::PlainText(plain) => { + ProcessedToDeviceEvent { + processed_type: ProcessedToDeviceEventType::PlainText, + wire_event: plain.json().get().to_owned().into(), + } + } + matrix_sdk_crypto::types::ProcessedToDeviceEvent::Invalid(invalid) => { + ProcessedToDeviceEvent { + processed_type: ProcessedToDeviceEventType::Invalid, + wire_event: invalid.json().get().to_owned().into(), + } + } + } + } +} diff --git a/tests/helper.js b/tests/helper.js index 61645df3d..7bfeb8746 100644 --- a/tests/helper.js +++ b/tests/helper.js @@ -16,8 +16,11 @@ async function addMachineToMachine(machineToAdd, machine) { const oneTimeKeyCounts = new Map(); const unusedFallbackKeys = new Set(); - const receiveSyncChanges = JSON.parse( - await machineToAdd.receiveSyncChanges(toDeviceEvents, changedDevices, oneTimeKeyCounts, unusedFallbackKeys), + const receiveSyncChanges = await machineToAdd.receiveSyncChanges( + toDeviceEvents, + changedDevices, + oneTimeKeyCounts, + unusedFallbackKeys, ); expect(receiveSyncChanges).toEqual([]); diff --git a/tests/machine.test.ts b/tests/machine.test.ts index edf6a9d5c..4dc4defc4 100644 --- a/tests/machine.test.ts +++ b/tests/machine.test.ts @@ -20,6 +20,7 @@ import { MegolmDecryptionError, OlmMachine, OwnUserIdentity, + ProcessedToDeviceEventType, RequestType, RoomId, RoomKeyWithheldInfo, @@ -306,8 +307,11 @@ describe(OlmMachine.name, () => { const oneTimeKeyCounts = new Map(); const unusedFallbackKeys = new Set(); - const receiveSyncChanges = JSON.parse( - await m.receiveSyncChanges(toDeviceEvents, changedDevices, oneTimeKeyCounts, unusedFallbackKeys), + const receiveSyncChanges = await m.receiveSyncChanges( + toDeviceEvents, + changedDevices, + oneTimeKeyCounts, + unusedFallbackKeys, ); expect(receiveSyncChanges).toEqual([]); @@ -319,8 +323,11 @@ describe(OlmMachine.name, () => { const changedDevices = new DeviceLists(); const oneTimeKeyCounts = new Map(); - const receiveSyncChanges = JSON.parse( - await m.receiveSyncChanges(toDeviceEvents, changedDevices, oneTimeKeyCounts, undefined), + const receiveSyncChanges = await m.receiveSyncChanges( + toDeviceEvents, + changedDevices, + oneTimeKeyCounts, + undefined, ); expect(receiveSyncChanges).toEqual([]); @@ -333,8 +340,11 @@ describe(OlmMachine.name, () => { const oneTimeKeyCounts = new Map(); const unusedFallbackKeys = new Set(); - const receiveSyncChanges = JSON.parse( - await m.receiveSyncChanges(toDeviceEvents, changedDevices, oneTimeKeyCounts, unusedFallbackKeys), + const receiveSyncChanges = await m.receiveSyncChanges( + toDeviceEvents, + changedDevices, + oneTimeKeyCounts, + unusedFallbackKeys, ); expect(receiveSyncChanges).toEqual([]); @@ -1629,11 +1639,11 @@ describe(OlmMachine.name, () => { undefined, ); - const receivedToDevices = JSON.parse(received); - expect(receivedToDevices).toBeInstanceOf(Array); - const receivedToDeviceArray: Array = receivedToDevices; - expect(receivedToDeviceArray.length).toBe(1); - const toDeviceEvent = receivedToDeviceArray[0]; + expect(received.length).toBe(1); + const processed = received[0]; + expect(processed.type).toEqual(ProcessedToDeviceEventType.PlainText); + + let toDeviceEvent = JSON.parse(processed.wireEvent); expect(toDeviceEvent.sender).toEqual("@alice:example.com"); expect(toDeviceEvent.type).toEqual("custom.type"); @@ -1649,7 +1659,7 @@ describe(OlmMachine.name, () => { content: { "algorithm": "m.olm.v1.curve25519-aes-sha2", "ciphertext": { - aliceCurve: { + [aliceCurve]: { // this payload is just captured from a sync of some other element web with other users body: "Awogjvpx458CGhuo77HX/+tp1sxgRoCi7iAlzMvfrpbWoREQAiKACysX/p+ojr5QitCi9WRXNyamW2v2LTvoyWKtVaA2oHnYGR5s5RYhDfnIgh5MMSqqKlAbfqLvrbLovTYcKagCBbFnbA43f6zYM44buGgy8q70hMVH5WP6aK1E9Z3DVZ+8PnXQGpsrxvz2IsL6w0Nzl/qUyBEQFcgkjoDPawbsZRCllMgq2LQUyqlun6IgDTCozqsfxhDWpdfYGde4z16m34Ang7f5pH+BmPrFs6E1AO5+UbhhhS6NwWlfEtA6/9yfMxWLz1d2OrLh+QG7lYFAU9/CzIoPxaHKKr4JxgL9CjsmUPyDymWOWHP0jLi1NwpOv6hGpx0FgM7jJIMk6gWGgC5rEgEeTIwdrJh3F9OKTNSva5hvD9LomGk6tZgzQG6oap1e3wiOUyTt6S7BlyMppIu3RlIiNihZ9e17JEGiGDXOXzMJ6ISAgvGVgTP7+EvyEt2Wt4du7uBo/UvljRvVNu3I8tfItizPAOlvz460+aBDxk+sflJWt7OnhiyPnOCfopb+1RzqKVCnnPyVaP2f4BPf8qpn/f5YZk+5jJgBrGPiHzzmb3sQ5pC470s6+U3MpVFlFTG/xPBtMRMwPsbKoHfnRPqIqGu5dQ1Sw7T6taDXWjP450TvjxgHK5t2z1rLA2SXzAB1P8xbi6YXqQwxL6PvMNHn/TM0jiIQHYuqg5/RKLyhHybfP8JAjgNBw9z16wfKR/YoYFr7c+S4McQaMNa8v2SxGzhpCC3duAoK2qCWLEkYRO5cMCsGm/9bf8Q+//OykygBU/hdkT1eHUbexgALPLdfhzduutU7pbChg4T7SH7euh/3NLmS/SQvkmPfm3ckbh/Vlcj9CsXws/7MX/VJbhpbyzgBNtMnbG6tAeAofMa6Go/yMgiNBZIhLpAm31iUbUhaGm2IIlF/lsmSYEiBPoSVfFU44tetX2I/PBDGiBlzyU+yC2TOEBwMGxBE3WHbIe5/7sKW8xJF9t+HBfxIyW1QRtY3EKdEcuVOTyMxYzq3L5OKOOtPDHObYiiXg00mAgdQqgfkEAIfoRCOa2NYfTedwwo0S77eQ1sPvW5Hhf+Cm+bLibkWzaYHEZF+vyE9/Tn0tZGtH07RXfUyhp1vtTH49OBZHGkb/r+L8OjYJTST1dDCGqeGXO3uwYjoWHXtezLVHYgL+UOwcLJfMF5s9DQiqcfYXzp2kEWGsaetBFXcUWqq4RMHqlr6QfbxyuYLlQzc/AYA/MrT3J6nDpNLcvozH3RcIs8NcKcjdtjvgL0QGThy3RcecJQEDx3STrkkePL3dlyFCtVsmtQ0vjBBCxUgdySfxiobGGnpezZYi7q+Xz61GOZ9QqYmkcZOPzfNWeqtmzB7gqlH1gkFsK2yMAzKq2XCDFHvA7YAT3yMGiY06FcQ+2jyg7Bk2Q+AvjTG8hlPlmt6BZfW5cz1qx1apQn1qHXHrgfWcI52rApYQlNPOU1Uc8kZ8Ee6XUhhXBGY1rvZiKjKFG0PPuS8xo4/P7/u+gH5gItmEVDFL6giYPFsPpqAQkUN7hFoGiVZEjO4PwrLOmydsEcNOfACqrnUs08FQtvPg0sjHnxh6nh6FUQv93ukKl6+c9d+pCsN2xukrQ7Dog3nrjFZ6PrS5J0k9rDAOwTB55sfGXPZ2rATOK1WS4XcpsCtqwnYm4sGNc8ALMQkQ97zCnw8TcQwLvdUMlfbqQ5ykDQpQD68fITEDDHmBAeTCjpC713E6AhvOMwTJvjhd7hSkeOTRTmn9zXIVGNo1jSr8u0xO9uLGeWsV0+UlRLgp7/nsgfermjwNN8wj6MW3DHGS8UzzYfe9TGCeywqqIUTqgfXY48leGgB7twh4cl4jcOQniLATTvigIAQIvq/Uv8L45BGnkpKTdQ5F73gehXdVA", type: 1, @@ -1671,17 +1681,74 @@ describe(OlmMachine.name, () => { undefined, ); - const receivedToDevices = JSON.parse(received); - expect(receivedToDevices).toBeInstanceOf(Array); - const receivedToDeviceArray: Array = receivedToDevices; - expect(receivedToDeviceArray.length).toBe(1); - const toDeviceEvent = receivedToDeviceArray[0]; + expect(received.length).toBe(1); + const processed = received[0]; + expect(processed.type).toEqual(ProcessedToDeviceEventType.UnableToDecrypt); + + let toDeviceEvent = JSON.parse(processed.wireEvent); expect(toDeviceEvent.sender).toEqual("@bob:example.org"); expect(toDeviceEvent.type).toEqual("m.room.encrypted"); expect(toDeviceEvent.content.ciphertext).toBeDefined(); }); + test("Should return invalid event properly", async () => { + const m = await machine(); + + let eventWithNoType = { + sender: "@alice:example.com", + content: { + algorithm: "m.megolm.v1.aes-sha2", + code: "m.unverified", + reason: "Device not verified", + room_id: "!Cuyf34gef24t:localhost", + sender_key: "RF3s+E7RkTQTGF2d8Deol0FkQvgII2aJDf3/Jp5mxVU", + session_id: "X3lUlvLELLYxeTx4yOVu6UDpasGEVO0Jbu+QFnm0cKQ", + }, + }; + + const eventWithMalformedCurveKey = { + content: { + "algorithm": "m.olm.v1.curve25519-aes-sha2", + "ciphertext": { + INVALID_CURVE: { + // this payload is just captured from a sync of some other element web with other users + body: "Awogjvpx458CGhuo77HX/", + type: 1, + }, + }, + "org.matrix.msgid": "93ee0170aa7740d0ac9ee137e820302d", + "sender_key": "WJ6Ce7U67a6jqkHYHd8o0+5H4bqdi9hInZdk0+swuXs", + }, + type: "m.room.encrypted", + sender: "@bob:example.org", + }; + + let toDeviceEvents = [eventWithNoType, eventWithMalformedCurveKey]; + + const received = await m.receiveSyncChanges( + JSON.stringify(toDeviceEvents), + new DeviceLists(), + new Map(), + undefined, + ); + + expect(received.length).toBe(2); + const processed0 = received[0]; + expect(processed0.type).toEqual(ProcessedToDeviceEventType.Invalid); + const processed1 = received[1]; + expect(processed1.type).toEqual(ProcessedToDeviceEventType.Invalid); + + let toDeviceEvent0 = JSON.parse(processed0.wireEvent); + expect(toDeviceEvent0.sender).toEqual("@alice:example.com"); + expect(toDeviceEvent0.content).toBeDefined(); + expect(toDeviceEvent0.type).toBeUndefined(); + + let toDeviceEvent1 = JSON.parse(processed1.wireEvent); + expect(toDeviceEvent1.sender).toEqual("@bob:example.org"); + expect(toDeviceEvent1.type).toEqual("m.room.encrypted"); + }); + test("Should return the clear text version of decrypted events", async () => { const aliceUserId = new UserId("@alice:example.org"); const aliceDeviceId = new DeviceId("ALICE_DEV"); @@ -1776,12 +1843,11 @@ describe(OlmMachine.name, () => { undefined, ); - const receivedToDevices = JSON.parse(received); - expect(receivedToDevices).toBeInstanceOf(Array); - const receivedToDeviceArray: Array = receivedToDevices; - expect(receivedToDeviceArray.length).toBe(1); - const toDeviceEvent = receivedToDeviceArray[0]; + expect(received.length).toBe(1); + const processed = received[0]; + expect(processed.type).toEqual(ProcessedToDeviceEventType.Decrypted); + let toDeviceEvent = JSON.parse(processed.wireEvent); expect(toDeviceEvent.sender).toEqual("@alice:example.org"); expect(toDeviceEvent.type).toEqual("custom.type"); expect(toDeviceEvent.content.foo).toEqual("bar");