827 lines
35 KiB
Rust
827 lines
35 KiB
Rust
![]() |
// from https://gitlab.com/sequoia-pgp/dump.sequoia-pgp.org/blob/master/src/dump.rs
|
||
|
// plus *very* slight adaptions for seqoia 0.8
|
||
|
|
||
|
use std::io::{self, Read};
|
||
|
|
||
|
use sequoia_openpgp::constants::SymmetricAlgorithm;
|
||
|
use sequoia_openpgp::conversions::hex;
|
||
|
use sequoia_openpgp::{Packet, Result};
|
||
|
use sequoia_openpgp::packet::ctb::CTB;
|
||
|
use sequoia_openpgp::packet::{Header, BodyLength, Signature};
|
||
|
use sequoia_openpgp::packet::signature::subpacket::{Subpacket, SubpacketValue};
|
||
|
use sequoia_openpgp::crypto::{SessionKey, s2k::S2K};
|
||
|
use sequoia_openpgp::parse::{map::Map, Parse, PacketParserResult};
|
||
|
|
||
|
const TIMEFMT: &'static str = "%Y-%m-%dT%H:%M";
|
||
|
|
||
|
#[derive(Debug)]
|
||
|
pub enum Kind {
|
||
|
Message {
|
||
|
encrypted: bool,
|
||
|
},
|
||
|
Keyring,
|
||
|
TPK,
|
||
|
Unknown,
|
||
|
}
|
||
|
|
||
|
pub fn dump(input: &mut dyn io::Read, output: &mut dyn io::Write, mpis: bool, hex: bool,
|
||
|
sk: Option<&SessionKey>)
|
||
|
-> Result<Kind> {
|
||
|
let mut ppr
|
||
|
= sequoia_openpgp::parse::PacketParserBuilder::from_reader(input)?
|
||
|
.map(hex).finalize()?;
|
||
|
let mut message_encrypted = false;
|
||
|
let width = 32 * 4 + 80;
|
||
|
let mut dumper = PacketDumper::new(width, mpis);
|
||
|
|
||
|
while let PacketParserResult::Some(mut pp) = ppr {
|
||
|
let additional_fields = match pp.packet {
|
||
|
Packet::Literal(_) => {
|
||
|
let mut prefix = vec![0; 40];
|
||
|
let n = pp.read(&mut prefix)?;
|
||
|
Some(vec![
|
||
|
format!("Content: {:?}{}",
|
||
|
String::from_utf8_lossy(&prefix[..n]),
|
||
|
if n == prefix.len() { "..." } else { "" }),
|
||
|
])
|
||
|
},
|
||
|
Packet::SEIP(_) if sk.is_none() => {
|
||
|
message_encrypted = true;
|
||
|
Some(vec!["No session key supplied".into()])
|
||
|
}
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
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 {
|
||
|
fields.push("Decryption failed".into());
|
||
|
}
|
||
|
Some(fields)
|
||
|
},
|
||
|
Packet::AED(_) if sk.is_none() => {
|
||
|
message_encrypted = true;
|
||
|
Some(vec!["No session key supplied".into()])
|
||
|
}
|
||
|
Packet::AED(_) if sk.is_some() => {
|
||
|
message_encrypted = true;
|
||
|
let sk = sk.as_ref().unwrap();
|
||
|
let algo = if let Packet::AED(ref aed) = pp.packet {
|
||
|
aed.symmetric_algo()
|
||
|
} else {
|
||
|
unreachable!()
|
||
|
};
|
||
|
|
||
|
let _ = pp.decrypt(algo, sk);
|
||
|
|
||
|
let mut fields = Vec::new();
|
||
|
fields.push(format!("Session key: {}", hex::encode(sk)));
|
||
|
if pp.decrypted() {
|
||
|
fields.push("Decryption successful".into());
|
||
|
} else {
|
||
|
fields.push("Decryption failed".into());
|
||
|
}
|
||
|
Some(fields)
|
||
|
},
|
||
|
_ => None,
|
||
|
};
|
||
|
|
||
|
let header = pp.header().clone();
|
||
|
let map = pp.take_map();
|
||
|
|
||
|
let (packet, ppr_) = pp.recurse()?;
|
||
|
ppr = ppr_;
|
||
|
let recursion_depth = ppr.last_recursion_depth().unwrap();
|
||
|
|
||
|
dumper.packet(output, recursion_depth as usize,
|
||
|
header, packet, map, additional_fields)?;
|
||
|
}
|
||
|
|
||
|
dumper.flush(output)?;
|
||
|
|
||
|
if let PacketParserResult::EOF(eof) = ppr {
|
||
|
if eof.is_message().is_ok() {
|
||
|
Ok(Kind::Message {
|
||
|
encrypted: message_encrypted,
|
||
|
})
|
||
|
} else if eof.is_tpk().is_ok() {
|
||
|
Ok(Kind::TPK)
|
||
|
} else if eof.is_keyring().is_ok() {
|
||
|
Ok(Kind::Keyring)
|
||
|
} else {
|
||
|
Ok(Kind::Unknown)
|
||
|
}
|
||
|
} else {
|
||
|
unreachable!()
|
||
|
}
|
||
|
}
|
||
|
|
||
|
struct Node {
|
||
|
header: Header,
|
||
|
packet: Packet,
|
||
|
map: Option<Map>,
|
||
|
additional_fields: Option<Vec<String>>,
|
||
|
children: Vec<Node>,
|
||
|
}
|
||
|
|
||
|
impl Node {
|
||
|
fn new(header: Header, packet: Packet, map: Option<Map>,
|
||
|
additional_fields: Option<Vec<String>>) -> Self {
|
||
|
Node {
|
||
|
header: header,
|
||
|
packet: packet,
|
||
|
map: map,
|
||
|
additional_fields: additional_fields,
|
||
|
children: Vec::new(),
|
||
|
}
|
||
|
}
|
||
|
|
||
|
fn append(&mut self, depth: usize, node: Node) {
|
||
|
if depth == 0 {
|
||
|
self.children.push(node);
|
||
|
} else {
|
||
|
self.children.iter_mut().last().unwrap().append(depth - 1, node);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pub struct PacketDumper {
|
||
|
width: usize,
|
||
|
mpis: bool,
|
||
|
root: Option<Node>,
|
||
|
}
|
||
|
|
||
|
impl PacketDumper {
|
||
|
pub fn new(width: usize, mpis: bool) -> Self {
|
||
|
PacketDumper {
|
||
|
width: width,
|
||
|
mpis: mpis,
|
||
|
root: None,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pub fn packet(&mut self, output: &mut dyn io::Write, depth: usize,
|
||
|
header: Header, p: Packet, map: Option<Map>,
|
||
|
additional_fields: Option<Vec<String>>)
|
||
|
-> Result<()> {
|
||
|
let node = Node::new(header, p, map, additional_fields);
|
||
|
if self.root.is_none() {
|
||
|
assert_eq!(depth, 0);
|
||
|
self.root = Some(node);
|
||
|
} else {
|
||
|
if depth == 0 {
|
||
|
let root = self.root.take().unwrap();
|
||
|
self.dump_tree(output, "", &root)?;
|
||
|
self.root = Some(node);
|
||
|
} else {
|
||
|
self.root.as_mut().unwrap().append(depth - 1, node);
|
||
|
}
|
||
|
}
|
||
|
Ok(())
|
||
|
}
|
||
|
|
||
|
pub fn flush(&self, output: &mut dyn io::Write) -> Result<()> {
|
||
|
if let Some(root) = self.root.as_ref() {
|
||
|
self.dump_tree(output, "", &root)?;
|
||
|
}
|
||
|
Ok(())
|
||
|
}
|
||
|
|
||
|
fn dump_tree(&self, output: &mut dyn io::Write, indent: &str, node: &Node)
|
||
|
-> Result<()> {
|
||
|
let indent_node =
|
||
|
format!("{}{} ", indent,
|
||
|
if node.children.is_empty() { " " } else { "│" });
|
||
|
self.dump_packet(output, &indent_node, Some(&node.header), &node.packet,
|
||
|
node.map.as_ref(), node.additional_fields.as_ref())?;
|
||
|
if node.children.is_empty() {
|
||
|
return Ok(());
|
||
|
}
|
||
|
|
||
|
let last = node.children.len() - 1;
|
||
|
for (i, child) in node.children.iter().enumerate() {
|
||
|
let is_last = i == last;
|
||
|
write!(output, "{}{}── ", indent,
|
||
|
if is_last { "└" } else { "├" })?;
|
||
|
let indent_child =
|
||
|
format!("{}{} ", indent,
|
||
|
if is_last { " " } else { "│" });
|
||
|
self.dump_tree(output, &indent_child, child)?;
|
||
|
}
|
||
|
Ok(())
|
||
|
}
|
||
|
|
||
|
fn dump_packet(&self, output: &mut dyn io::Write, i: &str,
|
||
|
header: Option<&Header>, p: &Packet, map: Option<&Map>,
|
||
|
additional_fields: Option<&Vec<String>>)
|
||
|
-> Result<()> {
|
||
|
use sequoia_openpgp::Packet::*;
|
||
|
|
||
|
if let Some(h) = header {
|
||
|
write!(output, "{} CTB, {}: ",
|
||
|
if let CTB::Old(_) = h.ctb { "Old" } else { "New" },
|
||
|
match h.length {
|
||
|
BodyLength::Full(n) =>
|
||
|
format!("{} bytes", n),
|
||
|
BodyLength::Partial(n) =>
|
||
|
format!("partial length, {} bytes in first chunk", n),
|
||
|
BodyLength::Indeterminate =>
|
||
|
"indeterminate length".into(),
|
||
|
})?;
|
||
|
}
|
||
|
|
||
|
match p {
|
||
|
Unknown(ref u) => {
|
||
|
writeln!(output, "Unknown Packet")?;
|
||
|
writeln!(output, "{} Tag: {}", i, u.tag())?;
|
||
|
writeln!(output, "{} Error: {}", i, u.error())?;
|
||
|
},
|
||
|
|
||
|
Signature(ref s) => {
|
||
|
writeln!(output, "Signature Packet")?;
|
||
|
writeln!(output, "{} Version: {}", i, s.version())?;
|
||
|
writeln!(output, "{} Type: {}", i, s.sigtype())?;
|
||
|
writeln!(output, "{} Pk algo: {}", i, s.pk_algo())?;
|
||
|
writeln!(output, "{} Hash algo: {}", i, s.hash_algo())?;
|
||
|
if s.hashed_area().iter().count() > 0 {
|
||
|
writeln!(output, "{} Hashed area:", i)?;
|
||
|
for (_, _, pkt) in s.hashed_area().iter() {
|
||
|
self.dump_subpacket(output, i, pkt, s)?;
|
||
|
}
|
||
|
}
|
||
|
if s.unhashed_area().iter().count() > 0 {
|
||
|
writeln!(output, "{} Unhashed area:", i)?;
|
||
|
for (_, _, pkt) in s.unhashed_area().iter() {
|
||
|
self.dump_subpacket(output, i, pkt, s)?;
|
||
|
}
|
||
|
}
|
||
|
writeln!(output, "{} Hash prefix: {}", i,
|
||
|
hex::encode(s.hash_prefix()))?;
|
||
|
write!(output, "{} Level: {} ", i, s.level())?;
|
||
|
match s.level() {
|
||
|
0 => writeln!(output, "(signature over data)")?,
|
||
|
1 => writeln!(output, "(notarization over signatures \
|
||
|
level 0 and data)")?,
|
||
|
n => writeln!(output, "(notarization over signatures \
|
||
|
level <= {} and data)", n - 1)?,
|
||
|
}
|
||
|
if self.mpis {
|
||
|
use sequoia_openpgp::crypto::mpis::Signature::*;
|
||
|
writeln!(output, "{}", i)?;
|
||
|
writeln!(output, "{} Signature:", i)?;
|
||
|
|
||
|
let ii = format!("{} ", i);
|
||
|
match s.mpis() {
|
||
|
RSA { s } =>
|
||
|
self.dump_mpis(output, &ii,
|
||
|
&[&s.value],
|
||
|
&["s"])?,
|
||
|
DSA { r, s } =>
|
||
|
self.dump_mpis(output, &ii,
|
||
|
&[&r.value, &s.value],
|
||
|
&["r", "s"])?,
|
||
|
Elgamal { r, s } =>
|
||
|
self.dump_mpis(output, &ii,
|
||
|
&[&r.value, &s.value],
|
||
|
&["r", "s"])?,
|
||
|
EdDSA { r, s } =>
|
||
|
self.dump_mpis(output, &ii,
|
||
|
&[&r.value, &s.value],
|
||
|
&["r", "s"])?,
|
||
|
ECDSA { r, s } =>
|
||
|
self.dump_mpis(output, &ii,
|
||
|
&[&r.value, &s.value],
|
||
|
&["r", "s"])?,
|
||
|
Unknown { mpis, rest } => {
|
||
|
let keys: Vec<String> =
|
||
|
(0..mpis.len()).map(
|
||
|
|i| format!("mpi{}", i)).collect();
|
||
|
self.dump_mpis(
|
||
|
output, &ii,
|
||
|
&mpis.iter().map(|m| m.value.iter().as_slice())
|
||
|
.collect::<Vec<_>>()[..],
|
||
|
&keys.iter().map(|k| k.as_str())
|
||
|
.collect::<Vec<_>>()[..],
|
||
|
)?;
|
||
|
|
||
|
self.dump_mpis(output, &ii, &[&rest[..]], &["rest"])?;
|
||
|
},
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
|
||
|
OnePassSig(ref o) => {
|
||
|
writeln!(output, "One-Pass Signature Packet")?;
|
||
|
writeln!(output, "{} Version: {}", i, o.version())?;
|
||
|
writeln!(output, "{} Type: {}", i, o.sigtype())?;
|
||
|
writeln!(output, "{} Pk algo: {}", i, o.pk_algo())?;
|
||
|
writeln!(output, "{} Hash algo: {}", i, o.hash_algo())?;
|
||
|
writeln!(output, "{} Issuer: {}", i, o.issuer())?;
|
||
|
writeln!(output, "{} Last: {}", i, o.last())?;
|
||
|
},
|
||
|
|
||
|
PublicKey(ref k) | PublicSubkey(ref k)
|
||
|
| SecretKey(ref k) | SecretSubkey(ref k) =>
|
||
|
{
|
||
|
writeln!(output, "{}", p.tag())?;
|
||
|
writeln!(output, "{} Version: {}", i, k.version())?;
|
||
|
writeln!(output, "{} Creation time: {}", i,
|
||
|
time::strftime(TIMEFMT, k.creation_time()).unwrap())?;
|
||
|
writeln!(output, "{} Pk algo: {}", i, k.pk_algo())?;
|
||
|
if let Some(bits) = k.mpis().bits() {
|
||
|
writeln!(output, "{} Pk size: {} bits", i, bits)?;
|
||
|
}
|
||
|
if self.mpis {
|
||
|
use sequoia_openpgp::crypto::mpis::PublicKey::*;
|
||
|
writeln!(output, "{}", i)?;
|
||
|
writeln!(output, "{} Public Key:", i)?;
|
||
|
|
||
|
let ii = format!("{} ", i);
|
||
|
match k.mpis() {
|
||
|
RSA { e, n } =>
|
||
|
self.dump_mpis(output, &ii,
|
||
|
&[&e.value, &n.value],
|
||
|
&["e", "n"])?,
|
||
|
DSA { p, q, g, y } =>
|
||
|
self.dump_mpis(output, &ii,
|
||
|
&[&p.value, &q.value, &g.value,
|
||
|
&y.value],
|
||
|
&["p", "q", "g", "y"])?,
|
||
|
Elgamal { p, g, y } =>
|
||
|
self.dump_mpis(output, &ii,
|
||
|
&[&p.value, &g.value, &y.value],
|
||
|
&["p", "g", "y"])?,
|
||
|
EdDSA { curve, q } => {
|
||
|
writeln!(output, "{} Curve: {}", ii, curve)?;
|
||
|
self.dump_mpis(output, &ii, &[&q.value], &["q"])?;
|
||
|
},
|
||
|
ECDSA { curve, q } => {
|
||
|
writeln!(output, "{} Curve: {}", ii, curve)?;
|
||
|
self.dump_mpis(output, &ii, &[&q.value], &["q"])?;
|
||
|
},
|
||
|
ECDH { curve, q, hash, sym } => {
|
||
|
writeln!(output, "{} Curve: {}", ii, curve)?;
|
||
|
writeln!(output, "{} Hash algo: {}", ii, hash)?;
|
||
|
writeln!(output, "{} Symmetric algo: {}", ii,
|
||
|
sym)?;
|
||
|
self.dump_mpis(output, &ii, &[&q.value], &["q"])?;
|
||
|
},
|
||
|
Unknown { mpis, rest } => {
|
||
|
let keys: Vec<String> =
|
||
|
(0..mpis.len()).map(
|
||
|
|i| format!("mpi{}", i)).collect();
|
||
|
self.dump_mpis(
|
||
|
output, &ii,
|
||
|
&mpis.iter().map(|m| m.value.iter().as_slice())
|
||
|
.collect::<Vec<_>>()[..],
|
||
|
&keys.iter().map(|k| k.as_str())
|
||
|
.collect::<Vec<_>>()[..],
|
||
|
)?;
|
||
|
|
||
|
self.dump_mpis(output, &ii, &[&rest[..]], &["rest"])?;
|
||
|
},
|
||
|
}
|
||
|
|
||
|
if let Some(secrets) = k.secret() {
|
||
|
use sequoia_openpgp::crypto::mpis::SecretKey::*;
|
||
|
writeln!(output, "{}", i)?;
|
||
|
writeln!(output, "{} Secret Key:", i)?;
|
||
|
|
||
|
let ii = format!("{} ", i);
|
||
|
match secrets {
|
||
|
sequoia_openpgp::packet::key::SecretKey::Unencrypted {
|
||
|
mpis,
|
||
|
} => match mpis {
|
||
|
RSA { d, p, q, u } =>
|
||
|
self.dump_mpis(output, &ii,
|
||
|
&[&d.value, &p.value, &q.value,
|
||
|
&u.value],
|
||
|
&["d", "p", "q", "u"])?,
|
||
|
DSA { x } =>
|
||
|
self.dump_mpis(output, &ii, &[&x.value],
|
||
|
&["x"])?,
|
||
|
Elgamal { x } =>
|
||
|
self.dump_mpis(output, &ii, &[&x.value],
|
||
|
&["x"])?,
|
||
|
EdDSA { scalar } =>
|
||
|
self.dump_mpis(output, &ii, &[&scalar.value],
|
||
|
&["scalar"])?,
|
||
|
ECDSA { scalar } =>
|
||
|
self.dump_mpis(output, &ii, &[&scalar.value],
|
||
|
&["scalar"])?,
|
||
|
ECDH { scalar } =>
|
||
|
self.dump_mpis(output, &ii, &[&scalar.value],
|
||
|
&["scalar"])?,
|
||
|
Unknown { mpis, rest } => {
|
||
|
let keys: Vec<String> =
|
||
|
(0..mpis.len()).map(
|
||
|
|i| format!("mpi{}", i)).collect();
|
||
|
self.dump_mpis(
|
||
|
output, &ii,
|
||
|
&mpis.iter()
|
||
|
.map(|m| m.value.iter().as_slice())
|
||
|
.collect::<Vec<_>>()[..],
|
||
|
&keys.iter().map(|k| k.as_str())
|
||
|
.collect::<Vec<_>>()[..],
|
||
|
)?;
|
||
|
|
||
|
self.dump_mpis(output, &ii, &[rest],
|
||
|
&["rest"])?;
|
||
|
},
|
||
|
},
|
||
|
sequoia_openpgp::packet::key::SecretKey::Encrypted {
|
||
|
s2k, algorithm, ciphertext,
|
||
|
} => {
|
||
|
writeln!(output, "{}", i)?;
|
||
|
write!(output, "{} S2K: ", ii)?;
|
||
|
self.dump_s2k(output, &ii, s2k)?;
|
||
|
writeln!(output, "{} Sym. algo: {}", ii,
|
||
|
algorithm)?;
|
||
|
self.dump_mpis(output, &ii, &[&ciphertext[..]],
|
||
|
&["ciphertext"])?;
|
||
|
},
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
|
||
|
Trust(ref p) => {
|
||
|
writeln!(output, "Trust Packet")?;
|
||
|
writeln!(output, "{} Value: {}", i, hex::encode(p.value()))?;
|
||
|
},
|
||
|
|
||
|
UserID(ref u) => {
|
||
|
writeln!(output, "User ID Packet")?;
|
||
|
writeln!(output, "{} Value: {}", i,
|
||
|
String::from_utf8_lossy(u.value()))?;
|
||
|
},
|
||
|
|
||
|
UserAttribute(ref u) => {
|
||
|
use sequoia_openpgp::packet::user_attribute::{Subpacket, Image};
|
||
|
writeln!(output, "User Attribute Packet")?;
|
||
|
|
||
|
for subpacket in u.subpackets() {
|
||
|
match subpacket {
|
||
|
Ok(Subpacket::Image(image)) => match image {
|
||
|
Image::JPEG(data) =>
|
||
|
writeln!(output, "{} JPEG: {} bytes", i,
|
||
|
data.len())?,
|
||
|
Image::Private(n, data) =>
|
||
|
writeln!(output,
|
||
|
"{} Private image({}): {} bytes", i,
|
||
|
n, data.len())?,
|
||
|
Image::Unknown(n, data) =>
|
||
|
writeln!(output,
|
||
|
"{} Unknown image({}): {} bytes", i,
|
||
|
n, data.len())?,
|
||
|
},
|
||
|
Ok(Subpacket::Unknown(n, data)) =>
|
||
|
writeln!(output,
|
||
|
"{} Unknown subpacket({}): {} bytes", i,
|
||
|
n, data.len())?,
|
||
|
Err(e) =>
|
||
|
writeln!(output,
|
||
|
"{} Invalid subpacket encoding: {}", i,
|
||
|
e)?,
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
|
||
|
Marker(_) => {
|
||
|
writeln!(output, "Marker Packet")?;
|
||
|
},
|
||
|
|
||
|
Literal(ref l) => {
|
||
|
writeln!(output, "Literal Data Packet")?;
|
||
|
writeln!(output, "{} Format: {}", i, l.format())?;
|
||
|
if let Some(filename) = l.filename() {
|
||
|
writeln!(output, "{} Filename: {}", i,
|
||
|
String::from_utf8_lossy(filename))?;
|
||
|
}
|
||
|
if let Some(timestamp) = l.date() {
|
||
|
writeln!(output, "{} Timestamp: {}", i,
|
||
|
time::strftime(TIMEFMT, timestamp).unwrap())?;
|
||
|
}
|
||
|
},
|
||
|
|
||
|
CompressedData(ref c) => {
|
||
|
writeln!(output, "Compressed Data Packet")?;
|
||
|
writeln!(output, "{} Algorithm: {}", i, c.algorithm())?;
|
||
|
},
|
||
|
|
||
|
PKESK(ref p) => {
|
||
|
writeln!(output, "Public-key Encrypted Session Key Packet")?;
|
||
|
writeln!(output, "{} Version: {}", i, p.version())?;
|
||
|
writeln!(output, "{} Recipient: {}", i, p.recipient())?;
|
||
|
writeln!(output, "{} Pk algo: {}", i, p.pk_algo())?;
|
||
|
if self.mpis {
|
||
|
use sequoia_openpgp::crypto::mpis::Ciphertext::*;
|
||
|
writeln!(output, "{}", i)?;
|
||
|
writeln!(output, "{} Encrypted session key:", i)?;
|
||
|
|
||
|
let ii = format!("{} ", i);
|
||
|
match p.esk() {
|
||
|
RSA { c } =>
|
||
|
self.dump_mpis(output, &ii,
|
||
|
&[&c.value],
|
||
|
&["c"])?,
|
||
|
Elgamal { e, c } =>
|
||
|
self.dump_mpis(output, &ii,
|
||
|
&[&e.value, &c.value],
|
||
|
&["e", "c"])?,
|
||
|
ECDH { e, key } =>
|
||
|
self.dump_mpis(output, &ii,
|
||
|
&[&e.value, key],
|
||
|
&["e", "key"])?,
|
||
|
Unknown { mpis, rest } => {
|
||
|
let keys: Vec<String> =
|
||
|
(0..mpis.len()).map(
|
||
|
|i| format!("mpi{}", i)).collect();
|
||
|
self.dump_mpis(
|
||
|
output, &ii,
|
||
|
&mpis.iter().map(|m| m.value.iter().as_slice())
|
||
|
.collect::<Vec<_>>()[..],
|
||
|
&keys.iter().map(|k| k.as_str())
|
||
|
.collect::<Vec<_>>()[..],
|
||
|
)?;
|
||
|
|
||
|
self.dump_mpis(output, &ii, &[rest], &["rest"])?;
|
||
|
},
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
|
||
|
SKESK(ref s) => {
|
||
|
writeln!(output, "Symmetric-key Encrypted Session Key Packet")?;
|
||
|
writeln!(output, "{} Version: {}", i, s.version())?;
|
||
|
match s {
|
||
|
sequoia_openpgp::packet::SKESK::V4(ref s) => {
|
||
|
writeln!(output, "{} Symmetric algo: {}", i,
|
||
|
s.symmetric_algo())?;
|
||
|
write!(output, "{} S2K: ", i)?;
|
||
|
self.dump_s2k(output, i, s.s2k())?;
|
||
|
if let Some(esk) = s.esk() {
|
||
|
writeln!(output, "{} ESK: {}", i,
|
||
|
hex::encode(esk))?;
|
||
|
}
|
||
|
},
|
||
|
|
||
|
sequoia_openpgp::packet::SKESK::V5(ref s) => {
|
||
|
writeln!(output, "{} Symmetric algo: {}", i,
|
||
|
s.symmetric_algo())?;
|
||
|
writeln!(output, "{} AEAD: {}", i,
|
||
|
s.aead_algo())?;
|
||
|
write!(output, "{} S2K: ", i)?;
|
||
|
self.dump_s2k(output, i, s.s2k())?;
|
||
|
writeln!(output, "{} IV: {}", i,
|
||
|
hex::encode(s.aead_iv()))?;
|
||
|
if let Some(esk) = s.esk() {
|
||
|
writeln!(output, "{} ESK: {}", i,
|
||
|
hex::encode(esk))?;
|
||
|
}
|
||
|
writeln!(output, "{} Digest: {}", i,
|
||
|
hex::encode(s.aead_digest()))?;
|
||
|
},
|
||
|
}
|
||
|
},
|
||
|
|
||
|
SEIP(ref s) => {
|
||
|
writeln!(output, "Encrypted and Integrity Protected Data Packet")?;
|
||
|
writeln!(output, "{} Version: {}", i, s.version())?;
|
||
|
},
|
||
|
|
||
|
MDC(ref m) => {
|
||
|
writeln!(output, "Modification Detection Code Packet")?;
|
||
|
writeln!(output, "{} Hash: {}",
|
||
|
i, hex::encode(m.hash()))?;
|
||
|
writeln!(output, "{} Computed hash: {}",
|
||
|
i, hex::encode(m.computed_hash()))?;
|
||
|
},
|
||
|
|
||
|
AED(ref a) => {
|
||
|
writeln!(output, "AEAD Encrypted Data Packet")?;
|
||
|
writeln!(output, "{} Version: {}", i, a.version())?;
|
||
|
writeln!(output, "{} Symmetric algo: {}", i, a.symmetric_algo())?;
|
||
|
writeln!(output, "{} AEAD: {}", i, a.aead())?;
|
||
|
writeln!(output, "{} Chunk size: {}", i, a.chunk_size())?;
|
||
|
writeln!(output, "{} IV: {}", i, hex::encode(a.iv()))?;
|
||
|
},
|
||
|
}
|
||
|
|
||
|
if let Some(fields) = additional_fields {
|
||
|
for field in fields {
|
||
|
writeln!(output, "{} {}", i, field)?;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if let Some(map) = map {
|
||
|
writeln!(output, "{}", i)?;
|
||
|
let mut hd = hex::Dumper::new(output, self.indentation_for_hexdump(
|
||
|
i, map.iter().map(|f| f.name.len()).max()
|
||
|
.expect("we always have one entry")));
|
||
|
|
||
|
for field in map.iter() {
|
||
|
hd.write(field.data, field.name)?;
|
||
|
}
|
||
|
|
||
|
let output = hd.into_inner();
|
||
|
writeln!(output, "{}", i)?;
|
||
|
} else {
|
||
|
writeln!(output, "{}", i)?;
|
||
|
}
|
||
|
|
||
|
Ok(())
|
||
|
}
|
||
|
|
||
|
fn dump_subpacket(&self, output: &mut dyn io::Write, i: &str,
|
||
|
s: Subpacket, sig: &Signature)
|
||
|
-> Result<()> {
|
||
|
use self::SubpacketValue::*;
|
||
|
|
||
|
match s.value {
|
||
|
Unknown(ref b) =>
|
||
|
write!(output, "{} Unknown: {:?}", i, b)?,
|
||
|
Invalid(ref b) =>
|
||
|
write!(output, "{} Invalid: {:?}", i, b)?,
|
||
|
SignatureCreationTime(ref t) =>
|
||
|
write!(output, "{} Signature creation time: {}", i,
|
||
|
time::strftime(TIMEFMT, t).unwrap())?,
|
||
|
SignatureExpirationTime(ref t) =>
|
||
|
write!(output, "{} Signature expiration time: {} ({})",
|
||
|
i, t,
|
||
|
if let Some(creation) = sig.signature_creation_time() {
|
||
|
time::strftime(TIMEFMT, &(creation + *t))
|
||
|
.unwrap()
|
||
|
} else {
|
||
|
" (no Signature Creation Time subpacket)".into()
|
||
|
})?,
|
||
|
ExportableCertification(e) =>
|
||
|
write!(output, "{} Exportable certification: {}", i, e)?,
|
||
|
TrustSignature{level, trust} =>
|
||
|
write!(output, "{} Trust signature: level {} trust {}", i,
|
||
|
level, trust)?,
|
||
|
RegularExpression(ref r) =>
|
||
|
write!(output, "{} Regular expression: {}", i,
|
||
|
String::from_utf8_lossy(r))?,
|
||
|
Revocable(r) =>
|
||
|
write!(output, "{} Revocable: {}", i, r)?,
|
||
|
KeyExpirationTime(ref t) =>
|
||
|
write!(output, "{} Key expiration time: {}", i, t)?,
|
||
|
PreferredSymmetricAlgorithms(ref c) =>
|
||
|
write!(output, "{} Symmetric algo preferences: {}", i,
|
||
|
c.iter().map(|c| format!("{:?}", c))
|
||
|
.collect::<Vec<String>>().join(", "))?,
|
||
|
RevocationKey{class, pk_algo, ref fp} =>
|
||
|
write!(output,
|
||
|
"{} Revocation key: class {} algo {} fingerprint {}", i,
|
||
|
class, pk_algo, fp)?,
|
||
|
Issuer(ref is) =>
|
||
|
write!(output, "{} Issuer: {}", i, is)?,
|
||
|
NotationData(ref n) =>
|
||
|
write!(output, "{} Notation: {:?}", i, n)?,
|
||
|
PreferredHashAlgorithms(ref h) =>
|
||
|
write!(output, "{} Hash preferences: {}", i,
|
||
|
h.iter().map(|h| format!("{:?}", h))
|
||
|
.collect::<Vec<String>>().join(", "))?,
|
||
|
PreferredCompressionAlgorithms(ref c) =>
|
||
|
write!(output, "{} Compression preferences: {}", i,
|
||
|
c.iter().map(|c| format!("{:?}", c))
|
||
|
.collect::<Vec<String>>().join(", "))?,
|
||
|
KeyServerPreferences(ref p) =>
|
||
|
write!(output, "{} Keyserver preferences: {:?}", i, p)?,
|
||
|
PreferredKeyServer(ref k) =>
|
||
|
write!(output, "{} Preferred keyserver: {}", i,
|
||
|
String::from_utf8_lossy(k))?,
|
||
|
PrimaryUserID(p) =>
|
||
|
write!(output, "{} Primary User ID: {}", i, p)?,
|
||
|
PolicyURI(ref p) =>
|
||
|
write!(output, "{} Policy URI: {}", i,
|
||
|
String::from_utf8_lossy(p))?,
|
||
|
KeyFlags(ref k) =>
|
||
|
write!(output, "{} Key flags: {:?}", i, k)?,
|
||
|
SignersUserID(ref u) =>
|
||
|
write!(output, "{} Signer's User ID: {}", i,
|
||
|
String::from_utf8_lossy(u))?,
|
||
|
ReasonForRevocation{code, ref reason} => {
|
||
|
let reason = String::from_utf8_lossy(reason);
|
||
|
write!(output, "{} Reason for revocation: {}{}{}", i, code,
|
||
|
if reason.len() > 0 { ", " } else { "" }, reason)?
|
||
|
}
|
||
|
Features(ref f) =>
|
||
|
write!(output, "{} Features: {:?}", i, f)?,
|
||
|
SignatureTarget{pk_algo, hash_algo, ref digest} =>
|
||
|
write!(output, "{} Signature target: {}, {}, {}", i,
|
||
|
pk_algo, hash_algo, hex::encode(digest))?,
|
||
|
EmbeddedSignature(_) =>
|
||
|
// Embedded signature is dumped below.
|
||
|
write!(output, "{} Embedded signature: ", i)?,
|
||
|
IssuerFingerprint(ref fp) =>
|
||
|
write!(output, "{} Issuer Fingerprint: {}", i, fp)?,
|
||
|
PreferredAEADAlgorithms(ref c) =>
|
||
|
write!(output, "{} AEAD preferences: {}", i,
|
||
|
c.iter().map(|c| format!("{:?}", c))
|
||
|
.collect::<Vec<String>>().join(", "))?,
|
||
|
IntendedRecipient(ref fp) =>
|
||
|
write!(output, "{} Intended Recipient: {}", i, fp)?,
|
||
|
}
|
||
|
|
||
|
if s.critical {
|
||
|
write!(output, " (critical)")?;
|
||
|
}
|
||
|
writeln!(output)?;
|
||
|
|
||
|
match s.value {
|
||
|
EmbeddedSignature(ref sig) => {
|
||
|
let indent = format!("{} ", i);
|
||
|
self.dump_packet(output, &indent, None, sig, None, None)?;
|
||
|
},
|
||
|
_ => (),
|
||
|
}
|
||
|
|
||
|
Ok(())
|
||
|
}
|
||
|
|
||
|
fn dump_s2k(&self, output: &mut dyn io::Write, i: &str, s2k: &S2K)
|
||
|
-> Result<()> {
|
||
|
use self::S2K::*;
|
||
|
match s2k {
|
||
|
Simple { hash } => {
|
||
|
writeln!(output, "Simple")?;
|
||
|
writeln!(output, "{} Hash: {}", i, hash)?;
|
||
|
},
|
||
|
Salted { hash, ref salt } => {
|
||
|
writeln!(output, "Salted")?;
|
||
|
writeln!(output, "{} Hash: {}", i, hash)?;
|
||
|
writeln!(output, "{} Salt: {}", i, hex::encode(salt))?;
|
||
|
},
|
||
|
Iterated { hash, ref salt, .. } => {
|
||
|
writeln!(output, "Iterated")?;
|
||
|
writeln!(output, "{} Hash: {}", i, hash)?;
|
||
|
writeln!(output, "{} Salt: {}", i, hex::encode(salt))?;
|
||
|
// writeln!(output, "{} Iterations: {}", i, iterations)?;
|
||
|
},
|
||
|
Private(n) =>
|
||
|
writeln!(output, "Private({})", n)?,
|
||
|
Unknown(n) =>
|
||
|
writeln!(output, "Unknown({})", n)?,
|
||
|
}
|
||
|
Ok(())
|
||
|
}
|
||
|
|
||
|
fn dump_mpis(&self, output: &mut dyn io::Write, i: &str,
|
||
|
chunks: &[&[u8]], keys: &[&str]) -> Result<()> {
|
||
|
assert_eq!(chunks.len(), keys.len());
|
||
|
if chunks.len() == 0 {
|
||
|
return Ok(());
|
||
|
}
|
||
|
|
||
|
let max_key_len = keys.iter().map(|k| k.len()).max().unwrap();
|
||
|
|
||
|
for (chunk, key) in chunks.iter().zip(keys.iter()) {
|
||
|
writeln!(output, "{}", i)?;
|
||
|
let mut hd = hex::Dumper::new(
|
||
|
Vec::new(), self.indentation_for_hexdump(i, max_key_len));
|
||
|
hd.write(*chunk, *key)?;
|
||
|
output.write_all(&hd.into_inner())?;
|
||
|
}
|
||
|
|
||
|
Ok(())
|
||
|
}
|
||
|
|
||
|
/// Returns indentation for hex dumps.
|
||
|
///
|
||
|
/// Returns a prefix of `i` so that a hexdump with labels no
|
||
|
/// longer than `max_label_len` will fit into the target width.
|
||
|
fn indentation_for_hexdump(&self, i: &str, max_label_len: usize) -> String {
|
||
|
let amount = ::std::cmp::max(
|
||
|
0,
|
||
|
::std::cmp::min(
|
||
|
self.width as isize
|
||
|
- 63 // Length of address, hex digits, and whitespace.
|
||
|
- max_label_len as isize,
|
||
|
i.len() as isize),
|
||
|
) as usize;
|
||
|
|
||
|
format!("{} ", &i.chars().take(amount).collect::<String>())
|
||
|
}
|
||
|
|
||
|
|
||
|
}
|