diff options
author | Justus Winter <justus@sequoia-pgp.org> | 2018-09-25 16:30:04 +0200 |
---|---|---|
committer | Justus Winter <justus@sequoia-pgp.org> | 2018-09-25 16:44:14 +0200 |
commit | c2a1f81a412ea3b743cbbb4e715d6166f1338e92 (patch) | |
tree | e13478f23acc8fde918f96d4be70816a91216d91 | |
parent | d250772aff31f495cbef3f87aa3679d71aad849f (diff) |
tool: Use the new streaming decryptor.
- We now also verify signatures, add the appropriate arguments.
- Fixes #75. The Decryptor returns an error in this case.
-rw-r--r-- | tool/src/commands/decrypt.rs | 191 | ||||
-rw-r--r-- | tool/src/sq-usage.rs | 2 | ||||
-rw-r--r-- | tool/src/sq.rs | 11 | ||||
-rw-r--r-- | tool/src/sq_cli.rs | 14 |
4 files changed, 126 insertions, 92 deletions
diff --git a/tool/src/commands/decrypt.rs b/tool/src/commands/decrypt.rs index a046a729..e0542dcb 100644 --- a/tool/src/commands/decrypt.rs +++ b/tool/src/commands/decrypt.rs @@ -1,57 +1,79 @@ -use failure; +use failure::{self, ResultExt}; use std::collections::HashMap; use std::io; use rpassword; extern crate openpgp; -use openpgp::{Packet, TPK, KeyID, SecretKey, Result}; -use openpgp::packet::{Key, Signature}; -use openpgp::parse::PacketParserResult; +use openpgp::{TPK, KeyID, SecretKey, Result, mpis}; +use openpgp::packet::{self, Key, Signature}; +use openpgp::parse::PacketParser; +use openpgp::parse::stream::{ + VerificationHelper, VerificationResult, DecryptionHelper, Decryptor, +}; extern crate sequoia_store as store; -use super::{INDENT, HexDumper, dump_packet}; - -pub fn decrypt(input: &mut io::Read, output: &mut io::Write, - secrets: Vec<TPK>, dump: bool, map: bool) - -> Result<()> { - let mut keys: HashMap<KeyID, Key> = HashMap::new(); - for tsk in secrets { - let can_encrypt = |key: &Key, sig: Option<&Signature>| -> bool { - if let Some(sig) = sig { - (sig.key_flags().can_encrypt_at_rest() - || sig.key_flags().can_encrypt_for_transport()) - // Check expiry. - && sig.signature_alive() - && sig.key_alive(key) - } else { - false +use super::{INDENT, HexDumper, dump_packet, VHelper}; + +struct Helper<'a> { + vhelper: VHelper<'a>, + secret_keys: HashMap<KeyID, Key>, + dump: bool, + hex: bool, +} + +impl<'a> Helper<'a> { + fn new(store: &'a mut store::Store, + signatures: usize, tpks: Vec<TPK>, secrets: Vec<TPK>, + dump: bool, hex: bool) + -> Self { + let mut keys: HashMap<KeyID, Key> = HashMap::new(); + for tsk in secrets { + let can_encrypt = |_: &Key, sig: Option<&Signature>| -> bool { + if let Some(sig) = sig { + sig.key_flags().can_encrypt_at_rest() + || sig.key_flags().can_encrypt_for_transport() + } else { + false + } + }; + + if can_encrypt(tsk.primary(), tsk.primary_key_signature()) { + keys.insert(tsk.fingerprint().to_keyid(), tsk.primary().clone()); } - }; - if can_encrypt(tsk.primary(), tsk.primary_key_signature()) { - keys.insert(tsk.fingerprint().to_keyid(), tsk.primary().clone()); + for skb in tsk.subkeys() { + let key = skb.subkey(); + if can_encrypt(key, skb.binding_signature()) { + keys.insert(key.fingerprint().to_keyid(), key.clone()); + } + } } - for skb in tsk.subkeys() { - let key = skb.subkey(); - if can_encrypt(key, skb.binding_signature()) { - keys.insert(key.fingerprint().to_keyid(), key.clone()); - } + Helper { + vhelper: VHelper::new(store, signatures, tpks), + secret_keys: keys, + dump: dump, + hex: hex, } } +} - let mut pkesks: Vec<openpgp::packet::PKESK> = Vec::new(); - let mut skesks: Vec<openpgp::packet::SKESK> = Vec::new(); - let mut ppr - = openpgp::parse::PacketParserBuilder::from_reader(input)? - .map(map).finalize()?; +impl<'a> VerificationHelper for Helper<'a> { + fn get_public_keys(&mut self, ids: &[KeyID]) -> Result<Vec<TPK>> { + self.vhelper.get_public_keys(ids) + } + fn check(&mut self, sigs: Vec<Vec<VerificationResult>>) -> Result<()> { + self.vhelper.check(sigs) + } +} - while let PacketParserResult::Some(mut pp) = ppr { - if ! pp.possible_message() { - return Err(failure::err_msg("Malformed OpenPGP message")); - } +impl<'a> DecryptionHelper for Helper<'a> { + fn mapping(&self) -> bool { + self.hex + } - if dump || map { + fn inspect(&mut self, pp: &PacketParser) -> Result<()> { + if self.dump || self.hex { dump_packet(&mut io::stderr(), &INDENT[0..4 * pp.recursion_depth as usize], false, @@ -67,62 +89,49 @@ pub fn decrypt(input: &mut io::Read, output: &mut io::Write, eprintln!(); } - match pp.packet { - Packet::SEIP(_) => { - let mut decrypted = false; - for pkesk in pkesks.iter() { - if let Some(tsk) = keys.get(pkesk.recipient()) { - // XXX: Deal with encrypted keys. - if let Some(SecretKey::Unencrypted{ref mpis}) = - tsk.secret() - { - if let Ok((algo, key)) = pkesk.decrypt(tsk, mpis) { - let r = pp.decrypt(algo, &key[..]); - if r.is_ok() { - decrypted = true; - break; - } - } - } - } - } - if ! decrypted && ! skesks.is_empty() { - let pass = rpassword::prompt_password_stderr( - "Enter password to decrypt message: ")? - .into_bytes(); - - for skesk in skesks.iter() { - let (algo, key) = skesk.decrypt(&pass)?; - - let r = pp.decrypt(algo, &key[..]); - if r.is_ok() { - break; - } - } - } - }, - Packet::Literal(_) => { - io::copy(&mut pp, output)?; - }, - _ => (), - } + Ok(()) + } - let ((packet, _), (ppr_tmp, _)) = pp.recurse()?; - ppr = ppr_tmp; + fn get_secret_key(&mut self, keyid: &KeyID) + -> Result<Option<(packet::Key, mpis::SecretKey)>> { + let key = if let Some(key) = self.secret_keys.get(keyid) { + key + } else { + return Ok(None); + }; - match packet { - Packet::PKESK(pkesk) => pkesks.push(pkesk), - Packet::SKESK(skesk) => skesks.push(skesk), - _ => (), - } - } - if let PacketParserResult::EOF(eof) = ppr { - if eof.is_message() { - Ok(()) + // XXX: Deal with encrypted keys. + if let Some(SecretKey::Unencrypted{ref mpis}) = key.secret() { + Ok(Some((key.clone(), mpis.clone()))) } else { - Err(failure::err_msg("Malformed OpenPGP message")) + Ok(None) } - } else { - unreachable!() } + + fn get_password(&mut self) -> Result<String> { + Ok(rpassword::prompt_password_stderr( + "Enter password to decrypt message: ")?) + } +} + +pub fn decrypt(store: &mut store::Store, + input: &mut io::Read, output: &mut io::Write, + signatures: usize, tpks: Vec<TPK>, secrets: Vec<TPK>, + dump: bool, hex: bool) + -> Result<()> { + let helper = Helper::new(store, signatures, tpks, secrets, dump, hex); + let mut decryptor = Decryptor::from_reader(input, helper) + .context("Decryption failed")?; + + io::copy(&mut decryptor, output) + .map_err(|e| if e.get_ref().is_some() { + // Wrapped failure::Error. Recover it. + failure::Error::from_boxed_compat(e.into_inner().unwrap()) + } else { + // Plain io::Error. + e.into() + }).context("Decryption failed")?; + + decryptor.into_helper().vhelper.print_status(); + return Ok(()); } diff --git a/tool/src/sq-usage.rs b/tool/src/sq-usage.rs index 1c35ed1f..9fd7bb1f 100644 --- a/tool/src/sq-usage.rs +++ b/tool/src/sq-usage.rs @@ -50,7 +50,9 @@ //! //! OPTIONS: //! -o, --output <FILE> Sets the output file to use +//! --public-key-file <TPK-FILE>... Public key to verify with, given as a file (can be given multiple times) //! --secret-key-file <TSK-FILE>... Secret key to decrypt with, given as a file (can be given multiple times) +//! -n, --signatures <N> The number of valid signatures required. Default: 1 //! //! ARGS: //! <FILE> Sets the input file to use diff --git a/tool/src/sq.rs b/tool/src/sq.rs index 8c975929..10e186df 100644 --- a/tool/src/sq.rs +++ b/tool/src/sq.rs @@ -103,10 +103,19 @@ fn real_main() -> Result<(), failure::Error> { let input = open_or_stdin(m.value_of("input"))?; let mut output = create_or_stdout(m.value_of("output"))?; let mut input = openpgp::Reader::from_reader(input)?; + let signatures: usize = + m.value_of("signatures").unwrap_or("1").parse()?; + let tpks = m.values_of("public-key-file") + .map(load_tpks) + .unwrap_or(Ok(vec![]))?; let secrets = m.values_of("secret-key-file") .map(load_tpks) .unwrap_or(Ok(vec![]))?; - commands::decrypt(&mut input, &mut output, secrets, + let mut store = Store::open(&ctx, store_name) + .context("Failed to open the store")?; + commands::decrypt(&mut store, + &mut input, &mut output, + signatures, tpks, secrets, m.is_present("dump"), m.is_present("hex"))?; }, ("encrypt", Some(m)) => { diff --git a/tool/src/sq_cli.rs b/tool/src/sq_cli.rs index bba7b448..163e75fe 100644 --- a/tool/src/sq_cli.rs +++ b/tool/src/sq_cli.rs @@ -29,6 +29,20 @@ pub fn build() -> App<'static, 'static> { .long("output") .short("o") .help("Sets the output file to use")) + .arg(Arg::with_name("signatures").value_name("N") + .help("The number of valid signatures required. \ + Default: 1") + .long("signatures") + .short("n") + .takes_value(true)) + .arg(Arg::with_name("public-key-file") + .long("public-key-file") + .multiple(true) + .takes_value(true) + .value_name("TPK-FILE") + .number_of_values(1) + .help("Public key to verify with, given as a file \ + (can be given multiple times)")) .arg(Arg::with_name("secret-key-file") .long("secret-key-file") .multiple(true) |