summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJustus Winter <justus@sequoia-pgp.org>2018-09-25 16:30:04 +0200
committerJustus Winter <justus@sequoia-pgp.org>2018-09-25 16:44:14 +0200
commitc2a1f81a412ea3b743cbbb4e715d6166f1338e92 (patch)
treee13478f23acc8fde918f96d4be70816a91216d91
parentd250772aff31f495cbef3f87aa3679d71aad849f (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.rs191
-rw-r--r--tool/src/sq-usage.rs2
-rw-r--r--tool/src/sq.rs11
-rw-r--r--tool/src/sq_cli.rs14
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)