diff --git a/src/decrypt.rs b/src/decrypt.rs index 43abf9797..0ced2671c 100644 --- a/src/decrypt.rs +++ b/src/decrypt.rs @@ -12,7 +12,7 @@ use crate::authres::{self, DkimResults}; use crate::contact::addr_cmp; use crate::context::Context; use crate::headerdef::{HeaderDef, HeaderDefMap}; -use crate::key::{DcKey, Fingerprint, SignedPublicKey, SignedSecretKey}; +use crate::key::{self, DcKey, Fingerprint, SignedPublicKey, SignedSecretKey}; use crate::peerstate::Peerstate; use crate::pgp; @@ -264,16 +264,22 @@ pub(crate) fn validate_detached_signature<'a, 'b>( } } -pub(crate) fn keyring_from_peerstate(peerstate: Option<&Peerstate>) -> Vec { +/// Returns public keyring for `peerstate`. +pub(crate) async fn keyring_from_peerstate( + context: &Context, + peerstate: Option<&Peerstate>, +) -> Result> { let mut public_keyring_for_validate = Vec::new(); if let Some(peerstate) = peerstate { if let Some(key) = &peerstate.public_key { public_keyring_for_validate.push(key.clone()); } else if let Some(key) = &peerstate.gossip_key { public_keyring_for_validate.push(key.clone()); + } else if context.is_self_addr(&peerstate.addr).await? { + public_keyring_for_validate = key::load_self_public_keyring(context).await?; } } - public_keyring_for_validate + Ok(public_keyring_for_validate) } /// Applies Autocrypt header to Autocrypt peer state and saves it into the database. @@ -292,6 +298,7 @@ pub(crate) async fn get_autocrypt_peerstate( message_time: i64, allow_change: bool, ) -> Result> { + let allow_change = allow_change && !context.is_self_addr(from).await?; let mut peerstate; // Apply Autocrypt header diff --git a/src/key.rs b/src/key.rs index 7521c7cd2..fb21d9fe0 100644 --- a/src/key.rs +++ b/src/key.rs @@ -101,6 +101,25 @@ pub(crate) async fn load_self_public_key(context: &Context) -> Result Result> { + let keys = context + .sql + .query_map( + r#"SELECT public_key + FROM keypairs + ORDER BY id=(SELECT value FROM config WHERE keyname='key_id') DESC"#, + (), + |row| row.get::<_, Vec>(0), + |keys| keys.collect::, _>>().map_err(Into::into), + ) + .await? + .into_iter() + .filter_map(|bytes| SignedPublicKey::from_slice(&bytes).log_err(context).ok()) + .collect(); + Ok(keys) +} + pub(crate) async fn load_self_secret_key(context: &Context) -> Result { let private_key = context .sql diff --git a/src/mimeparser.rs b/src/mimeparser.rs index 9b0c16dda..4da59e069 100644 --- a/src/mimeparser.rs +++ b/src/mimeparser.rs @@ -304,7 +304,8 @@ impl MimeMessage { hop_info += "\n\n"; hop_info += &decryption_info.dkim_results.to_string(); - let public_keyring = keyring_from_peerstate(decryption_info.peerstate.as_ref()); + let public_keyring = + keyring_from_peerstate(context, decryption_info.peerstate.as_ref()).await?; let (mail, mut signatures, encrypted) = match tokio::task::block_in_place(|| { try_decrypt(&mail, &private_keyring, &public_keyring) }) { diff --git a/src/peerstate.rs b/src/peerstate.rs index a465395c1..d44abbf3b 100644 --- a/src/peerstate.rs +++ b/src/peerstate.rs @@ -16,6 +16,7 @@ use crate::message::Message; use crate::mimeparser::SystemMessage; use crate::sql::Sql; use crate::stock_str; +use crate::tools; /// Type of the public key stored inside the peerstate. #[derive(Debug)] @@ -165,6 +166,9 @@ impl Peerstate { /// Loads peerstate corresponding to the given address from the database. pub async fn from_addr(context: &Context, addr: &str) -> Result> { + if context.is_self_addr(addr).await? { + return Ok(Some(Peerstate::get_self_stub(addr))); + } let query = "SELECT addr, last_seen, last_seen_autocrypt, prefer_encrypted, public_key, \ gossip_timestamp, gossip_key, public_key_fingerprint, gossip_key_fingerprint, \ verified_key, verified_key_fingerprint, \ @@ -182,6 +186,7 @@ impl Peerstate { context: &Context, fingerprint: &Fingerprint, ) -> Result> { + // NOTE: If it's our key fingerprint, this returns None currently. let query = "SELECT addr, last_seen, last_seen_autocrypt, prefer_encrypted, public_key, \ gossip_timestamp, gossip_key, public_key_fingerprint, gossip_key_fingerprint, \ verified_key, verified_key_fingerprint, \ @@ -206,6 +211,9 @@ impl Peerstate { fingerprint: &Fingerprint, addr: &str, ) -> Result> { + if context.is_self_addr(addr).await? { + return Ok(Some(Peerstate::get_self_stub(addr))); + } let query = "SELECT addr, last_seen, last_seen_autocrypt, prefer_encrypted, public_key, \ gossip_timestamp, gossip_key, public_key_fingerprint, gossip_key_fingerprint, \ verified_key, verified_key_fingerprint, \ @@ -221,6 +229,34 @@ impl Peerstate { Self::from_stmt(context, query, (&fp, &addr, &fp)).await } + /// Returns peerstate stub for self `addr`. + /// + /// Needed for [`crate::decrypt::keyring_from_peerstate()`] which returns a keyring of all our + /// pubkeys for such a stub so that we can check if a message is signed by us. + fn get_self_stub(addr: &str) -> Self { + let now = tools::time(); + // We can have multiple pubkeys, just make the corresponding fields None. + Self { + addr: addr.to_string(), + last_seen: now, + last_seen_autocrypt: now, + prefer_encrypt: EncryptPreference::Mutual, + public_key: None, + public_key_fingerprint: None, + gossip_key: None, + gossip_key_fingerprint: None, + gossip_timestamp: 0, + verified_key: None, + verified_key_fingerprint: None, + verifier: None, + secondary_verified_key: None, + secondary_verified_key_fingerprint: None, + secondary_verifier: None, + backward_verified_key_id: None, + fingerprint_changed: false, + } + } + async fn from_stmt( context: &Context, query: &str,