summaryrefslogtreecommitdiffstats
path: root/tool
diff options
context:
space:
mode:
authorJustus Winter <justus@sequoia-pgp.org>2019-10-04 15:31:28 +0200
committerJustus Winter <justus@sequoia-pgp.org>2019-12-06 15:11:54 +0100
commit30b37711392468161817653211754601f4f52057 (patch)
tree4060e00c6a3a9d18ec759e95d954a77f389f7a57 /tool
parent149f252e5d1f6e30869f7981622322ab345cd2fc (diff)
tool: Add packet decrypt that unwraps encryption containers.
Diffstat (limited to 'tool')
-rw-r--r--tool/src/commands/decrypt.rs54
-rw-r--r--tool/src/commands/mod.rs2
-rw-r--r--tool/src/sq-usage.rs32
-rw-r--r--tool/src/sq.rs22
-rw-r--r--tool/src/sq_cli.rs30
5 files changed, 134 insertions, 6 deletions
diff --git a/tool/src/commands/decrypt.rs b/tool/src/commands/decrypt.rs
index f04b6ac1..2e2753f6 100644
--- a/tool/src/commands/decrypt.rs
+++ b/tool/src/commands/decrypt.rs
@@ -10,8 +10,13 @@ use crate::openpgp::types::SymmetricAlgorithm;
use crate::openpgp::fmt::hex;
use crate::openpgp::crypto::{self, SessionKey};
use crate::openpgp::{Fingerprint, Cert, KeyID, Result};
+use crate::openpgp::packet;
use crate::openpgp::packet::prelude::*;
-use crate::openpgp::parse::PacketParser;
+use crate::openpgp::parse::{
+ Parse,
+ PacketParser,
+ PacketParserResult,
+};
use crate::openpgp::parse::stream::{
VerificationHelper, DecryptionHelper, Decryptor, MessageStructure,
};
@@ -319,3 +324,50 @@ pub fn decrypt(ctx: &Context, mapping: &mut store::Mapping,
helper.vhelper.print_status();
return Ok(());
}
+
+pub fn decrypt_unwrap(ctx: &Context, mapping: &mut store::Mapping,
+ input: &mut dyn io::Read, output: &mut dyn io::Write,
+ secrets: Vec<Cert>, dump_session_key: bool)
+ -> Result<()> {
+ let mut helper = Helper::new(ctx, mapping, 0, Vec::new(), secrets,
+ dump_session_key, false, false);
+
+ let mut ppr = PacketParser::from_reader(input)?;
+
+ let mut pkesks: Vec<packet::PKESK> = Vec::new();
+ let mut skesks: Vec<packet::SKESK> = Vec::new();
+ while let PacketParserResult::Some(mut pp) = ppr {
+ match pp.packet {
+ Packet::SEIP(_) | Packet::AED(_) => {
+ {
+ let decrypt =
+ |algo, secret: &SessionKey| pp.decrypt(algo, secret);
+ helper.decrypt(&pkesks[..], &skesks[..], decrypt)?;
+ }
+ if ! pp.decrypted() {
+ // XXX: That is not quite the right error to return.
+ return Err(
+ openpgp::Error::InvalidSessionKey(
+ "No session key".into()).into());
+ }
+
+ io::copy(&mut pp, output)?;
+ return Ok(());
+ },
+ Packet::MDC(ref mdc) => if ! mdc.valid() {
+ return Err(openpgp::Error::ManipulatedMessage.into());
+ },
+ _ => (),
+ }
+
+ let (p, ppr_tmp) = pp.recurse()?;
+ match p {
+ Packet::PKESK(pkesk) => pkesks.push(pkesk),
+ Packet::SKESK(skesk) => skesks.push(skesk),
+ _ => (),
+ }
+ ppr = ppr_tmp;
+ }
+
+ Ok(())
+}
diff --git a/tool/src/commands/mod.rs b/tool/src/commands/mod.rs
index 37254db2..3ecd65ca 100644
--- a/tool/src/commands/mod.rs
+++ b/tool/src/commands/mod.rs
@@ -31,7 +31,7 @@ use crate::openpgp::serialize::padding::{
};
extern crate sequoia_store as store;
-mod decrypt;
+pub mod decrypt;
pub use self::decrypt::decrypt;
mod sign;
pub use self::sign::sign;
diff --git a/tool/src/sq-usage.rs b/tool/src/sq-usage.rs
index 19607bb1..be4b3695 100644
--- a/tool/src/sq-usage.rs
+++ b/tool/src/sq-usage.rs
@@ -589,10 +589,34 @@
//! -V, --version Prints version information
//!
//! SUBCOMMANDS:
-//! dump Lists OpenPGP packets
-//! help Prints this message or the help of the given subcommand(s)
-//! join Joins OpenPGP packets split across files
-//! split Splits a message into OpenPGP packets
+//! decrypt Decrypts an OpenPGP message, dumping the content of the encryption container without further
+//! processing
+//! dump Lists OpenPGP packets
+//! help Prints this message or the help of the given subcommand(s)
+//! join Joins OpenPGP packets split across files
+//! split Splits a message into OpenPGP packets
+//! ```
+//!
+//! ### Subcommand packet decrypt
+//!
+//! ```text
+//! Decrypts an OpenPGP message, dumping the content of the encryption container without further processing
+//!
+//! USAGE:
+//! sq packet decrypt [FLAGS] [OPTIONS] [--] [FILE]
+//!
+//! FLAGS:
+//! -B, --binary Don't ASCII-armor encode the OpenPGP data
+//! --dump-session-key Prints the session key to stderr
+//! -h, --help Prints help information
+//! -V, --version Prints version information
+//!
+//! OPTIONS:
+//! -o, --output <FILE> Sets the output file to use
+//! --secret-key-file <TSK-FILE>... Secret key to decrypt with, given as a file (can be given multiple times)
+//!
+//! ARGS:
+//! <FILE> Sets the input file to use
//! ```
//!
//! ### Subcommand packet dump
diff --git a/tool/src/sq.rs b/tool/src/sq.rs
index 055cf642..950ce27a 100644
--- a/tool/src/sq.rs
+++ b/tool/src/sq.rs
@@ -332,6 +332,28 @@ fn real_main() -> Result<(), failure::Error> {
m.is_present("mpis"), m.is_present("hex"),
session_key.as_ref(), width)?;
},
+
+ ("decrypt", Some(m)) => {
+ let mut input = open_or_stdin(m.value_of("input"))?;
+ let output = create_or_stdout(m.value_of("output"), force)?;
+ let mut output = if ! m.is_present("binary") {
+ Box::new(armor::Writer::new(output,
+ armor::Kind::Message,
+ &[])?)
+ } else {
+ output
+ };
+ let secrets = m.values_of("secret-key-file")
+ .map(load_certs)
+ .unwrap_or(Ok(vec![]))?;
+ let mut mapping = Mapping::open(&ctx, realm_name, mapping_name)
+ .context("Failed to open the mapping")?;
+ commands::decrypt::decrypt_unwrap(
+ &ctx, &mut mapping,
+ &mut input, &mut output,
+ secrets, m.is_present("dump-session-key"))?;
+ },
+
("split", Some(m)) => {
let mut input = open_or_stdin(m.value_of("input"))?;
let prefix =
diff --git a/tool/src/sq_cli.rs b/tool/src/sq_cli.rs
index d247e6d7..06ccde14 100644
--- a/tool/src/sq_cli.rs
+++ b/tool/src/sq_cli.rs
@@ -450,6 +450,36 @@ pub fn build() -> App<'static, 'static> {
.long("hex")
.short("x")
.help("Print a hexdump")))
+
+ .subcommand(SubCommand::with_name("decrypt")
+ .display_order(10)
+ .about("Decrypts an OpenPGP message, dumping \
+ the content of the encryption \
+ container without further processing")
+ .arg(Arg::with_name("input").value_name("FILE")
+ .help("Sets the input file to use"))
+ .arg(Arg::with_name("output").value_name("FILE")
+ .long("output")
+ .short("o")
+ .help("Sets the output file to use"))
+ .arg(Arg::with_name("binary")
+ .long("binary")
+ .short("B")
+ .help("Don't ASCII-armor encode the \
+ OpenPGP data"))
+ .arg(Arg::with_name("secret-key-file")
+ .long("secret-key-file")
+ .multiple(true)
+ .takes_value(true)
+ .value_name("TSK-FILE")
+ .number_of_values(1)
+ .help("Secret key to decrypt with, given \
+ as a file \
+ (can be given multiple times)"))
+ .arg(Arg::with_name("dump-session-key")
+ .long("dump-session-key")
+ .help("Prints the session key to stderr")))
+
.subcommand(SubCommand::with_name("split")
.about("Splits a message into OpenPGP packets")
.arg(Arg::with_name("input").value_name("FILE")