mirror of
https://github.com/chatmail/core.git
synced 2026-05-04 22:06:29 +03:00
feat: Decrypt and answer on Alice's side (sending the message seems not to work yet)
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)) => {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 {
|
||||
|
||||
11
src/token.rs
11
src/token.rs
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user