Compare commits

..

1 Commits

Author SHA1 Message Date
Hocuri
298814d6db feat: Remove detached signature validation 2026-04-30 17:10:34 +02:00
8 changed files with 46 additions and 201 deletions

64
Cargo.lock generated
View File

@@ -36,7 +36,7 @@ checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0"
dependencies = [
"cfg-if",
"cipher",
"cpufeatures 0.2.17",
"cpufeatures",
]
[[package]]
@@ -136,7 +136,7 @@ checksum = "3c3610892ee6e0cbce8ae2700349fcf8f98adb0dbfbee85aec3c9179d29cc072"
dependencies = [
"base64ct",
"blake2",
"cpufeatures 0.2.17",
"cpufeatures",
"password-hash",
"zeroize",
]
@@ -497,16 +497,16 @@ dependencies = [
[[package]]
name = "blake3"
version = "1.8.5"
version = "1.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0aa83c34e62843d924f905e0f5c866eb1dd6545fc4d719e803d9ba6030371fce"
checksum = "2468ef7d57b3fb7e16b576e8377cdbde2320c60e1491e961d11da40fc4f02a2d"
dependencies = [
"arrayref",
"arrayvec",
"cc",
"cfg-if",
"constant_time_eq 0.4.2",
"cpufeatures 0.3.0",
"cpufeatures",
]
[[package]]
@@ -799,7 +799,7 @@ checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818"
dependencies = [
"cfg-if",
"cipher",
"cpufeatures 0.2.17",
"cpufeatures",
]
[[package]]
@@ -1011,15 +1011,6 @@ dependencies = [
"libc",
]
[[package]]
name = "cpufeatures"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201"
dependencies = [
"libc",
]
[[package]]
name = "crc"
version = "3.2.1"
@@ -1225,7 +1216,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be"
dependencies = [
"cfg-if",
"cpufeatures 0.2.17",
"cpufeatures",
"curve25519-dalek-derive",
"digest",
"fiat-crypto",
@@ -2610,9 +2601,9 @@ dependencies = [
[[package]]
name = "hyper"
version = "1.9.0"
version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6299f016b246a94207e63da54dbe807655bf9e00044f73ded42c3ac5305fbcca"
checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11"
dependencies = [
"atomic-waker",
"bytes",
@@ -2625,6 +2616,7 @@ dependencies = [
"httpdate",
"itoa",
"pin-project-lite",
"pin-utils",
"smallvec",
"tokio",
"want",
@@ -2662,7 +2654,7 @@ dependencies = [
"hyper",
"libc",
"pin-project-lite",
"socket2 0.5.9",
"socket2 0.6.0",
"tokio",
"tower-service",
"tracing",
@@ -3254,7 +3246,7 @@ version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb26cec98cce3a3d96cbb7bced3c4b16e3d13f27ec56dbd62cbc8f39cfb9d653"
dependencies = [
"cpufeatures 0.2.17",
"cpufeatures",
]
[[package]]
@@ -3461,13 +3453,13 @@ dependencies = [
[[package]]
name = "mio"
version = "1.2.0"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1"
checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd"
dependencies = [
"libc",
"wasi 0.11.0+wasi-snapshot-preview1",
"windows-sys 0.61.1",
"windows-sys 0.52.0",
]
[[package]]
@@ -4420,7 +4412,7 @@ version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf"
dependencies = [
"cpufeatures 0.2.17",
"cpufeatures",
"opaque-debug",
"universal-hash",
]
@@ -4432,7 +4424,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25"
dependencies = [
"cfg-if",
"cpufeatures 0.2.17",
"cpufeatures",
"opaque-debug",
"universal-hash",
]
@@ -5516,7 +5508,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f5058ada175748e33390e40e872bd0fe59a19f265d0158daa551c5a88a76009c"
dependencies = [
"cfg-if",
"cpufeatures 0.2.17",
"cpufeatures",
"digest",
]
@@ -5527,7 +5519,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba"
dependencies = [
"cfg-if",
"cpufeatures 0.2.17",
"cpufeatures",
"digest",
]
@@ -5555,7 +5547,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
dependencies = [
"cfg-if",
"cpufeatures 0.2.17",
"cpufeatures",
"digest",
]
@@ -5733,12 +5725,12 @@ dependencies = [
[[package]]
name = "socket2"
version = "0.6.3"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e"
checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807"
dependencies = [
"libc",
"windows-sys 0.61.1",
"windows-sys 0.59.0",
]
[[package]]
@@ -6152,9 +6144,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "tokio"
version = "1.52.1"
version = "1.50.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b67dee974fe86fd92cc45b7a95fdd2f99a36a6d7b0d431a231178d3d670bbcc6"
checksum = "27ad5e34374e03cfffefc301becb44e9dc3c17584f414349ebe29ed26661822d"
dependencies = [
"bytes",
"libc",
@@ -6162,7 +6154,7 @@ dependencies = [
"parking_lot",
"pin-project-lite",
"signal-hook-registry",
"socket2 0.6.3",
"socket2 0.6.0",
"tokio-macros",
"windows-sys 0.61.1",
]
@@ -6179,9 +6171,9 @@ dependencies = [
[[package]]
name = "tokio-macros"
version = "2.7.0"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496"
checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5"
dependencies = [
"proc-macro2",
"quote",

View File

@@ -33,17 +33,7 @@ ignore = [
# We do not check CRL and cannot update rustls-webpki 0.102.8
# which is a dependency of iroh 0.35.0.
# <https://rustsec.org/advisories/RUSTSEC-2026-0104>
"RUSTSEC-2026-0104",
# hickory-proto 0.25.2 unbounded loop in DNSSEC code.
# Dependency of iroh 0.35.0, cannot be updated as of 2026-05-02.
# <https://rustsec.org/advisories/RUSTSEC-2026-0118>
"RUSTSEC-2026-0118",
# hickory-proto 0.25.2 quadratic complexity issue.
# Dependency of iroh 0.35.0, cannot be updated as of 2026-05-02.
# <https://rustsec.org/advisories/RUSTSEC-2026-0119>
"RUSTSEC-2026-0119"
"RUSTSEC-2026-0104"
]
[bans]
@@ -55,7 +45,6 @@ skip = [
{ name = "async-channel", version = "1.9.0" },
{ name = "bitflags", version = "1.3.2" },
{ name = "constant_time_eq", version = "0.3.1" },
{ name = "cpufeatures", version = "0.2.17" },
{ name = "derive_more-impl", version = "1.0.0" },
{ name = "derive_more", version = "1.0.0" },
{ name = "event-listener", version = "2.5.3" },

View File

@@ -1,7 +1,6 @@
//! Helper functions for decryption.
//! The actual decryption is done in the [`crate::pgp`] module.
use std::collections::HashSet;
use std::io::Cursor;
use anyhow::{Context as _, Result, bail};
@@ -19,8 +18,8 @@ use crate::chat::ChatId;
use crate::constants::Chattype;
use crate::contact::ContactId;
use crate::context::Context;
use crate::key::load_self_secret_keyring;
use crate::key::self_fingerprint;
use crate::key::{Fingerprint, SignedPublicKey, load_self_secret_keyring};
use crate::token::Namespace;
/// Tries to decrypt the message,
@@ -335,36 +334,6 @@ fn get_autocrypt_mime<'a, 'b>(mail: &'a ParsedMail<'b>) -> Option<&'a ParsedMail
}
}
/// Validates signatures of Multipart/Signed message part, as defined in RFC 1847.
///
/// Returns the signed part and the set of key
/// fingerprints for which there is a valid signature.
///
/// Returns None if the message is not Multipart/Signed or doesn't contain necessary parts.
pub(crate) fn validate_detached_signature<'a, 'b>(
mail: &'a ParsedMail<'b>,
public_keyring_for_validate: &[SignedPublicKey],
) -> Option<(&'a ParsedMail<'b>, HashSet<Fingerprint>)> {
if mail.ctype.mimetype != "multipart/signed" {
return None;
}
if let [first_part, second_part] = &mail.subparts[..] {
// First part is the content, second part is the signature.
let content = first_part.raw_bytes;
let ret_valid_signatures = match second_part.get_body_raw() {
Ok(signature) => {
crate::pgp::pk_validate(content, &signature, public_keyring_for_validate)
.unwrap_or_default()
}
Err(_) => Default::default(),
};
Some((first_part, ret_valid_signatures))
} else {
None
}
}
#[cfg(test)]
mod tests {
use super::*;

View File

@@ -871,7 +871,7 @@ mod tests {
use crate::config::Config;
use crate::message::MessageState;
use crate::receive_imf::receive_imf;
use crate::test_utils::{ExpectedEvents, TestContext, TestContextManager};
use crate::test_utils::{TestContext, TestContextManager};
use crate::tools::SystemTime;
#[test]
@@ -1103,9 +1103,6 @@ Content-Disposition: attachment; filename="location.kml"
.await?;
let alice_chat = alice.create_chat(bob).await;
// Bob needs the chat accepted so that "normal" messages from Alice trigger `IncomingMsg`.
// Location-only messages still must trigger `MsgsChanged`.
bob.create_chat(alice).await;
// Alice enables location streaming.
// Bob receives a message saying that Alice enabled location streaming.
@@ -1120,18 +1117,7 @@ Content-Disposition: attachment; filename="location.kml"
SystemTime::shift(Duration::from_secs(10));
delete_expired(alice, time()).await?;
maybe_send(alice).await?;
bob.evtracker.clear_events();
bob.recv_msg_opt(&alice.pop_sent_msg().await).await;
bob.evtracker
.get_matching_ex(
bob,
ExpectedEvents {
expected: |e| matches!(e, EventType::MsgsChanged { .. }),
unexpected: |e| matches!(e, EventType::IncomingMsg { .. }),
},
)
.await
.unwrap();
assert_eq!(get_range(alice, None, None, 0, 0).await?.len(), 1);
assert_eq!(get_range(bob, None, None, 0, 0).await?.len(), 1);

View File

@@ -20,7 +20,7 @@ use crate::config::Config;
use crate::constants;
use crate::contact::{ContactId, import_public_key};
use crate::context::Context;
use crate::decrypt::{self, validate_detached_signature};
use crate::decrypt::{self};
use crate::dehtml::dehtml;
use crate::download::PostMsgMetadata;
use crate::events::EventType;
@@ -487,19 +487,6 @@ impl MimeMessage {
HashMap::new()
};
let mail = mail.as_ref().map(|mail| {
let (content, signatures_detached) = validate_detached_signature(mail, &public_keyring)
.unwrap_or((mail, Default::default()));
if is_encrypted {
let signatures_detached = signatures_detached
.into_iter()
.map(|fp| (fp, Vec::new()))
.collect::<HashMap<_, _>>();
signatures.extend(signatures_detached);
}
content
});
if let Some(expected_sender_fingerprint) = expected_sender_fingerprint {
ensure!(
!signatures.is_empty(),
@@ -515,7 +502,7 @@ impl MimeMessage {
);
}
if let (Ok(mail), true) = (mail, is_encrypted) {
if let (Ok(mail), true) = (&mail, is_encrypted) {
if !signatures.is_empty() {
// Unsigned "Subject" mustn't be prepended to messages shown as encrypted
// (<https://github.com/deltachat/deltachat-core-rust/issues/1790>).
@@ -540,7 +527,7 @@ impl MimeMessage {
&mut inner_from,
&mut list_post,
&mut chat_disposition_notification_to,
mail,
&mail,
);
if !signatures.is_empty() {
@@ -584,7 +571,7 @@ impl MimeMessage {
signatures.clear();
}
if let (Ok(mail), true) = (mail, is_encrypted)
if let (Ok(mail), true) = (&mail, is_encrypted)
&& let Some(post_msg_rfc724_mid) =
mail.headers.get_header_value(HeaderDef::ChatPostMessageId)
{
@@ -642,7 +629,7 @@ impl MimeMessage {
from,
incoming,
chat_disposition_notification_to,
decryption_error: mail.err().map(|err| format!("{err:#}")),
decryption_error: mail.as_ref().err().map(|err| format!("{err:#}")),
// only non-empty if it was a valid autocrypt message
signature,
@@ -668,9 +655,9 @@ impl MimeMessage {
pre_message,
};
match mail {
match &mail {
Ok(mail) => {
parser.parse_mime_recursive(context, mail, false).await?;
parser.parse_mime_recursive(context, &mail, false).await?;
}
Err(err) => {
let txt = "[This message cannot be decrypted.\n\n• It might already help to simply reply to this message and ask the sender to send the message again.\n\n• If you just re-installed Delta Chat then it is best if you re-setup Delta Chat now and choose \"Add as second device\" or import a backup.]";

View File

@@ -231,10 +231,7 @@ static DNS_PRELOAD: LazyLock<HashMap<&'static str, Vec<IpAddr>>> = LazyLock::new
HashMap::from([
(
"imap.163.com",
vec![
IpAddr::V4(Ipv4Addr::new(111, 124, 203, 45)),
IpAddr::V4(Ipv4Addr::new(111, 124, 203, 50)),
],
vec![IpAddr::V4(Ipv4Addr::new(111, 124, 203, 45))],
),
(
"smtp.163.com",
@@ -425,12 +422,12 @@ static DNS_PRELOAD: LazyLock<HashMap<&'static str, Vec<IpAddr>>> = LazyLock::new
"nine.testrun.org",
vec![
IpAddr::V4(Ipv4Addr::new(128, 140, 126, 197)),
IpAddr::V4(Ipv4Addr::new(116, 202, 233, 236)),
IpAddr::V4(Ipv4Addr::new(216, 144, 228, 100)),
IpAddr::V4(Ipv4Addr::new(77, 42, 49, 41)),
IpAddr::V6(Ipv6Addr::new(0x2a01, 0x4f8, 0x241, 0x4ce8, 0, 0, 0, 2)),
IpAddr::V6(Ipv6Addr::new(
0x2001, 0x41d0, 0x701, 0x1100, 0, 0, 0, 0x8ab1,
)),
IpAddr::V6(Ipv6Addr::new(0x2a01, 0x4f9, 0xfff1, 0x59, 0, 0, 0, 1)),
],
),
(
@@ -700,10 +697,6 @@ static DNS_PRELOAD: LazyLock<HashMap<&'static str, Vec<IpAddr>>> = LazyLock::new
"chatmail.hackea.org",
vec![IpAddr::V4(Ipv4Addr::new(82, 165, 11, 85))],
),
(
"chat.adminforge.de",
vec![IpAddr::V4(Ipv4Addr::new(94, 130, 17, 142))],
),
(
"chika.aangat.lahat.computer",
vec![IpAddr::V4(Ipv4Addr::new(71, 19, 150, 113))],
@@ -745,46 +738,6 @@ static DNS_PRELOAD: LazyLock<HashMap<&'static str, Vec<IpAddr>>> = LazyLock::new
"danneskjold.de",
vec![IpAddr::V4(Ipv4Addr::new(46, 62, 216, 132))],
),
(
"chat.in-the.eu",
vec![IpAddr::V4(Ipv4Addr::new(78, 46, 190, 129))],
),
(
"chat.nuvon.app",
vec![IpAddr::V4(Ipv4Addr::new(178, 238, 38, 165))],
),
(
"nibblehole.com",
vec![IpAddr::V4(Ipv4Addr::new(94, 247, 42, 209))],
),
(
"chat.zashm.org",
vec![IpAddr::V4(Ipv4Addr::new(91, 245, 76, 39))],
),
(
"chat.sus.fr",
vec![IpAddr::V4(Ipv4Addr::new(152, 67, 76, 190))],
),
(
"delta.thelab.uno",
vec![IpAddr::V4(Ipv4Addr::new(146, 59, 228, 39))],
),
(
"chat.vim.wtf",
vec![IpAddr::V4(Ipv4Addr::new(116, 203, 206, 170))],
),
(
"uninterest.ing",
vec![IpAddr::V4(Ipv4Addr::new(172, 245, 70, 237))],
),
(
"sweetfern.net",
vec![IpAddr::V4(Ipv4Addr::new(178, 156, 228, 133))],
),
(
"delta.disobey.net",
vec![IpAddr::V4(Ipv4Addr::new(37, 74, 102, 44))],
),
(
"darkrun.dev",
vec![IpAddr::V4(Ipv4Addr::new(72, 11, 149, 146))],

View File

@@ -1019,15 +1019,8 @@ UPDATE msgs SET state=? WHERE
let is_bot = context.get_config_bool(Config::Bot).await?;
let is_pre_message = matches!(mime_parser.pre_message, PreMessageMode::Pre { .. });
let skip_bot_notify = is_bot && is_pre_message;
let is_empty = !is_pre_message
&& mime_parser.parts.first().is_none_or(|p| {
p.typ == Viewtype::Text && p.msg.is_empty() && p.param.get(Param::Quote).is_none()
});
let important = mime_parser.incoming
&& !is_empty
&& fresh
&& !is_old_contact_request
&& !skip_bot_notify;
let important =
mime_parser.incoming && fresh && !is_old_contact_request && !skip_bot_notify;
for msg_id in &received_msg.msg_ids {
chat_id.emit_msg_event(context, *msg_id, important);

View File

@@ -1431,12 +1431,6 @@ pub fn fiona_keypair() -> SignedSecretKey {
#[derive(Debug)]
pub struct EventTracker(EventEmitter);
/// See [`super::EventTracker::get_matching_ex`].
pub struct ExpectedEvents<E: Fn(&EventType) -> bool, U: Fn(&EventType) -> bool> {
pub expected: E,
pub unexpected: U,
}
impl Deref for EventTracker {
type Target = EventEmitter;
@@ -1473,39 +1467,21 @@ impl EventTracker {
.expect("timeout waiting for event match")
}
/// Consumes all emitted events returning the first matching one if any.
/// Consumes emitted events returning the first matching one if any.
pub async fn get_matching_opt<F: Fn(&EventType) -> bool>(
&self,
ctx: &Context,
event_matcher: F,
) -> Option<EventType> {
self.get_matching_ex(
ctx,
ExpectedEvents {
expected: event_matcher,
unexpected: |_| false,
},
)
.await
}
/// Consumes all emitted events returning the first matching one if any. Panics on unexpected
/// events.
pub async fn get_matching_ex<E: Fn(&EventType) -> bool, U: Fn(&EventType) -> bool>(
&self,
ctx: &Context,
args: ExpectedEvents<E, U>,
) -> Option<EventType> {
ctx.emit_event(EventType::Test);
let mut found_event = None;
loop {
let event = self.recv().await.unwrap();
assert!(!(args.unexpected)(&event.typ));
if let EventType::Test = event.typ {
return found_event;
}
if (args.expected)(&event.typ) {
found_event.get_or_insert(event.typ);
if event_matcher(&event.typ) {
found_event = Some(event.typ);
}
}
}