From 8a0c913bbd484d07fd6f6740175f878fe4facb3e Mon Sep 17 00:00:00 2001 From: bjoern Date: Thu, 21 Nov 2024 19:00:29 +0100 Subject: [PATCH] feat: use privacy-preserving webxdc addresses (#6237) this PR adds the address to be used by the UI for `window.webxdc.selfAddr` to webxdc-info. UIs need to be changed accordingly and must not use configured_addr any longer. the address is created by sha256(private-key + rfc724_mid) , which results in different addresses for each webxdc, without the option to find out the real address of the user. this also returns the same address for a multi-device-setup - sending totally random self address around might be an alternative, however would require connectivity (both devices may be offline on first start). for existing app, after the change, there will be a new user, resulting eg. in a new highscore, otherwise, things should be mostly fine. this assumption is also important as we might change the thing another time when it comes to multi-transport. ftr, addresses look like `0f187e3f420748b03e3da76543e9a84ecff822687ce7e94f250c04c7c50398bc` now when this is merged, we need to adapt #6230 and file issues for all UI to use `info.selfAddr` closes #6216 --- Cargo.lock | 1 + Cargo.toml | 1 + deltachat-ffi/deltachat.h | 1 + deltachat-jsonrpc/src/api/types/webxdc.rs | 4 +++ deltachat-rpc-client/tests/test_webxdc.py | 1 + src/summary.rs | 4 +-- src/webxdc.rs | 32 +++++++++++++++++++++++ 7 files changed, 42 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 669e23cdb..33b7c4b79 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1372,6 +1372,7 @@ dependencies = [ "serde_json", "serde_urlencoded", "sha-1", + "sha2", "shadowsocks", "smallvec", "strum", diff --git a/Cargo.toml b/Cargo.toml index 03cac0c81..abfd53801 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -92,6 +92,7 @@ serde_json = { workspace = true } serde_urlencoded = "0.7.1" serde = { workspace = true, features = ["derive"] } sha-1 = "0.10" +sha2 = "0.10" shadowsocks = { version = "1.21.0", default-features = false, features = ["aead-cipher-2022"] } smallvec = "1.13.2" strum = "0.26" diff --git a/deltachat-ffi/deltachat.h b/deltachat-ffi/deltachat.h index 36bf24607..55235216b 100644 --- a/deltachat-ffi/deltachat.h +++ b/deltachat-ffi/deltachat.h @@ -4197,6 +4197,7 @@ char* dc_msg_get_webxdc_blob (const dc_msg_t* msg, const char* * true if the Webxdc should get full internet access, including Webrtc. * currently, this is only true for encrypted Webxdc's in the self chat * that have requested internet access in the manifest. + * - self_addr: address to be used for `window.webxdc.selfAddr` in JS land. * * @memberof dc_msg_t * @param msg The webxdc instance. diff --git a/deltachat-jsonrpc/src/api/types/webxdc.rs b/deltachat-jsonrpc/src/api/types/webxdc.rs index 71db9ed93..28f0f384b 100644 --- a/deltachat-jsonrpc/src/api/types/webxdc.rs +++ b/deltachat-jsonrpc/src/api/types/webxdc.rs @@ -35,6 +35,8 @@ pub struct WebxdcMessageInfo { source_code_url: Option, /// True if full internet access should be granted to the app. internet_access: bool, + /// Address to be used for `window.webxdc.selfAddr` in JS land. + self_addr: String, } impl WebxdcMessageInfo { @@ -50,6 +52,7 @@ impl WebxdcMessageInfo { summary, source_code_url, internet_access, + self_addr, } = message.get_webxdc_info(context).await?; Ok(Self { @@ -59,6 +62,7 @@ impl WebxdcMessageInfo { summary: maybe_empty_string_to_option(summary), source_code_url: maybe_empty_string_to_option(source_code_url), internet_access, + self_addr, }) } } diff --git a/deltachat-rpc-client/tests/test_webxdc.py b/deltachat-rpc-client/tests/test_webxdc.py index 3c5715731..e189251df 100644 --- a/deltachat-rpc-client/tests/test_webxdc.py +++ b/deltachat-rpc-client/tests/test_webxdc.py @@ -24,6 +24,7 @@ def test_webxdc(acfactory) -> None: "name": "Chess Board", "sourceCodeUrl": None, "summary": None, + "selfAddr": webxdc_info["selfAddr"], } status_updates = message.get_webxdc_status_updates() diff --git a/src/summary.rs b/src/summary.rs index 4881b0a67..72bf3395d 100644 --- a/src/summary.rs +++ b/src/summary.rs @@ -289,7 +289,7 @@ mod tests { use super::*; use crate::chat::ChatId; use crate::param::Param; - use crate::test_utils as test; + use crate::test_utils::TestContext; async fn assert_summary_texts(msg: &Message, ctx: &Context, expected: &str) { assert_eq!(msg.get_summary_text(ctx).await, expected); @@ -298,7 +298,7 @@ mod tests { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_get_summary_text() { - let d = test::TestContext::new().await; + let d = TestContext::new_alice().await; let ctx = &d.ctx; let chat_id = ChatId::create_for_contact(ctx, ContactId::SELF) .await diff --git a/src/webxdc.rs b/src/webxdc.rs index 8dacd778c..c19bbebd3 100644 --- a/src/webxdc.rs +++ b/src/webxdc.rs @@ -30,12 +30,14 @@ use lettre_email::PartBuilder; use rusqlite::OptionalExtension; use serde::{Deserialize, Serialize}; use serde_json::Value; +use sha2::{Digest, Sha256}; use crate::chat::{self, Chat}; use crate::constants::Chattype; use crate::contact::ContactId; use crate::context::Context; use crate::events::EventType; +use crate::key::{load_self_public_key, DcKey}; use crate::message::{Message, MessageState, MsgId, Viewtype}; use crate::mimefactory::wrapped_base64_encode; use crate::mimeparser::SystemMessage; @@ -97,6 +99,9 @@ pub struct WebxdcInfo { /// It should request access, be encrypted /// and sent to self for this. pub internet_access: bool, + + /// Address to be used for `window.webxdc.selfAddr` in JS land. + pub self_addr: String, } /// Status Update ID. @@ -872,6 +877,8 @@ impl Message { && self.chat_id.is_self_talk(context).await.unwrap_or_default() && self.get_showpadlock(); + let self_addr = self.get_webxdc_self_addr(context).await?; + Ok(WebxdcInfo { name: if let Some(name) = manifest.name { name @@ -904,8 +911,16 @@ impl Message { "".to_string() }, internet_access, + self_addr, }) } + + async fn get_webxdc_self_addr(&self, context: &Context) -> Result { + let fingerprint = load_self_public_key(context).await?.dc_fingerprint().hex(); + let data = format!("{}-{}", fingerprint, self.rfc724_mid); + let hash = Sha256::digest(data.as_bytes()); + Ok(format!("{:x}", hash)) + } } #[cfg(test)] @@ -2288,6 +2303,23 @@ sth_for_the = "future""# Ok(()) } + #[tokio::test(flavor = "multi_thread", worker_threads = 2)] + async fn test_get_webxdc_self_addr() -> Result<()> { + let t = TestContext::new_alice().await; + let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo").await?; + + let instance = send_webxdc_instance(&t, chat_id).await?; + let info1 = instance.get_webxdc_info(&t).await?; + let instance = send_webxdc_instance(&t, chat_id).await?; + let info2 = instance.get_webxdc_info(&t).await?; + + let real_addr = t.get_primary_self_addr().await?; + assert!(!info1.self_addr.contains(&real_addr)); + assert_ne!(info1.self_addr, info2.self_addr); + + Ok(()) + } + #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_webxdc_info_summary() -> Result<()> { let alice = TestContext::new_alice().await;