diff --git a/src/crypto.rs b/src/crypto.rs index 67dae6b..2d9d7f7 100644 --- a/src/crypto.rs +++ b/src/crypto.rs @@ -4,12 +4,14 @@ // cryptographic functions // +use std::io::Write; use std::sync::Arc; use anyhow; use bytes::Bytes; use sequoia_openpgp::parse::{PacketParser, PacketParserResult, Parse}; use sequoia_openpgp::policy::StandardPolicy; +use sequoia_openpgp::serialize::stream::{Message, Armorer, Encryptor, LiteralWriter}; use sequoia_openpgp::{Fingerprint, Cert, Packet}; use sequoia_openpgp::cert::raw::RawCertParser; use sequoia_cert_store::{Store, CertStore, LazyCert}; @@ -20,6 +22,7 @@ use wot::{Depth, Path}; use crate::types::errors::HuskError; use crate::types::husk_context::HuskContext; use crate::types::introducer::Introducer; +use crate::types::recipient::Recipient; pub fn get_local_trust_root(cert_store: &CertStore) -> anyhow::Result { @@ -155,6 +158,42 @@ pub fn is_encrypted(body: &Bytes) -> bool { false } +pub fn encrypt(context: &HuskContext<'_>, body: &Bytes, recipients: &Vec) + -> anyhow::Result { + + let mut keys = Vec::new(); + + // XXX: handle the case where a certificate misses an encryption subkey + for rcpt in recipients { + if rcpt.can_encrypt() { + for cert in &rcpt.certs { + cert.with_policy(&context.policy, None)? + .keys() + .supported() + .alive() + .revoked(false) + .for_transport_encryption() + .for_each(|k| { + keys.push(k); + }); + } + } + } + if keys.len() > 0 { + let mut sink = Vec::new(); + let message = Message::new(&mut sink); + let message = Armorer::new(message).build()?; + let message = Encryptor::for_recipients(message, keys).build()?; + let mut message = LiteralWriter::new(message).build()?; + message.write_all(&body[..])?; + message.finalize()?; + + Ok(Bytes::from(sink)) + } else { + Err(HuskError::NoEncryptionKeys.into()) + } +} + #[cfg(test)] pub mod tests { use bytes::Bytes; diff --git a/src/daemon/mod.rs b/src/daemon/mod.rs index 47518b5..eb82b9e 100644 --- a/src/daemon/mod.rs +++ b/src/daemon/mod.rs @@ -210,14 +210,21 @@ impl Daemon { // none -> accept // partial -> accept (later: split) // full -> encrypt - // check mailbody - // is encrypted -> accept - // else -> encrypt let protection = ProtectionPossibility::from(&context.mail); if protection == ProtectionPossibility::Full { - // encrypt + // if an error occures the body is not exchanged + match crypto::encrypt(&context, &body, &context.mail.recipients) { + Ok(encrypted) => { + if cx.actions.replace_body(&encrypted).await.is_err() { + log::error!("Cannot exchange body"); + } + }, + Err(e) => { + log::error!("{}", e); + } + } } } diff --git a/src/types/errors.rs b/src/types/errors.rs index 6951951..7562952 100644 --- a/src/types/errors.rs +++ b/src/types/errors.rs @@ -11,5 +11,7 @@ pub enum HuskError { #[error("Cannot find local trust root")] NoLocalTrustRoot, #[error("Cannot access cert store")] - NoCertStore + NoCertStore, + #[error("No encryption keys found")] + NoEncryptionKeys, } diff --git a/src/types/mail_context.rs b/src/types/mail_context.rs index 08c9d2c..6586af8 100644 --- a/src/types/mail_context.rs +++ b/src/types/mail_context.rs @@ -23,7 +23,7 @@ pub struct MailContext<'mc> { pub recipients: Vec>, // header body: Vec, - pub body_size: usize, + pub body_size: usize, } impl<'mc> MailContext<'mc> { @@ -46,7 +46,7 @@ impl<'mc> MailContext<'mc> { recipient.certs = certs; self.recipients.push(recipient); - + Ok(()) }