Skip to content

Commit 8c6494f

Browse files
committed
WIP: Start with decryption, and a test for it. Next TODO: SQL table migartion.
1 parent e597b6f commit 8c6494f

File tree

6 files changed

+94
-52
lines changed

6 files changed

+94
-52
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ opt-level = 1
1515
# Make anyhow `backtrace` feature useful.
1616
# With `debug = 0` there are no line numbers in the backtrace
1717
# produced with RUST_BACKTRACE=1.
18-
debug = 1
18+
debug = 'full'
1919
opt-level = 0
2020

2121
[profile.fuzz]

src/decrypt.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,14 @@ use crate::pgp;
1414
pub fn try_decrypt<'a>(
1515
mail: &'a ParsedMail<'a>,
1616
private_keyring: &'a [SignedSecretKey],
17+
symmetric_secrets: &[&str],
1718
) -> Result<Option<::pgp::composed::Message<'static>>> {
1819
let Some(encrypted_data_part) = get_encrypted_mime(mail) else {
1920
return Ok(None);
2021
};
2122

2223
let data = encrypted_data_part.get_body_raw()?;
23-
let msg = pgp::pk_decrypt(data, private_keyring)?;
24+
let msg = pgp::decrypt(data, private_keyring, symmetric_secrets)?;
2425

2526
Ok(Some(msg))
2627
}

src/e2ee.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,7 @@ impl EncryptHelper {
7373
let cursor = Cursor::new(&mut raw_message);
7474
mail_to_encrypt.clone().write_part(cursor).ok();
7575

76-
let ctext =
77-
pgp::encrypt_for_broadcast(raw_message, passphrase, Some(sign_key), compress).await?;
76+
let ctext = pgp::encrypt_for_broadcast(raw_message, passphrase, sign_key, compress).await?;
7877

7978
Ok(ctext)
8079
}

src/mimeparser.rs

Lines changed: 40 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -333,50 +333,52 @@ impl MimeMessage {
333333

334334
let mail_raw; // Memory location for a possible decrypted message.
335335
let decrypted_msg; // Decrypted signed OpenPGP message.
336+
let symmetric_secrets =
336337

337-
let (mail, is_encrypted) =
338-
match tokio::task::block_in_place(|| try_decrypt(&mail, &private_keyring)) {
339-
Ok(Some(mut msg)) => {
340-
mail_raw = msg.as_data_vec().unwrap_or_default();
341-
342-
let decrypted_mail = mailparse::parse_mail(&mail_raw)?;
343-
if std::env::var(crate::DCC_MIME_DEBUG).is_ok() {
344-
info!(
345-
context,
346-
"decrypted message mime-body:\n{}",
347-
String::from_utf8_lossy(&mail_raw),
348-
);
349-
}
350-
351-
decrypted_msg = Some(msg);
338+
let (mail, is_encrypted) = match tokio::task::block_in_place(|| {
339+
try_decrypt(&mail, &private_keyring, symmetric_secrets)
340+
}) {
341+
Ok(Some(mut msg)) => {
342+
mail_raw = msg.as_data_vec().unwrap_or_default();
352343

353-
timestamp_sent = Self::get_timestamp_sent(
354-
&decrypted_mail.headers,
355-
timestamp_sent,
356-
timestamp_rcvd,
344+
let decrypted_mail = mailparse::parse_mail(&mail_raw)?;
345+
if std::env::var(crate::DCC_MIME_DEBUG).is_ok() {
346+
info!(
347+
context,
348+
"decrypted message mime-body:\n{}",
349+
String::from_utf8_lossy(&mail_raw),
357350
);
351+
}
358352

359-
if let Some(protected_aheader_value) = decrypted_mail
360-
.headers
361-
.get_header_value(HeaderDef::Autocrypt)
362-
{
363-
aheader_value = Some(protected_aheader_value);
364-
}
353+
decrypted_msg = Some(msg);
365354

366-
(Ok(decrypted_mail), true)
367-
}
368-
Ok(None) => {
369-
mail_raw = Vec::new();
370-
decrypted_msg = None;
371-
(Ok(mail), false)
372-
}
373-
Err(err) => {
374-
mail_raw = Vec::new();
375-
decrypted_msg = None;
376-
warn!(context, "decryption failed: {:#}", err);
377-
(Err(err), false)
355+
timestamp_sent = Self::get_timestamp_sent(
356+
&decrypted_mail.headers,
357+
timestamp_sent,
358+
timestamp_rcvd,
359+
);
360+
361+
if let Some(protected_aheader_value) = decrypted_mail
362+
.headers
363+
.get_header_value(HeaderDef::Autocrypt)
364+
{
365+
aheader_value = Some(protected_aheader_value);
378366
}
379-
};
367+
368+
(Ok(decrypted_mail), true)
369+
}
370+
Ok(None) => {
371+
mail_raw = Vec::new();
372+
decrypted_msg = None;
373+
(Ok(mail), false)
374+
}
375+
Err(err) => {
376+
mail_raw = Vec::new();
377+
decrypted_msg = None;
378+
warn!(context, "decryption failed: {:#}", err);
379+
(Err(err), false)
380+
}
381+
};
380382

381383
let autocrypt_header = if !incoming {
382384
None

src/pgp.rs

Lines changed: 41 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -236,20 +236,28 @@ pub fn pk_calc_signature(
236236
///
237237
/// Receiver private keys are provided in
238238
/// `private_keys_for_decryption`.
239-
pub fn pk_decrypt(
239+
pub fn decrypt(
240240
ctext: Vec<u8>,
241241
private_keys_for_decryption: &[SignedSecretKey],
242+
symmetric_secrets: &[&str],
242243
) -> Result<pgp::composed::Message<'static>> {
243244
let cursor = Cursor::new(ctext);
244245
let (msg, _headers) = Message::from_armor(cursor)?;
245246

246247
let skeys: Vec<&SignedSecretKey> = private_keys_for_decryption.iter().collect();
247248
let empty_pw = Password::empty();
248249

250+
// TODO it may degrade performance that we always try out all passwords here
251+
let message_password: Vec<Password> = symmetric_secrets
252+
.iter()
253+
.map(|p| Password::from(*p))
254+
.collect();
255+
let message_password: Vec<&Password> = message_password.iter().collect();
256+
249257
let ring = TheRing {
250258
secret_keys: skeys,
251259
key_passwords: vec![&empty_pw],
252-
message_password: vec![],
260+
message_password,
253261
session_keys: vec![],
254262
allow_legacy: false,
255263
};
@@ -327,7 +335,7 @@ pub async fn symm_encrypt(passphrase: &str, plain: Vec<u8>) -> Result<String> {
327335
pub async fn encrypt_for_broadcast(
328336
plain: Vec<u8>,
329337
passphrase: &str,
330-
private_key_for_signing: Option<SignedSecretKey>,
338+
private_key_for_signing: SignedSecretKey,
331339
compress: bool,
332340
) -> Result<String> {
333341
let passphrase = Password::from(passphrase.to_string());
@@ -344,11 +352,9 @@ pub async fn encrypt_for_broadcast(
344352
);
345353
msg.encrypt_with_password(&mut rng, s2k, &passphrase)?;
346354

347-
if let Some(ref skey) = private_key_for_signing {
348-
msg.sign(&**skey, Password::empty(), HASH_ALGORITHM);
349-
if compress {
350-
msg.compression(CompressionAlgorithm::ZLIB);
351-
}
355+
msg.sign(&*private_key_for_signing, Password::empty(), HASH_ALGORITHM);
356+
if compress {
357+
msg.compression(CompressionAlgorithm::ZLIB);
352358
}
353359

354360
let encoded_msg = msg.to_armored_string(&mut rng, Default::default())?;
@@ -381,7 +387,10 @@ mod tests {
381387
use tokio::sync::OnceCell;
382388

383389
use super::*;
384-
use crate::test_utils::{alice_keypair, bob_keypair};
390+
use crate::{
391+
key::load_self_secret_key,
392+
test_utils::{TestContext, TestContextManager, alice_keypair, bob_keypair},
393+
};
385394

386395
fn pk_decrypt_and_validate<'a>(
387396
ctext: &'a [u8],
@@ -392,7 +401,7 @@ mod tests {
392401
HashSet<Fingerprint>,
393402
Vec<u8>,
394403
)> {
395-
let mut msg = pk_decrypt(ctext.to_vec(), private_keys_for_decryption)?;
404+
let mut msg = decrypt(ctext.to_vec(), private_keys_for_decryption)?;
396405
let content = msg.as_data_vec()?;
397406
let ret_signature_fingerprints =
398407
valid_signature_fingerprints(&msg, public_keys_for_validation)?;
@@ -578,4 +587,26 @@ mod tests {
578587
assert_eq!(content, CLEARTEXT);
579588
assert_eq!(valid_signatures.len(), 0);
580589
}
590+
591+
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
592+
async fn test_encrypt_decrypt_broadcast() -> Result<()> {
593+
let mut tcm = TestContextManager::new();
594+
let alice = &tcm.alice().await;
595+
let bob = &tcm.bob().await;
596+
597+
let plain = Vec::from(b"this is the secret message");
598+
let shared_secret = "shared secret";
599+
let ctext = encrypt_for_broadcast(
600+
plain,
601+
shared_secret,
602+
load_self_secret_key(alice).await?,
603+
true,
604+
)
605+
.await?;
606+
607+
let bob_private_keyring = crate::key::load_self_secret_keyring(bob).await?;
608+
let decrypted = decrypt(ctext.into(), &bob_private_keyring)?;
609+
610+
Ok(())
611+
}
581612
}

src/sql/migrations.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1247,6 +1247,15 @@ CREATE INDEX gossip_timestamp_index ON gossip_timestamp (chat_id, fingerprint);
12471247
);
12481248
}
12491249

1250+
inc_and_check(&mut migration_version, 133)?;
1251+
if dbversion < migration_version {
1252+
sql.execute_migration(
1253+
"CREATE TABLE symmetric_secrets(
1254+
chat_id INTEGER PRIMARY KEY NOT NULL,
1255+
symmetric_secret: ",
1256+
)
1257+
}
1258+
12501259
let new_version = sql
12511260
.get_raw_config_int(VERSION_CFG)
12521261
.await?

0 commit comments

Comments
 (0)