diff --git a/src/chat.rs b/src/chat.rs index cd5eca38f..2e4aeab45 100644 --- a/src/chat.rs +++ b/src/chat.rs @@ -43,9 +43,9 @@ use crate::smtp::send_msg_to_smtp; use crate::stock_str; use crate::sync::{self, Sync::*, SyncData}; use crate::tools::{ - IsNoneOrEmpty, SystemTime, buf_compress, create_id, create_outgoing_rfc724_mid, - create_smeared_timestamp, create_smeared_timestamps, get_abs_path, gm2local_offset, - smeared_time, time, truncate_msg_text, + IsNoneOrEmpty, SystemTime, buf_compress, create_broadcast_shared_secret, create_id, + create_outgoing_rfc724_mid, create_smeared_timestamp, create_smeared_timestamps, get_abs_path, + gm2local_offset, smeared_time, time, truncate_msg_text, }; use crate::webxdc::StatusUpdateSerial; use crate::{chatlist_events, imap}; @@ -3794,7 +3794,7 @@ pub async fn create_group_ex( /// Returns the created chat's id. pub async fn create_broadcast(context: &Context, chat_name: String) -> Result { let grpid = create_id(); - let secret = create_id(); + let secret = create_broadcast_shared_secret(); create_broadcast_ex(context, Sync, grpid, chat_name, secret).await } diff --git a/src/qr.rs b/src/qr.rs index c767503f3..0eea2da10 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_id; +use crate::tools::{validate_broadcast_shared_secret, validate_id}; const OPENPGP4FPR_SCHEME: &str = "OPENPGP4FPR:"; // yes: uppercase const IDELTACHAT_SCHEME: &str = "https://i.delta.chat/#"; @@ -459,7 +459,7 @@ async fn decode_openpgp(context: &Context, qr: &str) -> Result { .map(|s| s.to_string()); let broadcast_shared_secret = param .get("b") - .filter(|&s| validate_id(s)) + .filter(|&s| validate_broadcast_shared_secret(s)) .map(|s| s.to_string()); let grpname = if grpid.is_some() { diff --git a/src/receive_imf.rs b/src/receive_imf.rs index 3ca94a422..29e85527c 100644 --- a/src/receive_imf.rs +++ b/src/receive_imf.rs @@ -44,7 +44,7 @@ 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_id, remove_subject_prefix}; +use crate::tools::{self, buf_compress, create_broadcast_shared_secret, remove_subject_prefix}; use crate::{chatlist_events, ensure_and_debug_assert, ensure_and_debug_assert_eq, location}; use crate::{contact, imap}; @@ -1566,7 +1566,7 @@ async fn do_chat_assignment( } else { let name = compute_mailinglist_name(mailinglist_header, &listid, mime_parser); - let secret = create_id(); + let secret = create_broadcast_shared_secret(); chat::create_broadcast_ex(context, Nosync, listid, name, secret).await? }, ); diff --git a/src/tools.rs b/src/tools.rs index 59cca8d15..0acd7cf61 100644 --- a/src/tools.rs +++ b/src/tools.rs @@ -300,6 +300,25 @@ pub(crate) fn create_id() -> String { base64::engine::general_purpose::URL_SAFE.encode(arr) } +/// Generate a shared secret for a broadcast channel, consisting of 43 characters. +/// +/// The string generated by this function has 258 bits of entropy +/// and is returned as 43 Base64 characters, each containing 6 bits of entropy. +/// 256 is chosen because we may switch to AES-256 keys in the future, +/// and so that the shared secret definitely won't be the weak spot. +pub(crate) fn create_broadcast_shared_secret() -> String { + // ThreadRng implements CryptoRng trait and is supposed to be cryptographically secure. + let mut rng = thread_rng(); + + // Generate 264 random bits. + let mut arr = [0u8; 33]; + rng.fill(&mut arr[..]); + + let mut res = base64::engine::general_purpose::URL_SAFE.encode(arr); + res.truncate(43); + res +} + /// Returns true if given string is a valid ID. /// /// All IDs generated with `create_id()` should be considered valid. @@ -308,6 +327,11 @@ pub(crate) fn validate_id(s: &str) -> bool { s.chars().all(|c| alphabet.contains(c)) && s.len() > 10 && s.len() <= 32 } +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 +} + /// Function generates a Message-ID that can be used for a new outgoing message. /// - this function is called for all outgoing messages. /// - the message ID should be globally unique