diff --git a/src/commands/inspect.rs b/src/commands/inspect.rs index e81cc515..56020e81 100644 --- a/src/commands/inspect.rs +++ b/src/commands/inspect.rs @@ -1,11 +1,12 @@ use std::convert::TryFrom; use std::fmt; use std::io::{self, Read}; +use std::path::Path; use std::time::Duration; use anyhow::Context; -use buffered_reader::BufferedReader; +use buffered_reader::{BufferedReader, Dup}; use sequoia_openpgp as openpgp; use openpgp::{Fingerprint, KeyHandle, Packet, Result}; @@ -15,7 +16,13 @@ use openpgp::packet::{ Signature, key::PublicParts, }; -use openpgp::parse::{Dearmor, Parse, PacketParserBuilder, PacketParserResult}; +use openpgp::parse::{ + Cookie, + Dearmor, + Parse, + PacketParserBuilder, + PacketParserResult, +}; use openpgp::policy::{Policy, HashAlgoSecurity}; use openpgp::packet::key::SecretKeyMaterial; use openpgp::types::{ @@ -106,7 +113,8 @@ pub fn inspect<'a, R>(sq: &mut Sq, print_certifications: bool, dump_bad_signatures: bool) -> Result -where R: BufferedReader + 'a, +where + R: BufferedReader + 'a, { let mut ppr = openpgp::parse::PacketParser::from_buffered_reader(input)?; let mut type_called = None; // Did we print the type yet? @@ -849,3 +857,176 @@ impl fmt::Display for Kind { } } } + +impl Kind { + /// Identifies OpenPGP data. + /// + /// Returns the kind and the original reader without any data + /// consumed. + pub fn identify<'a, T>(sq: &mut Sq, input: T) + -> Result<(Kind, Box + 'a>)> + where + T: BufferedReader + 'a, + { + let mut sink: Box = + Box::new(io::Sink::default()); + let mut dup = Dup::with_cookie(input, Default::default()); + let kind = inspect(sq, &mut dup, None, &mut sink, false, false)?; + Ok((kind, dup.into_boxed().into_inner().unwrap())) + } + + /// Checks that `self` matches `expected`, or prints hints on what + /// to do instead and returns an error. + pub fn expect_or_else(&self, + sq: &Sq, + command: &str, + expected: Kind, + input_arg: &str, + input_path: Option<&Path>) + -> Result<()> + { + if self != &expected { + let input_path_text = + input_path.as_ref().map(|p| p.display().to_string()) + .unwrap_or_else(|| "stdin".into()); + let input_path_arg = + input_path.map(|p| p.display().to_string()) + .unwrap_or_else(|| "-".into()); + + let msg = format!( + "Expected {} for {}, but {} is {}.", + expected, input_arg, input_path_text, self); + let mut hint = sq.hint(format_args!("{}", msg)); + + match self { + Kind::Cert => { + if command == "verify" { + hint = hint.hint(format_args!( + "To verify a message or signature using {}:", + input_path_text)) + .sq().arg("verify") + .arg_value("--signer-file", &input_path_arg) + .done(); + } + + if command == "decrypt" { + hint = hint.hint(format_args!( + "To verify a message using {}:", + input_path_text)) + .sq().arg("decrypt") + .arg_value("--signer-file", &input_path_arg) + .done(); + } + + hint.hint(format_args!( + "To import the cert {}:", input_path_text)) + .sq().arg("cert").arg("import") + .arg(&input_path_arg) + .done(); + }, + + Kind::Key => { + if command == "verify" { + hint = hint.hint(format_args!( + "To verify a message or signature using {}:", + input_path_text)) + .sq().arg("verify") + .arg_value("--signer-file", &input_path_arg) + .done(); + } + + if command == "decrypt" { + hint = hint.hint(format_args!( + "To verify the signature on an encrypted message \ + using {}:", + input_path_text)) + .sq().arg("decrypt") + .arg_value("--signer-file", &input_path_arg) + .done(); + + hint = hint.hint(format_args!( + "To decrypt an encrypted message using {}:", + input_path_text)) + .sq().arg("decrypt") + .arg_value("--recipient-file", &input_path_arg) + .done(); + } + + hint.hint(format_args!( + "To import the key {}:", input_path_text)) + .sq().arg("key").arg("import") + .arg(&input_path_arg) + .done(); + }, + + Kind::Keyring => { + hint.hint(format_args!( + "To import the certificates in {}:", input_path_text)) + .sq().arg("cert").arg("import") + .arg(&input_path_arg) + .done(); + }, + + Kind::SignedMessage => { + hint.hint(format_args!( + "To verify a signed message:")) + .sq().arg("verify") + .arg(&input_path_arg) + .done(); + }, + + Kind::EncryptedMessage => { + hint.hint(format_args!( + "To decrypt an encrypted message:")) + .sq().arg("decrypt") + .arg(input_path_arg) + .done(); + }, + + Kind::DetachedSig => { + hint.hint(format_args!( + "To verify the detached signature {}:", + input_path_text)) + .sq().arg("verify") + .arg("--signature-file").arg(&input_path_arg) + .arg("the-data-file") + .done(); + }, + + Kind::RevocationCert => { + hint.hint(format_args!( + "To import the revocation certificate {}:", + input_path_text)) + .sq().arg("cert").arg("import") + .arg(&input_path_arg) + .done(); + }, + + Kind::Unknown => { + hint.hint(format_args!( + "To inspect the packet sequence in {}:", + input_path_text)) + .sq().arg("toolbox").arg("packet").arg("dump") + .arg(&input_path_arg) + .done(); + }, + + Kind::NotOpenPGP => { + if command == "verify" { + hint.hint(format_args!( + "To verify the detached signature \ + over the data in {}:", input_path_text)) + .sq().arg("verify") + .arg("--signature-file").arg("the-signature-file") + .arg(&input_path_arg) + .done(); + } + }, + } + + Err(anyhow::anyhow!("{}", msg)) + } else { + Ok(()) + } + } +} diff --git a/src/commands/verify.rs b/src/commands/verify.rs index 574666a9..e1dc9fa0 100644 --- a/src/commands/verify.rs +++ b/src/commands/verify.rs @@ -1,8 +1,10 @@ use std::io; -use std::fs::File; +use std::path::PathBuf; use anyhow::Context; +use buffered_reader::File; + use sequoia_openpgp as openpgp; use openpgp::Cert; use openpgp::types::KeyFlags; @@ -14,6 +16,7 @@ use crate::Sq; use crate::Result; use crate::cli; use crate::commands::VHelper; +use crate::commands::inspect::Kind; use crate::load_certs; pub fn dispatch(sq: Sq, command: cli::verify::Command) @@ -23,11 +26,6 @@ pub fn dispatch(sq: Sq, command: cli::verify::Command) let mut input = command.input.open()?; let mut output = command.output.create_safe(&sq)?; - let mut detached = if let Some(f) = command.detached { - Some(File::open(f)?) - } else { - None - }; let signatures = command.signatures; // TODO ugly adaptation to load_certs' signature, fix later let mut certs = load_certs( @@ -39,18 +37,30 @@ pub fn dispatch(sq: Sq, command: cli::verify::Command) false) .context("--sender-cert")?); verify(sq, &mut input, - detached.as_mut().map(|r| r as &mut (dyn io::Read + Sync + Send)), + command.detached, &mut output, signatures, certs)?; Ok(()) } -pub fn verify(sq: Sq, +pub fn verify(mut sq: Sq, input: &mut (dyn io::Read + Sync + Send), - detached: Option<&mut (dyn io::Read + Sync + Send)>, + detached: Option, output: &mut dyn io::Write, signatures: usize, certs: Vec) -> Result<()> { + let detached = if let Some(sig_path) = detached { + let sig = File::with_cookie(&sig_path, Default::default())?; + + let (kind, sig) = Kind::identify(&mut sq, sig)?; + kind.expect_or_else(&sq, "verify", Kind::DetachedSig, + "--signature-file", Some(&sig_path))?; + + Some(sig) + } else { + None + }; + let helper = VHelper::new(&sq, signatures, certs); let helper = if let Some(dsig) = detached { let mut v = DetachedVerifierBuilder::from_reader(dsig)?