From 2e09bf63a60e2edaf7223bc7bf410b6dbfe1a4ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Fri, 19 May 2023 13:07:19 +0200 Subject: [PATCH] Add a message id to our encrypted to-device events --- Cargo.lock | 10 ++++++ bindings/matrix-sdk-crypto-ffi/Cargo.toml | 1 + bindings/matrix-sdk-crypto-js/Cargo.toml | 7 ++++- bindings/matrix-sdk-crypto-nodejs/Cargo.toml | 7 ++++- crates/matrix-sdk-base/Cargo.toml | 1 + crates/matrix-sdk-crypto/CHANGELOG.md | 3 ++ crates/matrix-sdk-crypto/Cargo.toml | 4 ++- .../src/identities/device.rs | 22 +++++++++++-- crates/matrix-sdk-crypto/src/olm/account.rs | 1 + crates/matrix-sdk-crypto/src/olm/session.rs | 5 ++- .../src/types/events/room/encrypted.rs | 31 ++++++++++++++++--- crates/matrix-sdk/Cargo.toml | 1 + 12 files changed, 83 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0055789c9..2719d6d78 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2713,6 +2713,7 @@ dependencies = [ "tokio-stream", "tracing", "tracing-subscriber", + "ulid", "vodozemac", "zeroize", ] @@ -5440,6 +5441,15 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" +[[package]] +name = "ulid" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13a3aaa69b04e5b66cc27309710a569ea23593612387d67daaf102e73aa974fd" +dependencies = [ + "rand 0.8.5", +] + [[package]] name = "unarray" version = "0.1.4" diff --git a/bindings/matrix-sdk-crypto-ffi/Cargo.toml b/bindings/matrix-sdk-crypto-ffi/Cargo.toml index 7d64c8c69..e11aae441 100644 --- a/bindings/matrix-sdk-crypto-ffi/Cargo.toml +++ b/bindings/matrix-sdk-crypto-ffi/Cargo.toml @@ -46,6 +46,7 @@ version = "0.6.0" [dependencies.matrix-sdk-crypto] path = "../../crates/matrix-sdk-crypto" version = "0.6.0" +default_features = false features = ["qrcode", "backups_v1", "automatic-room-key-forwarding"] [dependencies.matrix-sdk-sqlite] diff --git a/bindings/matrix-sdk-crypto-js/Cargo.toml b/bindings/matrix-sdk-crypto-js/Cargo.toml index dffd30308..667472dcd 100644 --- a/bindings/matrix-sdk-crypto-js/Cargo.toml +++ b/bindings/matrix-sdk-crypto-js/Cargo.toml @@ -41,7 +41,6 @@ futures-util = "0.3.27" http = { workspace = true } js-sys = "0.3.49" matrix-sdk-common = { version = "0.6.0", path = "../../crates/matrix-sdk-common", features = ["js"] } -matrix-sdk-crypto = { version = "0.6.0", path = "../../crates/matrix-sdk-crypto", features = ["js", "automatic-room-key-forwarding"] } matrix-sdk-indexeddb = { version = "0.2.0", path = "../../crates/matrix-sdk-indexeddb" } matrix-sdk-qrcode = { version = "0.4.0", path = "../../crates/matrix-sdk-qrcode", optional = true } ruma = { workspace = true, features = ["js", "rand", "unstable-msc2677"] } @@ -52,3 +51,9 @@ vodozemac = { workspace = true, features = ["js"] } wasm-bindgen = "0.2.83" wasm-bindgen-futures = "0.4.33" zeroize = { workspace = true } + +[dependencies.matrix-sdk-crypto] +path = "../../crates/matrix-sdk-crypto" +version = "0.6.0" +default_features = false +features = ["js", "automatic-room-key-forwarding"] diff --git a/bindings/matrix-sdk-crypto-nodejs/Cargo.toml b/bindings/matrix-sdk-crypto-nodejs/Cargo.toml index c22e42205..c18a9f478 100644 --- a/bindings/matrix-sdk-crypto-nodejs/Cargo.toml +++ b/bindings/matrix-sdk-crypto-nodejs/Cargo.toml @@ -24,7 +24,6 @@ qrcode = ["matrix-sdk-crypto/qrcode"] tracing = ["dep:tracing-subscriber"] [dependencies] -matrix-sdk-crypto = { version = "0.6.0", path = "../../crates/matrix-sdk-crypto", features = ["js", "automatic-room-key-forwarding"] } matrix-sdk-common = { version = "0.6.0", path = "../../crates/matrix-sdk-common", features = ["js"] } matrix-sdk-sled = { version = "0.2.0", path = "../../crates/matrix-sdk-sled", default-features = false, features = ["crypto-store"] } matrix-sdk-sqlite = { version = "0.1.0", path = "../../crates/matrix-sdk-sqlite", features = ["crypto-store"] } @@ -37,5 +36,11 @@ tracing-subscriber = { version = "0.3", default-features = false, features = ["t vodozemac = { workspace = true, features = ["js"] } zeroize = { workspace = true } +[dependencies.matrix-sdk-crypto] +path = "../../crates/matrix-sdk-crypto" +version = "0.6.0" +default_features = false +features = ["js", "automatic-room-key-forwarding"] + [build-dependencies] napi-build = "2.0.0" diff --git a/crates/matrix-sdk-base/Cargo.toml b/crates/matrix-sdk-base/Cargo.toml index 12c569178..1696553ba 100644 --- a/crates/matrix-sdk-base/Cargo.toml +++ b/crates/matrix-sdk-base/Cargo.toml @@ -21,6 +21,7 @@ e2e-encryption = ["dep:matrix-sdk-crypto"] js = ["matrix-sdk-common/js", "matrix-sdk-crypto?/js", "ruma/js", "matrix-sdk-store-encryption/js"] qrcode = ["matrix-sdk-crypto?/qrcode"] automatic-room-key-forwarding = ["matrix-sdk-crypto?/automatic-room-key-forwarding"] +message-ids = ["matrix-sdk-crypto?/message-ids"] experimental-sliding-sync = ["ruma/unstable-msc3575"] # helpers for testing features build upon this diff --git a/crates/matrix-sdk-crypto/CHANGELOG.md b/crates/matrix-sdk-crypto/CHANGELOG.md index a8c7108df..5ad0c9ab8 100644 --- a/crates/matrix-sdk-crypto/CHANGELOG.md +++ b/crates/matrix-sdk-crypto/CHANGELOG.md @@ -1,5 +1,8 @@ # v0.7.0 +- Add a new optional `message-ids` feature which adds a unique ID to the content + of `m.room.encrypted` event contents which get sent out. + - Disable the automatic-key-forwarding feature by default. - Add a new variant to the `VerificationRequestState` enum called diff --git a/crates/matrix-sdk-crypto/Cargo.toml b/crates/matrix-sdk-crypto/Cargo.toml index f0cbd29b2..e2dff5826 100644 --- a/crates/matrix-sdk-crypto/Cargo.toml +++ b/crates/matrix-sdk-crypto/Cargo.toml @@ -20,6 +20,7 @@ automatic-room-key-forwarding = [] js = ["ruma/js", "vodozemac/js"] qrcode = ["dep:matrix-sdk-qrcode"] backups_v1 = ["dep:bs58", "dep:cbc", "dep:hkdf"] +message-ids = ["dep:ulid"] experimental-algorithms = [] # Testing helpers for implementations based upon this @@ -48,15 +49,16 @@ matrix-sdk-qrcode = { version = "0.4.0", path = "../matrix-sdk-qrcode", optional matrix-sdk-common = { version = "0.6.0", path = "../matrix-sdk-common" } pbkdf2 = { version = "0.11.0", default-features = false } rand = "0.8.5" +rmp-serde = "1.1.1" ruma = { workspace = true, features = ["rand", "canonical-json", "unstable-msc2677"] } serde = { workspace = true, features = ["derive", "rc"] } -rmp-serde = "1.1.1" serde_json = { workspace = true } sha2 = "0.10.2" tokio-stream = { version = "0.1.12", features = ["sync"] } tokio = { workspace = true, default-features = false, features = ["sync"] } thiserror = { workspace = true } tracing = { workspace = true, features = ["attributes"] } +ulid = { version = "1.0.0", optional = true } vodozemac = { workspace = true } zeroize = { workspace = true, features = ["zeroize_derive"] } diff --git a/crates/matrix-sdk-crypto/src/identities/device.rs b/crates/matrix-sdk-crypto/src/identities/device.rs index b86068510..a6aca5de5 100644 --- a/crates/matrix-sdk-crypto/src/identities/device.rs +++ b/crates/matrix-sdk-crypto/src/identities/device.rs @@ -440,6 +440,7 @@ impl Device { recipient_key = ?self.curve25519_key(), event_type, session, + message_id, )) ] pub(crate) async fn encrypt( @@ -447,7 +448,23 @@ impl Device { event_type: &str, content: Value, ) -> OlmResult<(Session, Raw)> { - self.inner.encrypt(self.verification_machine.store.inner(), event_type, content).await + #[cfg(feature = "message-ids")] + let message_id = { + #[cfg(not(target_arch = "wasm32"))] + let id = ulid::Ulid::new().to_string(); + #[cfg(target_arch = "wasm32")] + let id = ruma::TransactionId::new().to_string(); + + tracing::Span::current().record("message_id", &id); + Some(id) + }; + + #[cfg(not(feature = "message-ids"))] + let message_id = None; + + self.inner + .encrypt(self.verification_machine.store.inner(), event_type, content, message_id) + .await } pub(crate) async fn maybe_encrypt_room_key( @@ -779,11 +796,12 @@ impl ReadOnlyDevice { store: &DynCryptoStore, event_type: &str, content: Value, + message_id: Option, ) -> OlmResult<(Session, Raw)> { let session = self.get_most_recent_session(store).await?; if let Some(mut session) = session { - let message = session.encrypt(self, event_type, content).await?; + let message = session.encrypt(self, event_type, content, message_id).await?; trace!("Successfully encrypted an event"); diff --git a/crates/matrix-sdk-crypto/src/olm/account.rs b/crates/matrix-sdk-crypto/src/olm/account.rs index 2e0ec2bd8..62f34b34d 100644 --- a/crates/matrix-sdk-crypto/src/olm/account.rs +++ b/crates/matrix-sdk-crypto/src/olm/account.rs @@ -1204,6 +1204,7 @@ impl ReadOnlyAccount { &device, "m.dummy", serde_json::to_value(ToDeviceDummyEventContent::new()).unwrap(), + None, ) .await .unwrap() diff --git a/crates/matrix-sdk-crypto/src/olm/session.rs b/crates/matrix-sdk-crypto/src/olm/session.rs index d1004a5e2..5663d7126 100644 --- a/crates/matrix-sdk-crypto/src/olm/session.rs +++ b/crates/matrix-sdk-crypto/src/olm/session.rs @@ -14,7 +14,7 @@ use std::{fmt, sync::Arc}; -use ruma::{serde::Raw, OwnedDeviceId, OwnedUserId, SecondsSinceUnixEpoch}; +use ruma::{serde::Raw, JsOption, OwnedDeviceId, OwnedUserId, SecondsSinceUnixEpoch}; use serde::{Deserialize, Serialize}; use serde_json::{json, Value}; use tokio::sync::Mutex; @@ -151,6 +151,7 @@ impl Session { recipient_device: &ReadOnlyDevice, event_type: &str, content: Value, + message_id: Option, ) -> OlmResult> { let plaintext = { let recipient_signing_key = @@ -180,12 +181,14 @@ impl Session { ciphertext, recipient_key: self.sender_key, sender_key: self.our_identity_keys.curve25519, + message_id: JsOption::from_implicit_option(message_id), } .into(), #[cfg(feature = "experimental-algorithms")] EventEncryptionAlgorithm::OlmV2Curve25519AesSha2 => OlmV2Curve25519AesSha2Content { ciphertext, sender_key: self.our_identity_keys.curve25519, + message_id: JsOption::from_implicit_option(message_id), } .into(), _ => unreachable!(), diff --git a/crates/matrix-sdk-crypto/src/types/events/room/encrypted.rs b/crates/matrix-sdk-crypto/src/types/events/room/encrypted.rs index 5f8c5fe86..ee1bdd1aa 100644 --- a/crates/matrix-sdk-crypto/src/types/events/room/encrypted.rs +++ b/crates/matrix-sdk-crypto/src/types/events/room/encrypted.rs @@ -16,7 +16,7 @@ use std::collections::BTreeMap; -use ruma::{OwnedDeviceId, RoomId}; +use ruma::{JsOption, OwnedDeviceId, RoomId}; use serde::{Deserialize, Serialize}; use serde_json::Value; use vodozemac::{megolm::MegolmMessage, olm::OlmMessage, Curve25519PublicKey}; @@ -124,6 +124,9 @@ pub struct OlmV1Curve25519AesSha2Content { /// The Curve25519 key of the sender. pub sender_key: Curve25519PublicKey, + + /// The unique ID of this content. + pub message_id: JsOption, } /// The event content for events encrypted with the m.olm.v2.curve25519-aes-sha2 @@ -137,6 +140,10 @@ pub struct OlmV2Curve25519AesSha2Content { /// The Curve25519 key of the sender. #[serde(deserialize_with = "deserialize_curve_key", serialize_with = "serialize_curve_key")] pub sender_key: Curve25519PublicKey, + + /// The unique ID of this content. + #[serde(default, skip_serializing_if = "JsOption::is_undefined", rename = "org.matrix.msgid")] + pub message_id: JsOption, } #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] @@ -144,6 +151,8 @@ struct OlmHelper { #[serde(deserialize_with = "deserialize_curve_key", serialize_with = "serialize_curve_key")] sender_key: Curve25519PublicKey, ciphertext: BTreeMap, + #[serde(default, skip_serializing_if = "JsOption::is_undefined", rename = "org.matrix.msgid")] + message_id: JsOption, } impl Serialize for OlmV1Curve25519AesSha2Content { @@ -154,7 +163,12 @@ impl Serialize for OlmV1Curve25519AesSha2Content { let ciphertext = BTreeMap::from([(self.recipient_key.to_base64(), self.ciphertext.clone())]); - OlmHelper { sender_key: self.sender_key, ciphertext }.serialize(serializer) + OlmHelper { + sender_key: self.sender_key, + ciphertext, + message_id: self.message_id.to_owned(), + } + .serialize(serializer) } } @@ -171,7 +185,12 @@ impl TryFrom for OlmV1Curve25519AesSha2Content { let recipient_key = Curve25519PublicKey::from_base64(&recipient_key).map_err(serde::de::Error::custom)?; - Ok(Self { ciphertext, recipient_key, sender_key: value.sender_key }) + Ok(Self { + ciphertext, + recipient_key, + sender_key: value.sender_key, + message_id: value.message_id, + }) } } @@ -479,7 +498,11 @@ pub(crate) mod test { let json = to_device_json(); let event: EncryptedToDeviceEvent = serde_json::from_value(json.clone())?; - assert_matches!(event.content, ToDeviceEncryptedEventContent::OlmV1Curve25519AesSha2(_)); + let content = assert_matches!( + &event.content, + ToDeviceEncryptedEventContent::OlmV1Curve25519AesSha2(c) => c.to_owned() + ); + assert!(content.message_id.is_undefined()); let serialized = serde_json::to_value(event)?; assert_eq!(json, serialized); diff --git a/crates/matrix-sdk/Cargo.toml b/crates/matrix-sdk/Cargo.toml index b42b8e464..8d71e3e2a 100644 --- a/crates/matrix-sdk/Cargo.toml +++ b/crates/matrix-sdk/Cargo.toml @@ -26,6 +26,7 @@ testing = [] e2e-encryption = [ "matrix-sdk-base/e2e-encryption", + "matrix-sdk-base/message-ids", "matrix-sdk-sqlite?/crypto-store", # activate crypto-store on sqlite if given "matrix-sdk-indexeddb?/e2e-encryption", # activate on indexeddb if given ]