diff options
-rw-r--r-- | sq/src/commands/dump.rs | 30 | ||||
-rw-r--r-- | sq/src/sq.rs | 30 | ||||
-rw-r--r-- | sq/tests/data/messages/rsa.msg.pgp | bin | 0 -> 463 bytes | |||
-rw-r--r-- | sq/tests/sq-packet-dump.rs | 121 |
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 Binary files differnew file mode 100644 index 00000000..1d820827 --- /dev/null +++ b/sq/tests/data/messages/rsa.msg.pgp 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(()) + } +} |