diff --git a/deltachat-jsonrpc/src/api/types/qr.rs b/deltachat-jsonrpc/src/api/types/qr.rs index 7c4207e1f..7082a7381 100644 --- a/deltachat-jsonrpc/src/api/types/qr.rs +++ b/deltachat-jsonrpc/src/api/types/qr.rs @@ -46,10 +46,6 @@ pub enum QrObject { fingerprint: String, authcode: String, - - /// The secret shared between all members, - /// used to symmetrically encrypt&decrypt messages. - shared_secret: String, }, /// Contact fingerprint is verified. /// @@ -230,7 +226,6 @@ impl From for QrObject { contact_id, fingerprint, authcode, - shared_secret, } => { let contact_id = contact_id.to_u32(); let fingerprint = fingerprint.to_string(); @@ -240,7 +235,6 @@ impl From for QrObject { contact_id, fingerprint, authcode, - shared_secret, } } Qr::FprOk { contact_id } => { diff --git a/src/qr.rs b/src/qr.rs index 0eea2da10..d6f53954c 100644 --- a/src/qr.rs +++ b/src/qr.rs @@ -20,7 +20,7 @@ use crate::message::Message; use crate::net::http::post_empty; use crate::net::proxy::{DEFAULT_SOCKS_PORT, ProxyConfig}; use crate::token; -use crate::tools::{validate_broadcast_shared_secret, validate_id}; +use crate::tools::validate_id; const OPENPGP4FPR_SCHEME: &str = "OPENPGP4FPR:"; // yes: uppercase const IDELTACHAT_SCHEME: &str = "https://i.delta.chat/#"; @@ -97,8 +97,6 @@ pub enum Qr { fingerprint: Fingerprint, authcode: String, - - shared_secret: String, }, /// Contact fingerprint is verified. @@ -398,7 +396,7 @@ pub fn format_backup(qr: &Qr) -> Result { /// scheme: `OPENPGP4FPR:FINGERPRINT#a=ADDR&n=NAME&i=INVITENUMBER&s=AUTH` /// or: `OPENPGP4FPR:FINGERPRINT#a=ADDR&g=GROUPNAME&x=GROUPID&i=INVITENUMBER&s=AUTH` -/// or: `OPENPGP4FPR:FINGERPRINT#a=ADDR&g=BROADCAST_NAME&x=BROADCAST_ID&s=AUTH&b=BROADCAST_SHARED_SECRET` +/// or: `OPENPGP4FPR:FINGERPRINT#a=ADDR&b=BROADCAST_NAME&x=BROADCAST_ID&s=AUTH` /// or: `OPENPGP4FPR:FINGERPRINT#a=ADDR` async fn decode_openpgp(context: &Context, qr: &str) -> Result { let payload = qr @@ -457,24 +455,9 @@ async fn decode_openpgp(context: &Context, qr: &str) -> Result { .get("x") .filter(|&s| validate_id(s)) .map(|s| s.to_string()); - let broadcast_shared_secret = param - .get("b") - .filter(|&s| validate_broadcast_shared_secret(s)) - .map(|s| s.to_string()); - let grpname = if grpid.is_some() { - if let Some(encoded_name) = param.get("g") { - let encoded_name = encoded_name.replace('+', "%20"); // sometimes spaces are encoded as `+` - match percent_decode_str(&encoded_name).decode_utf8() { - Ok(name) => Some(name.to_string()), - Err(err) => bail!("Invalid group name: {}", err), - } - } else { - None - } - } else { - None - }; + let grpname = decode_chat_name(¶m, &grpid, "g")?; + let broadcast_name = decode_chat_name(¶m, &grpid, "b")?; if let (Some(addr), Some(invitenumber), Some(authcode)) = (&addr, invitenumber, authcode.clone()) @@ -549,13 +532,8 @@ async fn decode_openpgp(context: &Context, qr: &str) -> Result { authcode, }) } - } else if let ( - Some(addr), - Some(broadcast_name), - Some(grpid), - Some(authcode), - Some(shared_secret), - ) = (&addr, grpname, grpid, authcode, broadcast_shared_secret) + } else if let (Some(addr), Some(broadcast_name), Some(grpid), Some(authcode)) = + (&addr, broadcast_name, grpid, authcode) { // This is a broadcast channel invite link. // TODO code duplication with the previous block @@ -577,7 +555,6 @@ async fn decode_openpgp(context: &Context, qr: &str) -> Result { contact_id, fingerprint, authcode, - shared_secret, }) } else if let Some(addr) = addr { let fingerprint = fingerprint.hex(); @@ -600,6 +577,26 @@ async fn decode_openpgp(context: &Context, qr: &str) -> Result { } } +fn decode_chat_name( + param: &BTreeMap<&str, &str>, + grpid: &Option, + key: &str, +) -> Result> { + if grpid.is_some() { + if let Some(encoded_name) = param.get(key) { + let encoded_name = encoded_name.replace('+', "%20"); // sometimes spaces are encoded as `+` + match percent_decode_str(&encoded_name).decode_utf8() { + Ok(name) => Ok(Some(name.to_string())), + Err(err) => bail!("Invalid group name: {}", err), + } + } else { + Ok(None) + } + } else { + Ok(None) + } +} + /// scheme: `https://i.delta.chat[/]#FINGERPRINT&a=ADDR[&OPTIONAL_PARAMS]` async fn decode_ideltachat(context: &Context, prefix: &str, qr: &str) -> Result { let qr = qr.replacen(prefix, OPENPGP4FPR_SCHEME, 1); diff --git a/src/receive_imf.rs b/src/receive_imf.rs index ec8c3f92a..9363f1cd0 100644 --- a/src/receive_imf.rs +++ b/src/receive_imf.rs @@ -45,7 +45,10 @@ use crate::securejoin::{self, handle_securejoin_handshake, observe_securejoin_on use crate::simplify; use crate::stock_str; use crate::sync::Sync::*; -use crate::tools::{self, buf_compress, create_broadcast_shared_secret, remove_subject_prefix}; +use crate::tools::{ + self, buf_compress, create_broadcast_shared_secret, remove_subject_prefix, + validate_broadcast_shared_secret, +}; use crate::{chatlist_events, ensure_and_debug_assert, ensure_and_debug_assert_eq, location}; use crate::{contact, imap}; @@ -3569,7 +3572,11 @@ async fn apply_in_broadcast_changes( } if let Some(secret) = mime_parser.get_header(HeaderDef::ChatBroadcastSecret) { - save_broadcast_shared_secret(context, chat.id, secret).await?; + if validate_broadcast_shared_secret(secret) { + save_broadcast_shared_secret(context, chat.id, secret).await?; + } else { + warn!(context, "Not saving invalid broadcast secret"); + } } if send_event_chat_modified { diff --git a/src/securejoin.rs b/src/securejoin.rs index 431be8e90..883ca4e74 100644 --- a/src/securejoin.rs +++ b/src/securejoin.rs @@ -109,17 +109,13 @@ pub async fn get_securejoin_qr(context: &Context, chat: Option) -> Resul let broadcast_name = chat.get_name(); let broadcast_name_urlencoded = utf8_percent_encode(broadcast_name, NON_ALPHANUMERIC).to_string(); - let broadcast_secret = load_broadcast_shared_secret(context, chat.id) - .await? - .context("Could not find broadcast secret")?; format!( - "https://i.delta.chat/#{}&a={}&g={}&x={}&s={}&b={}", + "https://i.delta.chat/#{}&a={}&b={}&x={}&s={}", fingerprint.hex(), self_addr_urlencoded, &broadcast_name_urlencoded, &chat.grpid, &auth, - broadcast_secret ) } else { // parameters used: a=g=x=i=s= diff --git a/src/securejoin/qrinvite.rs b/src/securejoin/qrinvite.rs index 20d20baaa..0010ffc81 100644 --- a/src/securejoin/qrinvite.rs +++ b/src/securejoin/qrinvite.rs @@ -35,7 +35,6 @@ pub enum QrInvite { broadcast_name: String, grpid: String, authcode: String, - shared_secret: String, }, } @@ -129,14 +128,12 @@ impl TryFrom for QrInvite { contact_id, fingerprint, authcode, - shared_secret, } => Ok(QrInvite::Broadcast { broadcast_name, grpid, contact_id, fingerprint, authcode, - shared_secret, }), _ => bail!("Unsupported QR type: {qr:?}"), } diff --git a/src/tools.rs b/src/tools.rs index b4cce6ee6..4e26eb5ee 100644 --- a/src/tools.rs +++ b/src/tools.rs @@ -334,7 +334,7 @@ pub(crate) fn validate_id(s: &str) -> bool { pub(crate) fn validate_broadcast_shared_secret(s: &str) -> bool { let alphabet = base64::alphabet::URL_SAFE.as_str(); - s.chars().all(|c| alphabet.contains(c)) && s.len() == 43 + s.chars().all(|c| alphabet.contains(c)) && s.len() >= 43 && s.len() <= 100 } /// Function generates a Message-ID that can be used for a new outgoing message.