feat: Decrypt and answer on Alice's side (sending the message seems not to work yet)

This commit is contained in:
Hocuri
2026-01-20 17:10:34 +01:00
parent 1014bf17a4
commit c91b349f6c
7 changed files with 125 additions and 106 deletions

View File

@@ -2064,6 +2064,22 @@ pub(crate) async fn set_msg_failed(
Ok(())
}
/// Inserts a tombstone into `msgs` table
/// to prevent downloading the same message in the future.
///
/// Returns tombstone database row ID.
pub(crate) async fn insert_tombstone(context: &Context, rfc724_mid: &str) -> Result<MsgId> {
let row_id = context
.sql
.insert(
"INSERT INTO msgs(rfc724_mid, chat_id) VALUES (?,?)",
(rfc724_mid, DC_CHAT_ID_TRASH),
)
.await?;
let msg_id = MsgId::new(u32::try_from(row_id)?);
Ok(msg_id)
}
/// The number of messages assigned to unblocked chats
pub async fn get_unblocked_msg_cnt(context: &Context) -> usize {
match context

View File

@@ -2367,7 +2367,7 @@ pub(crate) async fn render_symm_encrypted_securejoin_message(
// leaking information about the tokens.
let compress = false;
let encrypted = encrypt_helper
.encrypt_symmetrically(context, auth, message, compress)
.encrypt_symmetrically(context, auth, message, compress) // TODO this also signs the message. vc-request-pubkey shouldn't be signed.
.await?;
wrap_encrypted_part(encrypted)

View File

@@ -18,7 +18,6 @@ use crate::authres::handle_authres;
use crate::blob::BlobObject;
use crate::chat::ChatId;
use crate::config::Config;
use crate::constants;
use crate::contact::ContactId;
use crate::context::Context;
use crate::decrypt::{try_decrypt, validate_detached_signature};
@@ -36,6 +35,7 @@ use crate::tools::{
get_filemeta, parse_receive_headers, smeared_time, time, truncate_msg_text, validate_id,
};
use crate::{chatlist_events, location, tools};
use crate::{constants, token};
/// Public key extracted from `Autocrypt-Gossip`
/// header with associated information.
@@ -382,7 +382,7 @@ impl MimeMessage {
let mail_raw; // Memory location for a possible decrypted message.
let decrypted_msg; // Decrypted signed OpenPGP message.
let secrets: Vec<String> = context
let mut secrets: Vec<String> = context
.sql
.query_map_vec("SELECT secret FROM broadcast_secrets", (), |row| {
let secret: String = row.get(0)?;
@@ -390,6 +390,8 @@ impl MimeMessage {
})
.await?;
secrets.extend(token::lookup_all(context, token::Namespace::Auth).await?);
let (mail, is_encrypted) =
match tokio::task::block_in_place(|| try_decrypt(&mail, &private_keyring, &secrets)) {
Ok(Some(mut msg)) => {

View File

@@ -177,22 +177,6 @@ pub(crate) async fn receive_imf_from_inbox(
receive_imf_inner(context, rfc724_mid, imf_raw, seen).await
}
/// Inserts a tombstone into `msgs` table
/// to prevent downloading the same message in the future.
///
/// Returns tombstone database row ID.
async fn insert_tombstone(context: &Context, rfc724_mid: &str) -> Result<MsgId> {
let row_id = context
.sql
.insert(
"INSERT INTO msgs(rfc724_mid, chat_id) VALUES (?,?)",
(rfc724_mid, DC_CHAT_ID_TRASH),
)
.await?;
let msg_id = MsgId::new(u32::try_from(row_id)?);
Ok(msg_id)
}
async fn get_to_and_past_contact_ids(
context: &Context,
mime_parser: &MimeMessage,
@@ -477,7 +461,7 @@ pub(crate) async fn receive_imf_inner(
return Ok(None);
}
let msg_ids = vec![insert_tombstone(context, rfc724_mid).await?];
let msg_ids = vec![message::insert_tombstone(context, rfc724_mid).await?];
return Ok(Some(ReceivedMsg {
chat_id: DC_CHAT_ID_TRASH,
@@ -653,7 +637,7 @@ pub(crate) async fn receive_imf_inner(
match res {
securejoin::HandshakeMessage::Done | securejoin::HandshakeMessage::Ignore => {
let msg_id = insert_tombstone(context, rfc724_mid).await?;
let msg_id = message::insert_tombstone(context, rfc724_mid).await?;
received_msg = Some(ReceivedMsg {
chat_id: DC_CHAT_ID_TRASH,
state: MessageState::InSeen,

View File

@@ -20,14 +20,14 @@ use crate::headerdef::HeaderDef;
use crate::key::{DcKey, Fingerprint, load_self_public_key};
use crate::log::LogExt as _;
use crate::log::warn;
use crate::message::{Message, Viewtype};
use crate::message::{self, Message, MsgId, Viewtype};
use crate::mimeparser::{MimeMessage, SystemMessage};
use crate::param::Param;
use crate::qr::check_qr;
use crate::securejoin::bob::JoinerProgress;
use crate::sync::Sync::*;
use crate::tools::{create_id, time};
use crate::{SecurejoinSource, stats};
use crate::tools::{create_id, create_outgoing_rfc724_mid, time};
use crate::{SecurejoinSource, mimefactory, stats};
use crate::{SecurejoinUiPath, token};
mod bob;
@@ -346,12 +346,18 @@ pub(crate) enum HandshakeMessage {
/// Step of Secure-Join protocol.
#[derive(Debug, Display, PartialEq, Eq)]
pub(crate) enum SecureJoinStep {
/// vc-request or vg-request
/// vc-request or vg-request; only used in legacy securejoin
Request { invitenumber: String },
/// vc-auth-required or vg-auth-required
/// vc-auth-required or vg-auth-required; only used in legacy securejoin
AuthRequired,
/// vc-request-pubkey; only used in securejoin v3
RequestPubkey,
/// vc-pubkey; only used in securejoin v3
Pubkey,
/// vc-request-with-auth or vg-request-with-auth
RequestWithAuth,
@@ -381,6 +387,8 @@ pub(crate) fn get_secure_join_step(mime_message: &MimeMessage) -> Option<SecureJ
})
} else if let Some(step) = mime_message.get_header(HeaderDef::SecureJoin) {
match step {
"vc-request-pubkey" => Some(SecureJoinStep::RequestPubkey),
"vc-pubkey" => Some(SecureJoinStep::Pubkey),
"vg-auth-required" | "vc-auth-required" => Some(SecureJoinStep::AuthRequired),
"vg-request-with-auth" | "vc-request-with-auth" => {
Some(SecureJoinStep::RequestWithAuth)
@@ -438,7 +446,10 @@ pub(crate) async fn handle_securejoin_handshake(
// will improve security (completely unrelated to the securejoin protocol)
// and is something we want to do in the future:
// https://www.rfc-editor.org/rfc/rfc9580.html#name-surreptitious-forwarding
if !matches!(step, SecureJoinStep::Request { .. }) {
if !matches!(
step,
SecureJoinStep::Request { .. } | SecureJoinStep::RequestPubkey
) {
let mut self_found = false;
let self_fingerprint = load_self_public_key(context).await?.dc_fingerprint();
for (addr, key) in &mime_message.gossiped_keys {
@@ -506,6 +517,53 @@ pub(crate) async fn handle_securejoin_handshake(
========================================================*/
bob::handle_auth_required(context, mime_message).await
}
SecureJoinStep::RequestPubkey => {
/*========================================================
==== Alice - the inviter's side =====
==== Bob requests our public key (Securejoin v3) =====
========================================================*/
if !mime_message.was_encrypted() {
warn!(context, "Ignoring unencrypted RequestPubkey");
return Ok(HandshakeMessage::Ignore);
}
let Some(auth) = mime_message.get_header(HeaderDef::SecureJoinAuth) else {
warn!(
context,
"Ignoring {step} message because of missing auth code."
);
return Ok(HandshakeMessage::Ignore);
};
if !token::exists(context, token::Namespace::Auth, auth).await? {
warn!(context, "Secure-join denied (bad auth).");
return Ok(HandshakeMessage::Ignore);
}
let rfc724_mid = create_outgoing_rfc724_mid();
let addr = ContactAddress::new(&mime_message.from.addr)?;
let attach_self_pubkey = true;
let rendered_message = mimefactory::render_symm_encrypted_securejoin_message(
context,
"vc-pubkey",
&rfc724_mid,
attach_self_pubkey,
auth,
)
.await?;
let msg_id = message::insert_tombstone(context, &rfc724_mid).await?;
insert_into_smtp(context, &rfc724_mid, &addr, rendered_message, msg_id).await?;
context.scheduler.interrupt_smtp().await;
Ok(HandshakeMessage::Done)
}
SecureJoinStep::Pubkey => {
/*========================================================
==== Bob - the joiner's side =====
==== Alice sent us her pubkey (Securejoin v3) =====
========================================================*/
todo!()
}
SecureJoinStep::RequestWithAuth => {
/*==========================================================
==== Alice - the inviter side ====
@@ -665,6 +723,24 @@ pub(crate) async fn handle_securejoin_handshake(
}
}
async fn insert_into_smtp(
context: &Context,
rfc724_mid: &str,
recipient: &str,
rendered_message: String,
msg_id: MsgId,
) -> Result<(), Error> {
context
.sql
.execute(
"INSERT INTO smtp (rfc724_mid, recipients, mime, msg_id)
VALUES (?1, ?2, ?3, ?4)",
(&rfc724_mid, &recipient, &rendered_message, msg_id),
)
.await?;
Ok(())
}
/// Observe self-sent Securejoin message.
///
/// In a multi-device-setup, there may be other devices that "see" the handshake messages.
@@ -696,6 +772,8 @@ pub(crate) async fn observe_securejoin_on_other_device(
match step {
SecureJoinStep::Request { .. }
| SecureJoinStep::AuthRequired
| SecureJoinStep::RequestPubkey
| SecureJoinStep::Pubkey
| SecureJoinStep::Deprecated
| SecureJoinStep::Unknown { .. } => {
return Ok(HandshakeMessage::Ignore);

View File

@@ -11,10 +11,12 @@ use crate::context::Context;
use crate::events::EventType;
use crate::key::self_fingerprint;
use crate::log::LogExt;
use crate::message::{Message, MsgId, Viewtype};
use crate::message::{self, Message, MsgId, Viewtype};
use crate::mimeparser::{MimeMessage, SystemMessage};
use crate::param::{Param, Params};
use crate::securejoin::{ContactId, encrypted_and_signed, verify_sender_by_fingerprint};
use crate::securejoin::{
ContactId, encrypted_and_signed, insert_into_smtp, verify_sender_by_fingerprint,
};
use crate::stock_str;
use crate::sync::Sync::*;
use crate::tools::{create_outgoing_rfc724_mid, smeared_time, time};
@@ -302,95 +304,21 @@ pub(crate) async fn send_handshake_message(
if invite.is_v3() && matches!(step, BobHandshakeMsg::Request) {
// Send a minimal symmetrically-encrypted vc-request message
// TODO: Either add a message to the database, or make sure that smtp.rs gets along with a 0 or NULL msg_id
/*
msg.state = MessageState::OutPending;
msg.timestamp_sort = create_smeared_timestamp(context);
msg.rfc724_mid = create_outgoing_rfc724_mid();
let is_bot = context.get_config_bool(Config::Bot).await?;
msg.param
.set_optional(Param::Bot, Some("1").filter(|_| is_bot));
let raw_id = context
.sql
.insert(
"INSERT INTO msgs (
rfc724_mid,
chat_id,
from_id,
to_id,
timestamp,
type,
state,
txt,
txt_normalized,
subject,
param,
hidden,
mime_in_reply_to,
mime_references,
mime_modified,
mime_headers,
mime_compressed,
location_id,
ephemeral_timer,
ephemeral_timestamp)
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,1,?,?,?);",
params_slice![
msg.rfc724_mid,
msg.chat_id,
msg.from_id,
to_id,
msg.timestamp_sort,
msg.viewtype,
msg.state,
msg_text,
normalize_text(&msg_text),
&msg.subject,
msg.param.to_string(),
msg.hidden,
msg.in_reply_to.as_deref().unwrap_or_default(),
new_references,
new_mime_headers.is_some(),
new_mime_headers.unwrap_or_default(),
location_id as i32,
ephemeral_timer,
ephemeral_timestamp
],
)
.await?;
context.new_msgs_notify.notify_one();
msg.id = MsgId::new(u32::try_from(raw_id)?);
*/
let rfc724_mid = create_outgoing_rfc724_mid();
let contact = Contact::get_by_id(context, invite.contact_id()).await?;
let recipient = contact.get_addr();
let attach_self_pubkey = false;
let rendered_message = mimefactory::render_symm_encrypted_securejoin_message(
context,
step.securejoin_header(invite),
"vc-request-pubkey",
&rfc724_mid,
attach_self_pubkey,
invite.authcode(),
)
.await?;
// TODO code duplication
context
.sql
.execute(
"INSERT INTO smtp (rfc724_mid, recipients, mime, msg_id)
VALUES (?1, ?2, ?3, ?4)",
(
&rfc724_mid,
&recipient,
&rendered_message,
0, // TODO
),
)
.await?;
let msg_id = message::insert_tombstone(context, &rfc724_mid).await?;
insert_into_smtp(context, &rfc724_mid, recipient, rendered_message, msg_id).await?;
context.scheduler.interrupt_smtp().await;
} else {
let mut msg = Message {

View File

@@ -62,6 +62,17 @@ pub async fn lookup(
.await
}
pub async fn lookup_all(context: &Context, namespace: Namespace) -> Result<Vec<String>> {
context
.sql
.query_map_vec(
"SELECT token FROM tokens WHERE namespc=? ORDER BY timestamp DESC LIMIT 1",
(namespace,),
|row| Ok(row.get(0)?),
)
.await
}
pub async fn lookup_or_new(
context: &Context,
namespace: Namespace,