Skip to content

build: update to rPGP 0.16.0 #6719

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 25 commits into from
May 29, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
180 changes: 145 additions & 35 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ num-derive = "0.4"
num-traits = { workspace = true }
parking_lot = "0.12"
percent-encoding = "2.3"
pgp = { version = "0.15.0", default-features = false }
pgp = { version = "0.16.0", default-features = false }
pin-project = "1"
qrcodegen = "1.7.0"
quick-xml = "0.37"
Expand Down
3 changes: 3 additions & 0 deletions deny.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ ignore = [
skip = [
{ name = "async-channel", version = "1.9.0" },
{ name = "bitflags", version = "1.3.2" },
{ name = "derive_more-impl", version = "1.0.0" },
{ name = "derive_more", version = "1.0.0" },
{ name = "event-listener", version = "2.5.3" },
{ name = "generator", version = "0.7.5" },
{ name = "getrandom", version = "0.2.12" },
Expand All @@ -38,6 +40,7 @@ skip = [
{ name = "regex-automata", version = "0.1.10" },
{ name = "regex-syntax", version = "0.6.29" },
{ name = "rustix", version = "0.38.44" },
{ name = "serdect", version = "0.2.0" },
{ name = "spin", version = "0.9.8" },
{ name = "strum_macros", version = "0.26.2" },
{ name = "strum", version = "0.26.2" },
Expand Down
4 changes: 2 additions & 2 deletions src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ use std::time::Duration;

use anyhow::{bail, ensure, Context as _, Result};
use async_channel::{self as channel, Receiver, Sender};
use pgp::composed::SignedPublicKey;
use pgp::types::PublicKeyTrait;
use pgp::SignedPublicKey;
use ratelimit::Ratelimit;
use tokio::sync::{Mutex, Notify, RwLock};

Expand Down Expand Up @@ -1074,7 +1074,7 @@ impl Context {
res += &format!("db_size_bytes {db_size}\n");

let secret_key = &load_self_secret_key(self).await?.primary_key;
let key_created = secret_key.created_at().timestamp();
let key_created = secret_key.public_key().created_at().timestamp();
res += &format!("key_created {key_created}\n");

// how many of the chats active in the last months are:
Expand Down
8 changes: 4 additions & 4 deletions src/decrypt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ use crate::pgp;
/// Tries to decrypt a message, but only if it is structured as an Autocrypt message.
///
/// If successful and the message is encrypted, returns decrypted body.
pub fn try_decrypt(
mail: &ParsedMail<'_>,
private_keyring: &[SignedSecretKey],
) -> Result<Option<::pgp::composed::Message>> {
pub fn try_decrypt<'a>(
mail: &'a ParsedMail<'a>,
private_keyring: &'a [SignedSecretKey],
) -> Result<Option<::pgp::composed::Message<'static>>> {
let Some(encrypted_data_part) = get_encrypted_mime(mail) else {
return Ok(None);
};
Expand Down
7 changes: 3 additions & 4 deletions src/e2ee.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ impl EncryptHelper {
let cursor = Cursor::new(&mut raw_message);
mail_to_encrypt.clone().write_part(cursor).ok();

let ctext = pgp::pk_encrypt(&raw_message, keyring, Some(sign_key), compress).await?;
let ctext = pgp::pk_encrypt(raw_message, keyring, Some(sign_key), compress).await?;

Ok(ctext)
}
Expand All @@ -153,9 +153,8 @@ impl EncryptHelper {
pub async fn sign(self, context: &Context, mail: &MimePart<'static>) -> Result<String> {
let sign_key = load_self_secret_key(context).await?;
let mut buffer = Vec::new();
let cursor = Cursor::new(&mut buffer);
mail.clone().write_part(cursor).ok();
let signature = pgp::pk_calc_signature(&buffer, &sign_key)?;
mail.clone().write_part(&mut buffer)?;
let signature = pgp::pk_calc_signature(buffer, &sign_key)?;
Ok(signature)
}
}
Expand Down
8 changes: 6 additions & 2 deletions src/imex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ use std::ffi::OsStr;
use std::path::{Path, PathBuf};
use std::pin::Pin;

use ::pgp::types::PublicKeyTrait;
use anyhow::{bail, ensure, format_err, Context as _, Result};
use futures::TryStreamExt;
use futures_lite::FutureExt;
Expand Down Expand Up @@ -32,6 +31,7 @@ use crate::tools::{
mod key_transfer;
mod transfer;

use ::pgp::types::KeyDetails;
pub use key_transfer::{continue_key_transfer, initiate_key_transfer};
pub use transfer::{get_backup, BackupProvider};

Expand Down Expand Up @@ -173,7 +173,11 @@ async fn set_self_key(context: &Context, armored: &str) -> Result<()> {
};
key::store_self_keypair(context, &keypair).await?;

info!(context, "stored self key: {:?}", keypair.secret.key_id());
info!(
context,
"stored self key: {:?}",
keypair.secret.public_key().key_id()
);
Ok(())
}

Expand Down
30 changes: 15 additions & 15 deletions src/imex/key_transfer.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
//! # Key transfer via Autocrypt Setup Message.
use std::io::BufReader;

use rand::{thread_rng, Rng};

use anyhow::{bail, ensure, Result};
Expand Down Expand Up @@ -71,7 +73,7 @@ pub async fn continue_key_transfer(
if let Some(filename) = msg.get_file(context) {
let file = open_file_std(context, filename)?;
let sc = normalize_setup_code(setup_code);
let armored_key = decrypt_setup_file(&sc, file).await?;
let armored_key = decrypt_setup_file(&sc, BufReader::new(file)).await?;
set_self_key(context, &armored_key).await?;
context.set_config_bool(Config::BccSelf, true).await?;

Expand All @@ -96,7 +98,7 @@ pub async fn render_setup_file(context: &Context, passphrase: &str) -> Result<St
true => Some(("Autocrypt-Prefer-Encrypt", "mutual")),
};
let private_key_asc = private_key.to_asc(ac_headers);
let encr = pgp::symm_encrypt(passphrase, private_key_asc.as_bytes())
let encr = pgp::symm_encrypt(passphrase, private_key_asc.into_bytes())
.await?
.replace('\n', "\r\n");

Expand Down Expand Up @@ -155,7 +157,7 @@ fn create_setup_code(_context: &Context) -> String {
ret
}

async fn decrypt_setup_file<T: std::io::Read + std::io::Seek>(
async fn decrypt_setup_file<T: std::fmt::Debug + std::io::BufRead + Send + 'static>(
passphrase: &str,
file: T,
) -> Result<String> {
Expand Down Expand Up @@ -258,11 +260,10 @@ mod tests {

assert!(!base64.is_empty());

let setup_file = S_EM_SETUPFILE.to_string();
let decrypted =
decrypt_setup_file(S_EM_SETUPCODE, std::io::Cursor::new(setup_file.as_bytes()))
.await
.unwrap();
let setup_file = S_EM_SETUPFILE;
let decrypted = decrypt_setup_file(S_EM_SETUPCODE, setup_file.as_bytes())
.await
.unwrap();

let (typ, headers, _base64) = split_armored_data(decrypted.as_bytes()).unwrap();

Expand All @@ -278,14 +279,13 @@ mod tests {
/// "Implementations MUST NOT use plaintext in Symmetrically Encrypted Data packets".
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_decrypt_plaintext_autocrypt_setup_message() {
let setup_file = S_PLAINTEXT_SETUPFILE.to_string();
let setup_file = S_PLAINTEXT_SETUPFILE;
let incorrect_setupcode = "0000-0000-0000-0000-0000-0000-0000-0000-0000";
assert!(decrypt_setup_file(
incorrect_setupcode,
std::io::Cursor::new(setup_file.as_bytes()),
)
.await
.is_err());
assert!(
decrypt_setup_file(incorrect_setupcode, setup_file.as_bytes(),)
.await
.is_err()
);
}

#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
Expand Down
36 changes: 28 additions & 8 deletions src/key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use deltachat_contact_tools::EmailAddress;
use pgp::composed::Deserializable;
pub use pgp::composed::{SignedPublicKey, SignedSecretKey};
use pgp::ser::Serialize;
use pgp::types::{PublicKeyTrait, SecretKeyTrait};
use pgp::types::{KeyDetails, KeyId, Password};
use rand::thread_rng;
use tokio::runtime::Handle;

Expand All @@ -24,7 +24,7 @@ use crate::tools::{self, time_elapsed};
/// This trait is implemented for rPGP's [SignedPublicKey] and
/// [SignedSecretKey] types and makes working with them a little
/// easier in the deltachat world.
pub(crate) trait DcKey: Serialize + Deserializable + PublicKeyTrait + Clone {
pub(crate) trait DcKey: Serialize + Deserializable + Clone {
/// Create a key from some bytes.
fn from_slice(bytes: &[u8]) -> Result<Self> {
let res = <Self as Deserializable>::from_bytes(Cursor::new(bytes));
Expand Down Expand Up @@ -78,7 +78,7 @@ pub(crate) trait DcKey: Serialize + Deserializable + PublicKeyTrait + Clone {
let bytes = data.as_bytes();
let res = Self::from_armor_single(Cursor::new(bytes));
let (key, headers) = match res {
Err(pgp::errors::Error::NoMatchingPacket) => match Self::is_private() {
Err(pgp::errors::Error::NoMatchingPacket { .. }) => match Self::is_private() {
true => bail!("No private key packet found"),
false => bail!("No public key packet found"),
},
Expand Down Expand Up @@ -123,11 +123,10 @@ pub(crate) trait DcKey: Serialize + Deserializable + PublicKeyTrait + Clone {
fn to_asc(&self, header: Option<(&str, &str)>) -> String;

/// The fingerprint for the key.
fn dc_fingerprint(&self) -> Fingerprint {
PublicKeyTrait::fingerprint(self).into()
}
fn dc_fingerprint(&self) -> Fingerprint;

fn is_private() -> bool;
fn key_id(&self) -> KeyId;
}

pub(crate) async fn load_self_public_key(context: &Context) -> Result<SignedPublicKey> {
Expand Down Expand Up @@ -230,6 +229,14 @@ impl DcKey for SignedPublicKey {
fn is_private() -> bool {
false
}

fn dc_fingerprint(&self) -> Fingerprint {
self.fingerprint().into()
}

fn key_id(&self) -> KeyId {
KeyDetails::key_id(self)
}
}

impl DcKey for SignedSecretKey {
Expand All @@ -249,6 +256,14 @@ impl DcKey for SignedSecretKey {
fn is_private() -> bool {
true
}

fn dc_fingerprint(&self) -> Fingerprint {
self.fingerprint().into()
}

fn key_id(&self) -> KeyId {
KeyDetails::key_id(&**self)
}
}

/// Deltachat extension trait for secret keys.
Expand All @@ -262,9 +277,14 @@ pub(crate) trait DcSecretKey {
impl DcSecretKey for SignedSecretKey {
fn split_public_key(&self) -> Result<SignedPublicKey> {
self.verify()?;
let unsigned_pubkey = SecretKeyTrait::public_key(self);
let unsigned_pubkey = self.public_key();
let mut rng = thread_rng();
let signed_pubkey = unsigned_pubkey.sign(&mut rng, self, || "".into())?;
let signed_pubkey = unsigned_pubkey.sign(
&mut rng,
&self.primary_key,
self.primary_key.public_key(),
&Password::empty(),
)?;
Ok(signed_pubkey)
}
}
Expand Down
10 changes: 5 additions & 5 deletions src/mimeparser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -342,10 +342,10 @@ impl MimeMessage {
let mail_raw; // Memory location for a possible decrypted message.
let decrypted_msg; // Decrypted signed OpenPGP message.

let (mail, encrypted) =
let (mail, is_encrypted) =
match tokio::task::block_in_place(|| try_decrypt(&mail, &private_keyring)) {
Ok(Some(msg)) => {
mail_raw = msg.get_content()?.unwrap_or_default();
Ok(Some(mut msg)) => {
mail_raw = msg.as_data_vec().unwrap_or_default();

let decrypted_mail = mailparse::parse_mail(&mail_raw)?;
if std::env::var(crate::DCC_MIME_DEBUG).is_ok() {
Expand Down Expand Up @@ -434,7 +434,7 @@ impl MimeMessage {
signatures.extend(signatures_detached);
content
});
if let (Ok(mail), true) = (mail, encrypted) {
if let (Ok(mail), true) = (mail, is_encrypted) {
if !signatures.is_empty() {
// Remove unsigned opportunistically protected headers from messages considered
// Autocrypt-encrypted / displayed with padlock.
Expand Down Expand Up @@ -529,7 +529,7 @@ impl MimeMessage {
}
}
}
if !encrypted {
if !is_encrypted {
signatures.clear();
}
if let Some(peerstate) = &mut peerstate {
Expand Down
Loading