diff --git a/src/dc_imex.rs b/src/dc_imex.rs index d252e7446..c6f2a77b4 100644 --- a/src/dc_imex.rs +++ b/src/dc_imex.rs @@ -196,8 +196,8 @@ pub fn dc_render_setup_file(context: &Context, passphrase: &str) -> Result Some(("Autocrypt-Prefer-Encrypt", "mutual")), }; let private_key_asc = private_key.to_asc(ac_headers); - let encr = dc_pgp_symm_encrypt(&passphrase, private_key_asc.as_bytes()) - .ok_or(format_err!("Failed to encrypt private key."))?; + let encr = dc_pgp_symm_encrypt(&passphrase, private_key_asc.as_bytes())?; + let replacement = format!( concat!( "-----BEGIN PGP MESSAGE-----\r\n", @@ -385,7 +385,7 @@ fn set_self_key( } pub unsafe fn dc_decrypt_setup_file( - _context: &Context, + context: &Context, passphrase: *const libc::c_char, filecontent: *const libc::c_char, ) -> *mut libc::c_char { @@ -424,12 +424,17 @@ pub unsafe fn dc_decrypt_setup_file( || binary_bytes == 0) { /* decrypt symmetrically */ - if let Some(plain) = dc_pgp_symm_decrypt( + match dc_pgp_symm_decrypt( as_str(passphrase), std::slice::from_raw_parts(binary as *const u8, binary_bytes), ) { - let payload_c = CString::new(plain).unwrap(); - payload = strdup(payload_c.as_ptr()); + Ok(plain) => { + let payload_c = CString::new(plain).unwrap(); + payload = strdup(payload_c.as_ptr()); + } + Err(err) => { + error!(context, "Failed to decrypt message: {:?}", err); + } } } } diff --git a/src/e2ee.rs b/src/e2ee.rs index 8b00c1f1e..2ae60d1d6 100644 --- a/src/e2ee.rs +++ b/src/e2ee.rs @@ -296,7 +296,7 @@ impl E2eeHelper { if (*plain).str_0.is_null() || (*plain).len <= 0 { ok_to_continue = false; } else { - if let Some(ctext_v) = dc_pgp_pk_encrypt( + if let Ok(ctext_v) = dc_pgp_pk_encrypt( std::slice::from_raw_parts( (*plain).str_0 as *const u8, (*plain).len, @@ -908,7 +908,7 @@ unsafe fn decrypt_part( }; /*if we already have fingerprints, do not add more; this ensures, only the fingerprints from the outer-most part are collected */ - if let Some(plain) = dc_pgp_pk_decrypt( + if let Ok(plain) = dc_pgp_pk_decrypt( std::slice::from_raw_parts(decoded_data as *const u8, decoded_data_bytes), &private_keyring, &public_keyring_for_validate, diff --git a/src/error.rs b/src/error.rs index 2ee35ed66..cd8a3069a 100644 --- a/src/error.rs +++ b/src/error.rs @@ -24,6 +24,8 @@ pub enum Error { Utf8(std::str::Utf8Error), #[fail(display = "{:?}", _0)] CStringError(crate::dc_tools::CStringError), + #[fail(display = "PGP: {:?}", _0)] + Pgp(pgp::errors::Error), } pub type Result = std::result::Result; @@ -70,6 +72,12 @@ impl From for Error { } } +impl From for Error { + fn from(err: pgp::errors::Error) -> Error { + Error::Pgp(err) + } +} + #[macro_export] macro_rules! bail { ($e:expr) => { diff --git a/src/pgp.rs b/src/pgp.rs index 2a86504b1..84c300ce6 100644 --- a/src/pgp.rs +++ b/src/pgp.rs @@ -12,6 +12,7 @@ use pgp::types::{CompressionAlgorithm, KeyTrait, SecretKeyTrait, StringToKey}; use rand::thread_rng; use crate::dc_tools::*; +use crate::error::Error; use crate::key::*; use crate::keyring::*; use crate::x::*; @@ -189,7 +190,7 @@ pub fn dc_pgp_pk_encrypt( plain: &[u8], public_keys_for_encryption: &Keyring, private_key_for_signing: Option<&Key>, -) -> Option { +) -> Result { let lit_msg = Message::new_literal_bytes("", plain); let pkeys: Vec<&SignedPublicKey> = public_keys_for_encryption .keys() @@ -203,9 +204,10 @@ pub fn dc_pgp_pk_encrypt( let mut rng = thread_rng(); // TODO: measure time - // TODO: better error handling let encrypted_msg = if let Some(private_key) = private_key_for_signing { - let skey: &SignedSecretKey = private_key.try_into().unwrap(); + let skey: &SignedSecretKey = private_key + .try_into() + .map_err(|_| format_err!("Invalid private key"))?; lit_msg .sign(skey, || "".into(), Default::default()) @@ -215,9 +217,10 @@ pub fn dc_pgp_pk_encrypt( lit_msg.encrypt_to_keys(&mut rng, Default::default(), &pkeys) }; - encrypted_msg - .and_then(|msg| msg.to_armored_string(None)) - .ok() + let msg = encrypted_msg?; + let encoded_msg = msg.to_armored_string(None)?; + + Ok(encoded_msg) } pub fn dc_pgp_pk_decrypt( @@ -225,77 +228,73 @@ pub fn dc_pgp_pk_decrypt( private_keys_for_decryption: &Keyring, public_keys_for_validation: &Keyring, ret_signature_fingerprints: Option<&mut HashSet>, -) -> Option> { - // TODO: proper error handling - if let Ok((msg, _)) = Message::from_armor_single(Cursor::new(ctext)) { - let skeys: Vec<&SignedSecretKey> = private_keys_for_decryption - .keys() - .iter() - .filter_map(|key| { - let k: &Key = &key; - k.try_into().ok() - }) - .collect(); +) -> Result, Error> { + let (msg, _) = Message::from_armor_single(Cursor::new(ctext))?; + let skeys: Vec<&SignedSecretKey> = private_keys_for_decryption + .keys() + .iter() + .filter_map(|key| { + let k: &Key = &key; + k.try_into().ok() + }) + .collect(); - msg.decrypt(|| "".into(), || "".into(), &skeys[..]) - .and_then(|(mut decryptor, _)| { - // TODO: how to handle the case when we detect multiple messages? - decryptor.next().expect("no message") - }) - .and_then(|dec_msg| { - if let Some(ret_signature_fingerprints) = ret_signature_fingerprints { - if !public_keys_for_validation.keys().is_empty() { - let pkeys: Vec<&SignedPublicKey> = public_keys_for_validation - .keys() - .iter() - .filter_map(|key| { - let k: &Key = &key; - k.try_into().ok() - }) - .collect(); + let (decryptor, _) = msg.decrypt(|| "".into(), || "".into(), &skeys[..])?; + let msgs = decryptor.collect::, _>>()?; + ensure!(!msgs.is_empty(), "No valid messages found"); - for pkey in &pkeys { - if dec_msg.verify(&pkey.primary_key).is_ok() { - let fp = hex::encode_upper(pkey.fingerprint()); - ret_signature_fingerprints.insert(fp); - } - } - } + let dec_msg = &msgs[0]; + + if let Some(ret_signature_fingerprints) = ret_signature_fingerprints { + if !public_keys_for_validation.keys().is_empty() { + let pkeys: Vec<&SignedPublicKey> = public_keys_for_validation + .keys() + .iter() + .filter_map(|key| { + let k: &Key = &key; + k.try_into().ok() + }) + .collect(); + + for pkey in &pkeys { + if dec_msg.verify(&pkey.primary_key).is_ok() { + let fp = hex::encode_upper(pkey.fingerprint()); + ret_signature_fingerprints.insert(fp); } - dec_msg.get_content() - }) - .ok() - .and_then(|content| content) - } else { - None + } + } + } + + match dec_msg.get_content()? { + Some(content) => Ok(content), + None => bail!("Decrypted message is empty"), } } /// Symmetric encryption. -pub fn dc_pgp_symm_encrypt(passphrase: &str, plain: &[u8]) -> Option { +pub fn dc_pgp_symm_encrypt(passphrase: &str, plain: &[u8]) -> Result { let mut rng = thread_rng(); let lit_msg = Message::new_literal_bytes("", plain); let s2k = StringToKey::new_default(&mut rng); let msg = - lit_msg.encrypt_with_password(&mut rng, s2k, Default::default(), || passphrase.into()); + lit_msg.encrypt_with_password(&mut rng, s2k, Default::default(), || passphrase.into())?; - msg.and_then(|msg| msg.to_armored_string(None)).ok() + let encoded_msg = msg.to_armored_string(None)?; + + Ok(encoded_msg) } /// Symmetric decryption. -pub fn dc_pgp_symm_decrypt(passphrase: &str, ctext: &[u8]) -> Option> { - let enc_msg = Message::from_bytes(Cursor::new(ctext)); +pub fn dc_pgp_symm_decrypt(passphrase: &str, ctext: &[u8]) -> Result, Error> { + let enc_msg = Message::from_bytes(Cursor::new(ctext))?; + let decryptor = enc_msg.decrypt_with_password(|| passphrase.into())?; - enc_msg - .and_then(|msg| { - let mut decryptor = msg.decrypt_with_password(|| passphrase.into())?; - match decryptor.next() { - Some(x) => x, - None => Err(pgp::errors::Error::InvalidInput), - } - }) - .and_then(|msg| msg.get_content()) - .ok() - .and_then(|content| content) + let msgs = decryptor.collect::, _>>()?; + ensure!(!msgs.is_empty(), "No valid messages found"); + + match msgs[0].get_content()? { + Some(content) => Ok(content), + None => bail!("Decrypted message is empty"), + } }