mirror of
https://github.com/chatmail/core.git
synced 2026-04-02 05:22:14 +03:00
refactor: use slices and vectors instead of Keyring wrapper
This change removes all traces of dc_keyring_t, which was a C implementation of dynamically sized array.
This commit is contained in:
@@ -846,7 +846,7 @@ EXCLUDE_PATTERNS =
|
||||
# exclude all test directories use the pattern */test/*
|
||||
|
||||
######################################################
|
||||
EXCLUDE_SYMBOLS = dc_aheader_t dc_apeerstate_t dc_e2ee_helper_t dc_imap_t dc_job*_t dc_key_t dc_keyring_t dc_loginparam_t dc_mime*_t
|
||||
EXCLUDE_SYMBOLS = dc_aheader_t dc_apeerstate_t dc_e2ee_helper_t dc_imap_t dc_job*_t dc_key_t dc_loginparam_t dc_mime*_t
|
||||
EXCLUDE_SYMBOLS += dc_saxparser_t dc_simplify_t dc_smtp_t dc_sqlite3_t dc_strbuilder_t dc_param_t dc_hash_t dc_hashelem_t
|
||||
EXCLUDE_SYMBOLS += _dc_* jsmn*
|
||||
######################################################
|
||||
|
||||
@@ -13,7 +13,6 @@ use crate::contact::addr_cmp;
|
||||
use crate::context::Context;
|
||||
use crate::headerdef::{HeaderDef, HeaderDefMap};
|
||||
use crate::key::{DcKey, Fingerprint, SignedPublicKey, SignedSecretKey};
|
||||
use crate::keyring::Keyring;
|
||||
use crate::peerstate::Peerstate;
|
||||
use crate::pgp;
|
||||
|
||||
@@ -26,8 +25,8 @@ use crate::pgp;
|
||||
pub fn try_decrypt(
|
||||
context: &Context,
|
||||
mail: &ParsedMail<'_>,
|
||||
private_keyring: &Keyring<SignedSecretKey>,
|
||||
public_keyring_for_validate: &Keyring<SignedPublicKey>,
|
||||
private_keyring: &[SignedSecretKey],
|
||||
public_keyring_for_validate: &[SignedPublicKey],
|
||||
) -> Result<Option<(Vec<u8>, HashSet<Fingerprint>)>> {
|
||||
let encrypted_data_part = match get_autocrypt_mime(mail)
|
||||
.or_else(|| get_mixed_up_mime(mail))
|
||||
@@ -211,8 +210,8 @@ fn get_autocrypt_mime<'a, 'b>(mail: &'a ParsedMail<'b>) -> Option<&'a ParsedMail
|
||||
/// Returns Ok(None) if nothing encrypted was found.
|
||||
fn decrypt_part(
|
||||
mail: &ParsedMail<'_>,
|
||||
private_keyring: &Keyring<SignedSecretKey>,
|
||||
public_keyring_for_validate: &Keyring<SignedPublicKey>,
|
||||
private_keyring: &[SignedSecretKey],
|
||||
public_keyring_for_validate: &[SignedPublicKey],
|
||||
) -> Result<Option<(Vec<u8>, HashSet<Fingerprint>)>> {
|
||||
let data = mail.get_body_raw()?;
|
||||
|
||||
@@ -247,7 +246,7 @@ fn has_decrypted_pgp_armor(input: &[u8]) -> bool {
|
||||
/// 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: &Keyring<SignedPublicKey>,
|
||||
public_keyring_for_validate: &[SignedPublicKey],
|
||||
) -> Option<(&'a ParsedMail<'b>, HashSet<Fingerprint>)> {
|
||||
if mail.ctype.mimetype != "multipart/signed" {
|
||||
return None;
|
||||
@@ -267,13 +266,13 @@ pub(crate) fn validate_detached_signature<'a, 'b>(
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn keyring_from_peerstate(peerstate: Option<&Peerstate>) -> Keyring<SignedPublicKey> {
|
||||
let mut public_keyring_for_validate: Keyring<SignedPublicKey> = Keyring::new();
|
||||
pub(crate) fn keyring_from_peerstate(peerstate: Option<&Peerstate>) -> Vec<SignedPublicKey> {
|
||||
let mut public_keyring_for_validate = Vec::new();
|
||||
if let Some(peerstate) = peerstate {
|
||||
if let Some(key) = &peerstate.public_key {
|
||||
public_keyring_for_validate.add(key.clone());
|
||||
public_keyring_for_validate.push(key.clone());
|
||||
} else if let Some(key) = &peerstate.gossip_key {
|
||||
public_keyring_for_validate.add(key.clone());
|
||||
public_keyring_for_validate.push(key.clone());
|
||||
}
|
||||
}
|
||||
public_keyring_for_validate
|
||||
|
||||
@@ -7,7 +7,6 @@ use crate::aheader::{Aheader, EncryptPreference};
|
||||
use crate::config::Config;
|
||||
use crate::context::Context;
|
||||
use crate::key::{DcKey, SignedPublicKey, SignedSecretKey};
|
||||
use crate::keyring::Keyring;
|
||||
use crate::peerstate::{Peerstate, PeerstateVerifiedStatus};
|
||||
use crate::pgp;
|
||||
|
||||
@@ -104,7 +103,7 @@ impl EncryptHelper {
|
||||
mail_to_encrypt: lettre_email::PartBuilder,
|
||||
peerstates: Vec<(Option<Peerstate>, &str)>,
|
||||
) -> Result<String> {
|
||||
let mut keyring: Keyring<SignedPublicKey> = Keyring::new();
|
||||
let mut keyring: Vec<SignedPublicKey> = Vec::new();
|
||||
|
||||
for (peerstate, addr) in peerstates
|
||||
.into_iter()
|
||||
@@ -113,9 +112,9 @@ impl EncryptHelper {
|
||||
let key = peerstate
|
||||
.take_key(min_verified)
|
||||
.with_context(|| format!("proper enc-key for {addr} missing, cannot encrypt"))?;
|
||||
keyring.add(key);
|
||||
keyring.push(key);
|
||||
}
|
||||
keyring.add(self.public_key.clone());
|
||||
keyring.push(self.public_key.clone());
|
||||
let sign_key = SignedSecretKey::load_self(context).await?;
|
||||
|
||||
let raw_message = mail_to_encrypt.build().as_string().into_bytes();
|
||||
|
||||
@@ -1,91 +0,0 @@
|
||||
//! Keyring to perform rpgp operations with.
|
||||
|
||||
use anyhow::Result;
|
||||
|
||||
use crate::context::Context;
|
||||
use crate::key::DcKey;
|
||||
|
||||
/// An in-memory keyring.
|
||||
///
|
||||
/// Instances are usually constructed just for the rpgp operation and
|
||||
/// short-lived.
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct Keyring<T>
|
||||
where
|
||||
T: DcKey,
|
||||
{
|
||||
keys: Vec<T>,
|
||||
}
|
||||
|
||||
impl<T> Keyring<T>
|
||||
where
|
||||
T: DcKey,
|
||||
{
|
||||
/// New empty keyring.
|
||||
pub fn new() -> Keyring<T> {
|
||||
Keyring { keys: Vec::new() }
|
||||
}
|
||||
|
||||
/// Create a new keyring with the the user's secret key loaded.
|
||||
pub async fn new_self(context: &Context) -> Result<Keyring<T>> {
|
||||
let mut keyring: Keyring<T> = Keyring::new();
|
||||
keyring.load_self(context).await?;
|
||||
Ok(keyring)
|
||||
}
|
||||
|
||||
/// Load the user's key into the keyring.
|
||||
pub async fn load_self(&mut self, context: &Context) -> Result<()> {
|
||||
self.add(T::load_self(context).await?);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Add a key to the keyring.
|
||||
pub fn add(&mut self, key: T) {
|
||||
self.keys.push(key);
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.keys.len()
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.keys.is_empty()
|
||||
}
|
||||
|
||||
/// A vector with reference to all the keys in the keyring.
|
||||
pub fn keys(&self) -> &[T] {
|
||||
&self.keys
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::key::{SignedPublicKey, SignedSecretKey};
|
||||
use crate::test_utils::{alice_keypair, TestContext};
|
||||
|
||||
#[test]
|
||||
fn test_keyring_add_keys() {
|
||||
let alice = alice_keypair();
|
||||
let mut pub_ring: Keyring<SignedPublicKey> = Keyring::new();
|
||||
pub_ring.add(alice.public.clone());
|
||||
assert_eq!(pub_ring.keys(), [alice.public]);
|
||||
|
||||
let mut sec_ring: Keyring<SignedSecretKey> = Keyring::new();
|
||||
sec_ring.add(alice.secret.clone());
|
||||
assert_eq!(sec_ring.keys(), [alice.secret]);
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_keyring_load_self() {
|
||||
// new_self() implies load_self()
|
||||
let t = TestContext::new_alice().await;
|
||||
let alice = alice_keypair();
|
||||
|
||||
let pub_ring: Keyring<SignedPublicKey> = Keyring::new_self(&t).await.unwrap();
|
||||
assert_eq!(pub_ring.keys(), [alice.public]);
|
||||
|
||||
let sec_ring: Keyring<SignedSecretKey> = Keyring::new_self(&t).await.unwrap();
|
||||
assert_eq!(sec_ring.keys(), [alice.secret]);
|
||||
}
|
||||
}
|
||||
@@ -70,7 +70,6 @@ mod scheduler;
|
||||
#[macro_use]
|
||||
mod job;
|
||||
pub mod key;
|
||||
mod keyring;
|
||||
pub mod location;
|
||||
mod login_param;
|
||||
pub mod message;
|
||||
|
||||
@@ -28,7 +28,6 @@ use crate::dehtml::dehtml;
|
||||
use crate::events::EventType;
|
||||
use crate::headerdef::{HeaderDef, HeaderDefMap};
|
||||
use crate::key::{DcKey, Fingerprint, SignedPublicKey, SignedSecretKey};
|
||||
use crate::keyring::Keyring;
|
||||
use crate::message::{self, set_msg_failed, update_msg_state, MessageState, MsgId, Viewtype};
|
||||
use crate::param::{Param, Params};
|
||||
use crate::peerstate::Peerstate;
|
||||
@@ -276,9 +275,10 @@ impl MimeMessage {
|
||||
headers.remove("chat-verified");
|
||||
|
||||
let from = from.context("No from in message")?;
|
||||
let private_keyring: Keyring<SignedSecretKey> = Keyring::new_self(context)
|
||||
let private_keyring = vec![SignedSecretKey::load_self(context)
|
||||
.await
|
||||
.context("failed to get own keyring")?;
|
||||
.context("Failed to get own key")?];
|
||||
|
||||
let mut decryption_info =
|
||||
prepare_decryption(context, &mail, &from.addr, message_time).await?;
|
||||
|
||||
|
||||
76
src/pgp.rs
76
src/pgp.rs
@@ -20,7 +20,6 @@ use tokio::runtime::Handle;
|
||||
|
||||
use crate::constants::KeyGenType;
|
||||
use crate::key::{DcKey, Fingerprint};
|
||||
use crate::keyring::Keyring;
|
||||
use crate::tools::EmailAddress;
|
||||
|
||||
#[allow(missing_docs)]
|
||||
@@ -229,7 +228,7 @@ fn select_pk_for_encryption(key: &SignedPublicKey) -> Option<SignedPublicKeyOrSu
|
||||
/// and signs it using `private_key_for_signing`.
|
||||
pub async fn pk_encrypt(
|
||||
plain: &[u8],
|
||||
public_keys_for_encryption: Keyring<SignedPublicKey>,
|
||||
public_keys_for_encryption: Vec<SignedPublicKey>,
|
||||
private_key_for_signing: Option<SignedSecretKey>,
|
||||
) -> Result<String> {
|
||||
let lit_msg = Message::new_literal_bytes("", plain);
|
||||
@@ -237,7 +236,6 @@ pub async fn pk_encrypt(
|
||||
Handle::current()
|
||||
.spawn_blocking(move || {
|
||||
let pkeys: Vec<SignedPublicKeyOrSubkey> = public_keys_for_encryption
|
||||
.keys()
|
||||
.iter()
|
||||
.filter_map(select_pk_for_encryption)
|
||||
.collect();
|
||||
@@ -288,15 +286,15 @@ pub fn pk_calc_signature(
|
||||
#[allow(clippy::implicit_hasher)]
|
||||
pub fn pk_decrypt(
|
||||
ctext: Vec<u8>,
|
||||
private_keys_for_decryption: &Keyring<SignedSecretKey>,
|
||||
public_keys_for_validation: &Keyring<SignedPublicKey>,
|
||||
private_keys_for_decryption: &[SignedSecretKey],
|
||||
public_keys_for_validation: &[SignedPublicKey],
|
||||
) -> Result<(Vec<u8>, HashSet<Fingerprint>)> {
|
||||
let mut ret_signature_fingerprints: HashSet<Fingerprint> = Default::default();
|
||||
|
||||
let cursor = Cursor::new(ctext);
|
||||
let (msg, _) = Message::from_armor_single(cursor)?;
|
||||
|
||||
let skeys: Vec<&SignedSecretKey> = private_keys_for_decryption.keys().iter().collect();
|
||||
let skeys: Vec<&SignedSecretKey> = private_keys_for_decryption.iter().collect();
|
||||
|
||||
let (decryptor, _) = msg.decrypt(|| "".into(), &skeys[..])?;
|
||||
let msgs = decryptor.collect::<pgp::errors::Result<Vec<_>>>()?;
|
||||
@@ -311,20 +309,13 @@ pub fn pk_decrypt(
|
||||
None => bail!("The decrypted message is empty"),
|
||||
};
|
||||
|
||||
if !public_keys_for_validation.is_empty() {
|
||||
let pkeys = public_keys_for_validation.keys();
|
||||
|
||||
let mut fingerprints: Vec<Fingerprint> = Vec::new();
|
||||
if let signed_msg @ pgp::composed::Message::Signed { .. } = msg {
|
||||
for pkey in pkeys {
|
||||
if signed_msg.verify(&pkey.primary_key).is_ok() {
|
||||
let fp = DcKey::fingerprint(pkey);
|
||||
fingerprints.push(fp);
|
||||
}
|
||||
if let signed_msg @ pgp::composed::Message::Signed { .. } = msg {
|
||||
for pkey in public_keys_for_validation {
|
||||
if signed_msg.verify(&pkey.primary_key).is_ok() {
|
||||
let fp = DcKey::fingerprint(pkey);
|
||||
ret_signature_fingerprints.insert(fp);
|
||||
}
|
||||
}
|
||||
|
||||
ret_signature_fingerprints.extend(fingerprints);
|
||||
}
|
||||
Ok((content, ret_signature_fingerprints))
|
||||
} else {
|
||||
@@ -336,12 +327,11 @@ pub fn pk_decrypt(
|
||||
pub fn pk_validate(
|
||||
content: &[u8],
|
||||
signature: &[u8],
|
||||
public_keys_for_validation: &Keyring<SignedPublicKey>,
|
||||
public_keys_for_validation: &[SignedPublicKey],
|
||||
) -> Result<HashSet<Fingerprint>> {
|
||||
let mut ret: HashSet<Fingerprint> = Default::default();
|
||||
|
||||
let standalone_signature = StandaloneSignature::from_armor_single(Cursor::new(signature))?.0;
|
||||
let pkeys = public_keys_for_validation.keys();
|
||||
|
||||
// Remove trailing CRLF before the delimiter.
|
||||
// According to RFC 3156 it is considered to be part of the MIME delimiter for the purpose of
|
||||
@@ -350,7 +340,7 @@ pub fn pk_validate(
|
||||
.get(..content.len().saturating_sub(2))
|
||||
.context("index is out of range")?;
|
||||
|
||||
for pkey in pkeys {
|
||||
for pkey in public_keys_for_validation {
|
||||
if standalone_signature.verify(pkey, content).is_ok() {
|
||||
let fp = DcKey::fingerprint(pkey);
|
||||
ret.insert(fp);
|
||||
@@ -485,9 +475,7 @@ mod tests {
|
||||
async fn ctext_signed() -> &'static String {
|
||||
CTEXT_SIGNED
|
||||
.get_or_init(|| async {
|
||||
let mut keyring = Keyring::new();
|
||||
keyring.add(KEYS.alice_public.clone());
|
||||
keyring.add(KEYS.bob_public.clone());
|
||||
let keyring = vec![KEYS.alice_public.clone(), KEYS.bob_public.clone()];
|
||||
|
||||
pk_encrypt(CLEARTEXT, keyring, Some(KEYS.alice_secret.clone()))
|
||||
.await
|
||||
@@ -500,9 +488,7 @@ mod tests {
|
||||
async fn ctext_unsigned() -> &'static String {
|
||||
CTEXT_UNSIGNED
|
||||
.get_or_init(|| async {
|
||||
let mut keyring = Keyring::new();
|
||||
keyring.add(KEYS.alice_public.clone());
|
||||
keyring.add(KEYS.bob_public.clone());
|
||||
let keyring = vec![KEYS.alice_public.clone(), KEYS.bob_public.clone()];
|
||||
pk_encrypt(CLEARTEXT, keyring, None).await.unwrap()
|
||||
})
|
||||
.await
|
||||
@@ -527,10 +513,8 @@ mod tests {
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_decrypt_singed() {
|
||||
// Check decrypting as Alice
|
||||
let mut decrypt_keyring: Keyring<SignedSecretKey> = Keyring::new();
|
||||
decrypt_keyring.add(KEYS.alice_secret.clone());
|
||||
let mut sig_check_keyring: Keyring<SignedPublicKey> = Keyring::new();
|
||||
sig_check_keyring.add(KEYS.alice_public.clone());
|
||||
let decrypt_keyring = vec![KEYS.alice_secret.clone()];
|
||||
let sig_check_keyring = vec![KEYS.alice_public.clone()];
|
||||
let (plain, valid_signatures) = pk_decrypt(
|
||||
ctext_signed().await.as_bytes().to_vec(),
|
||||
&decrypt_keyring,
|
||||
@@ -541,10 +525,8 @@ mod tests {
|
||||
assert_eq!(valid_signatures.len(), 1);
|
||||
|
||||
// Check decrypting as Bob
|
||||
let mut decrypt_keyring = Keyring::new();
|
||||
decrypt_keyring.add(KEYS.bob_secret.clone());
|
||||
let mut sig_check_keyring = Keyring::new();
|
||||
sig_check_keyring.add(KEYS.alice_public.clone());
|
||||
let decrypt_keyring = vec![KEYS.bob_secret.clone()];
|
||||
let sig_check_keyring = vec![KEYS.alice_public.clone()];
|
||||
let (plain, valid_signatures) = pk_decrypt(
|
||||
ctext_signed().await.as_bytes().to_vec(),
|
||||
&decrypt_keyring,
|
||||
@@ -557,15 +539,9 @@ mod tests {
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_decrypt_no_sig_check() {
|
||||
let mut keyring = Keyring::new();
|
||||
keyring.add(KEYS.alice_secret.clone());
|
||||
let empty_keyring = Keyring::new();
|
||||
let (plain, valid_signatures) = pk_decrypt(
|
||||
ctext_signed().await.as_bytes().to_vec(),
|
||||
&keyring,
|
||||
&empty_keyring,
|
||||
)
|
||||
.unwrap();
|
||||
let keyring = vec![KEYS.alice_secret.clone()];
|
||||
let (plain, valid_signatures) =
|
||||
pk_decrypt(ctext_signed().await.as_bytes().to_vec(), &keyring, &[]).unwrap();
|
||||
assert_eq!(plain, CLEARTEXT);
|
||||
assert_eq!(valid_signatures.len(), 0);
|
||||
}
|
||||
@@ -573,10 +549,8 @@ mod tests {
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_decrypt_signed_no_key() {
|
||||
// The validation does not have the public key of the signer.
|
||||
let mut decrypt_keyring = Keyring::new();
|
||||
decrypt_keyring.add(KEYS.bob_secret.clone());
|
||||
let mut sig_check_keyring = Keyring::new();
|
||||
sig_check_keyring.add(KEYS.bob_public.clone());
|
||||
let decrypt_keyring = vec![KEYS.bob_secret.clone()];
|
||||
let sig_check_keyring = vec![KEYS.bob_public.clone()];
|
||||
let (plain, valid_signatures) = pk_decrypt(
|
||||
ctext_signed().await.as_bytes().to_vec(),
|
||||
&decrypt_keyring,
|
||||
@@ -589,13 +563,11 @@ mod tests {
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_decrypt_unsigned() {
|
||||
let mut decrypt_keyring = Keyring::new();
|
||||
decrypt_keyring.add(KEYS.bob_secret.clone());
|
||||
let sig_check_keyring = Keyring::new();
|
||||
let decrypt_keyring = vec![KEYS.bob_secret.clone()];
|
||||
let (plain, valid_signatures) = pk_decrypt(
|
||||
ctext_unsigned().await.as_bytes().to_vec(),
|
||||
&decrypt_keyring,
|
||||
&sig_check_keyring,
|
||||
&[],
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(plain, CLEARTEXT);
|
||||
|
||||
Reference in New Issue
Block a user