summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sq/src/commands/dump.rs30
-rw-r--r--sq/src/sq.rs30
-rw-r--r--sq/tests/data/messages/rsa.msg.pgpbin0 -> 463 bytes
-rw-r--r--sq/tests/sq-packet-dump.rs121
4 files changed, 160 insertions, 21 deletions
diff --git a/sq/src/commands/dump.rs b/sq/src/commands/dump.rs
index 396377ba..d11bcf56 100644
--- a/sq/src/commands/dump.rs
+++ b/sq/src/commands/dump.rs
@@ -56,6 +56,7 @@ impl Convert<chrono::DateTime<chrono::offset::Utc>> for Timestamp {
pub fn dump<W>(input: &mut (dyn io::Read + Sync + Send),
output: &mut dyn io::Write,
mpis: bool, hex: bool, sk: Option<&SessionKey>,
+ algo_hint: Option<SymmetricAlgorithm>,
width: W)
-> Result<Kind>
where W: Into<Option<usize>>
@@ -85,26 +86,29 @@ pub fn dump<W>(input: &mut (dyn io::Read + Sync + Send),
Packet::SEIP(_) if sk.is_some() => {
message_encrypted = true;
let sk = sk.as_ref().unwrap();
- let mut decrypted_with = None;
- for algo in 1..20 {
- let algo = SymmetricAlgorithm::from(algo);
- if let Ok(size) = algo.key_size() {
- if size != sk.len() { continue; }
- } else {
- continue;
- }
+ let decrypted_with = if let Some(algo) = algo_hint {
+ // We know which algorithm to use, so only try decrypting
+ // with that one.
+ pp.decrypt(algo, sk).is_ok().then(|| algo)
+ } else {
+ // We don't know which algorithm to use,
+ // try to find one that decrypts the message.
+ (1u8..=19)
+ .map(SymmetricAlgorithm::from)
+ .find(|algo| pp.decrypt(*algo, sk).is_ok())
+ };
- if let Ok(_) = pp.decrypt(algo, sk) {
- decrypted_with = Some(algo);
- break;
- }
- }
let mut fields = Vec::new();
fields.push(format!("Session key: {}", hex::encode(sk)));
if let Some(algo) = decrypted_with {
fields.push(format!("Symmetric algo: {}", algo));
fields.push("Decryption successful".into());
} else {
+ if let Some(algo) = algo_hint {
+ fields.push(format!(
+ "Indicated Symmetric algo: {}", algo
+ ));
+ };
fields.push("Decryption failed".into());
}
Some(fields)
diff --git a/sq/src/sq.rs b/sq/src/sq.rs
index 4305f164..9cf1eceb 100644
--- a/sq/src/sq.rs
+++ b/sq/src/sq.rs
@@ -15,9 +15,9 @@ use openpgp::{
Result,
};
use crate::openpgp::{armor, Cert};
-use crate::openpgp::crypto::Password;
+use crate::openpgp::crypto::{Password, SessionKey};
use crate::openpgp::fmt::hex;
-use crate::openpgp::types::KeyFlags;
+use crate::openpgp::types::{KeyFlags, SymmetricAlgorithm};
use crate::openpgp::packet::prelude::*;
use crate::openpgp::parse::{Parse, PacketParser, PacketParserResult};
use crate::openpgp::packet::signature::subpacket::NotationData;
@@ -660,20 +660,34 @@ fn main() -> Result<()> {
let mut input = open_or_stdin(m.value_of("input"))?;
let mut output =
config.create_or_stdout_unsafe(m.value_of("output"))?;
- let session_key: Option<openpgp::crypto::SessionKey> =
+
+ fn decode_session_key(
+ sk: &str,
+ ) -> Result<(Option<SessionKey>, Option<SymmetricAlgorithm>)> {
+ if let Some((algo, sk)) = sk.split_once(':') {
+ let algo = SymmetricAlgorithm::from(algo.parse::<u8>()?);
+ let dsk = hex::decode_pretty(sk)?.into();
+ Ok((Some(dsk), Some(algo)))
+ } else {
+ let dsk = hex::decode_pretty(sk)?.into();
+ Ok((Some(dsk), None))
+ }
+ }
+
+ let (session_key, algo_hint) =
if let Some(sk) = m.value_of("session-key") {
- let dsk = hex::decode_pretty(sk).with_context(|| format!(
+ decode_session_key(sk)
+ .with_context(|| format!(
"Bad value passed to --session-key: {:?}",
sk
- ))?;
- Some(dsk.into())
+ ))?
} else {
- None
+ (None, None)
};
let width = term_size::dimensions_stdout().map(|(w, _)| w);
commands::dump(&mut input, &mut output,
m.is_present("mpis"), m.is_present("hex"),
- session_key.as_ref(), width)?;
+ session_key.as_ref(), algo_hint, width)?;
},
Some(("decrypt", m)) => {
diff --git a/sq/tests/data/messages/rsa.msg.pgp b/sq/tests/data/messages/rsa.msg.pgp
new file mode 100644
index 00000000..1d820827
--- /dev/null
+++ b/sq/tests/data/messages/rsa.msg.pgp
Binary files differ
diff --git a/sq/tests/sq-packet-dump.rs b/sq/tests/sq-packet-dump.rs
new file mode 100644
index 00000000..d431d995
--- /dev/null
+++ b/sq/tests/sq-packet-dump.rs
@@ -0,0 +1,121 @@
+#[cfg(test)]
+mod sq_packet_dump {
+ use assert_cmd::Command;
+ use predicates::prelude::*;
+
+ use openpgp::Result;
+ use sequoia_openpgp as openpgp;
+
+ fn artifact(filename: &str) -> String {
+ format!("tests/data/{}", filename)
+ }
+
+ #[test]
+ fn session_key_without_prefix() -> Result<()> {
+ Command::cargo_bin("sq")
+ .unwrap()
+ .arg("packet")
+ .arg("dump")
+ .args(["--session-key", "1FE820EC21FB5D7E33D83367106D1D3747DCD48E6320C1AEC57EE7D18FC437D4"])
+ .arg(artifact("messages/rsa.msg.pgp"))
+ .assert()
+ .success()
+ .stdout(predicate::str::contains("Decryption failed").not());
+ Ok(())
+ }
+
+ #[test]
+ fn session_key_with_prefix() -> Result<()> {
+ Command::cargo_bin("sq")
+ .unwrap()
+ .arg("packet")
+ .arg("dump")
+ .args(["--session-key", "9:1FE820EC21FB5D7E33D83367106D1D3747DCD48E6320C1AEC57EE7D18FC437D4"])
+ .arg(artifact("messages/rsa.msg.pgp"))
+ .assert()
+ .success()
+ .stdout(predicate::str::contains("Decryption failed").not());
+ Ok(())
+ }
+
+ #[test]
+ fn session_key_with_bad_prefix() -> Result<()> {
+ Command::cargo_bin("sq")
+ .unwrap()
+ .arg("packet")
+ .arg("dump")
+ .args(["--session-key", "1:1FE820EC21FB5D7E33D83367106D1D3747DCD48E6320C1AEC57EE7D18FC437D4"])
+ .arg(artifact("messages/rsa.msg.pgp"))
+ .assert()
+ .success()
+ .stdout(predicate::str::contains("Indicated Symmetric algo: IDEA"))
+ .stdout(predicate::str::contains("Decryption failed"));
+ Ok(())
+ }
+
+ #[test]
+ fn session_key_wrong_length_without_prefix() -> Result<()> {
+ // too short
+ Command::cargo_bin("sq")
+ .unwrap()
+ .arg("packet")
+ .arg("dump")
+ .args(["--session-key", "1FE820EC21FB5D7E33D83367106D1D3747DCD48E6320C1AEC57EE7D18FC437"])
+ .arg(artifact("messages/rsa.msg.pgp"))
+ .assert()
+ .success()
+ .stdout(predicate::str::contains("Decryption failed"));
+
+ // too long
+ Command::cargo_bin("sq")
+ .unwrap()
+ .arg("packet")
+ .arg("dump")
+ .args(["--session-key", "1FE820EC21FB5D7E33D83367106D1D3747DCD48E6320C1AEC57EE7D18FC437D4AB"])
+ .arg(artifact("messages/rsa.msg.pgp"))
+ .assert()
+ .success()
+ .stdout(predicate::str::contains("Decryption failed"));
+ Ok(())
+ }
+
+ #[test]
+ fn session_key_wrong_length_with_prefix() -> Result<()> {
+ // too short
+ Command::cargo_bin("sq")
+ .unwrap()
+ .arg("packet")
+ .arg("dump")
+ .args(["--session-key", "1:1FE820EC21FB5D7E33D83367106D1D3747DCD48E6320C1AEC57EE7D18FC437"])
+ .arg(artifact("messages/rsa.msg.pgp"))
+ .assert()
+ .success()
+ .stdout(predicate::str::contains("Decryption failed"));
+
+ // too long
+ Command::cargo_bin("sq")
+ .unwrap()
+ .arg("packet")
+ .arg("dump")
+ .args(["--session-key", "1:1FE820EC21FB5D7E33D83367106D1D3747DCD48E6320C1AEC57EE7D18FC437D4AB"])
+ .arg(artifact("messages/rsa.msg.pgp"))
+ .assert()
+ .success()
+ .stdout(predicate::str::contains("Decryption failed"));
+ Ok(())
+ }
+
+ #[test]
+ fn session_key_wrong_key_with_prefix() -> Result<()> {
+ Command::cargo_bin("sq")
+ .unwrap()
+ .arg("packet")
+ .arg("dump")
+ .args(["--session-key", "9:BB9CCB8EDE22DC222C83BD1C63AEB97335DDC7B696DB171BD16EAA5784CC0478"])
+ .arg(artifact("messages/rsa.msg.pgp"))
+ .assert()
+ .success()
+ .stdout(predicate::str::contains("Decryption failed"));
+ Ok(())
+ }
+}