diff options
author | Justus Winter <justus@sequoia-pgp.org> | 2019-03-18 16:33:51 +0100 |
---|---|---|
committer | Justus Winter <justus@sequoia-pgp.org> | 2019-03-25 19:00:49 +0100 |
commit | aa3004990403860a9a0261354e384e5ce8bdf1ed (patch) | |
tree | d59aeb8cc760a7adc8c4e7c0189182eb76f74822 /tool/src/commands/decrypt.rs | |
parent | f572bc851d520a56eebb0a6cea38144051168a8a (diff) |
openpgp: Rework the DecryptionHelper trait.
- Simplify the protocol by removing the iteration. Instead, the
callee gets a proxy for PacketParser::decrypt() that she can use
to decrypt the message. If successful, the session key can be
cached without involving the DecryptionHelper trait. This also
allows us to dump session keys.
- Fixes #219.
Diffstat (limited to 'tool/src/commands/decrypt.rs')
-rw-r--r-- | tool/src/commands/decrypt.rs | 156 |
1 files changed, 68 insertions, 88 deletions
diff --git a/tool/src/commands/decrypt.rs b/tool/src/commands/decrypt.rs index f128bb17..916071a0 100644 --- a/tool/src/commands/decrypt.rs +++ b/tool/src/commands/decrypt.rs @@ -5,11 +5,13 @@ use rpassword; extern crate sequoia_openpgp as openpgp; use sequoia_core::Context; +use openpgp::constants::SymmetricAlgorithm; +use openpgp::crypto::SessionKey; use openpgp::{Fingerprint, TPK, KeyID, Result}; use openpgp::packet::{Key, key::SecretKey, Signature, PKESK, SKESK}; use openpgp::parse::PacketParser; use openpgp::parse::stream::{ - VerificationHelper, VerificationResult, DecryptionHelper, Decryptor, Secret, + VerificationHelper, VerificationResult, DecryptionHelper, Decryptor, }; extern crate sequoia_store as store; @@ -22,19 +24,6 @@ struct Helper<'a> { key_hints: HashMap<KeyID, String>, dumper: Option<PacketDumper>, hex: bool, - pass: Pass, -} - -enum Pass { - UnencryptedKey(usize), - EncryptedKey(usize), - Passwords, -} - -impl Default for Pass { - fn default() -> Self { - Pass::UnencryptedKey(0) - } } impl<'a> Helper<'a> { @@ -90,7 +79,6 @@ impl<'a> Helper<'a> { None }, hex: hex, - pass: Pass::default(), } } } @@ -119,87 +107,79 @@ impl<'a> DecryptionHelper for Helper<'a> { Ok(()) } - fn get_secret(&mut self, pkesks: &[&PKESK], skesks: &[&SKESK]) - -> Result<Option<Secret>> { - loop { - self.pass = match self.pass { - Pass::UnencryptedKey(ref mut i) => { - while let Some(pkesk) = pkesks.get(*i) { - *i += 1; - let keyid = pkesk.recipient(); - let key = if let Some(key) = self.secret_keys.get(keyid) - { - key - } else { - continue; - }; - - if let Some(SecretKey::Unencrypted { ref mpis }) = - key.secret() - { - return Ok(Some(Secret::Asymmetric { - identity: self.key_identities.get(keyid) - .unwrap().clone(), - key: key.clone(), - secret: mpis.clone(), - })) - } + fn decrypt<D>(&mut self, pkesks: &[PKESK], skesks: &[SKESK], + mut decrypt: D) -> openpgp::Result<Option<Fingerprint>> + where D: FnMut(SymmetricAlgorithm, &SessionKey) -> openpgp::Result<()> + { + // First, we try those keys that we can use without prompting + // for a password. + for pkesk in pkesks { + let keyid = pkesk.recipient(); + if let Some(key) = self.secret_keys.get(&keyid) { + if let Some(SecretKey::Unencrypted { mpis }) = key.secret() { + if let Ok(_) = pkesks[0].decrypt(key, mpis) + .and_then(|(algo, sk)| decrypt(algo, &sk)) + { + return Ok(self.key_identities.get(keyid) + .map(|fp| fp.clone())); } + } + } + } + + // Second, we try those keys that are encrypted. + for pkesk in pkesks { + let keyid = pkesk.recipient(); + if let Some(key) = self.secret_keys.get(&keyid) { + if key.secret().map(|s| ! s.is_encrypted()) + .unwrap_or(true) + { + continue; + } - Pass::EncryptedKey(0) - }, - - Pass::EncryptedKey(ref mut i) => { - while let Some(pkesk) = pkesks.get(*i) { - *i += 1; - let keyid = pkesk.recipient(); - let key = if let Some(key) = self.secret_keys.get(keyid) { - key - } else { - continue; - }; - - if key.secret().map(|s| s.is_encrypted()) - .unwrap_or(false) + loop { + let p = rpassword::prompt_password_stderr( + &format!( + "Enter password to decrypt key {}: ", + self.key_hints.get(&keyid).unwrap())) + ?.into(); + + if let Ok(mpis) = + key.secret().unwrap().decrypt(key.pk_algo(), &p) + { + if let Ok(_) = pkesk.decrypt(key, &mpis) + .and_then(|(algo, sk)| decrypt(algo, sk)) { - loop { - let p = rpassword::prompt_password_stderr( - &format!( - "Enter password to decrypt key {}: ", - self.key_hints.get(keyid).unwrap()))? - .into(); - - if let Ok(mpis) = - key.secret().unwrap() - .decrypt(key.pk_algo(), &p) - { - return Ok(Some(Secret::Asymmetric { - identity: self.key_identities.get(keyid) - .unwrap().clone(), - key: key.clone(), - secret: mpis, - })); - } - - eprintln!("Bad password."); - } + return Ok(self.key_identities.get(keyid) + .map(|fp| fp.clone())); } + + eprintln!("Bad password."); } + } + } + } - Pass::Passwords - }, + if skesks.is_empty() { + return + Err(failure::err_msg("No key to decrypt message")); + } - Pass::Passwords => { - if skesks.is_empty() { - return - Err(failure::err_msg("No key to decrypt message")); - } - return Ok(Some(Secret::Symmetric { - password: rpassword::prompt_password_stderr( - "Enter password to decrypt message: ")?.into(), - })); - }, + // Finally, try to decrypt using the SKESKs. + loop { + let password = + rpassword::prompt_password_stderr( + "Enter password to decrypt message: ")?.into(); + + for skesk in skesks { + if let Ok(_) = skesk.decrypt(&password) + .and_then(|(algo, sk)| decrypt(algo, &sk)) + { + return Ok(None); + } } + + eprintln!("Bad password."); } } } |