hagrid-keyserver--hagrid/src/dump.rs

1135 lines
42 KiB
Rust

use std::io::{self, Read};
use self::openpgp::crypto::mpi;
use self::openpgp::crypto::{SessionKey, S2K};
use self::openpgp::fmt::hex;
use self::openpgp::packet::header::CTB;
use self::openpgp::packet::prelude::*;
use self::openpgp::packet::signature::subpacket::{Subpacket, SubpacketValue};
use self::openpgp::packet::{header::BodyLength, Header, Signature};
use self::openpgp::parse::{map::Map, PacketParserResult, Parse};
use self::openpgp::types::{Duration, SymmetricAlgorithm, Timestamp};
use self::openpgp::{Packet, Result};
use sequoia_openpgp as openpgp;
#[derive(Debug)]
pub enum Kind {
Message { encrypted: bool },
Keyring,
Cert,
Unknown,
}
/// Converts sequoia_openpgp types for rendering.
pub trait Convert<T> {
/// Performs the conversion.
fn convert(self) -> T;
}
impl Convert<chrono::Duration> for std::time::Duration {
fn convert(self) -> chrono::Duration {
chrono::Duration::seconds(self.as_secs() as i64)
}
}
impl Convert<chrono::Duration> for Duration {
fn convert(self) -> chrono::Duration {
chrono::Duration::seconds(self.as_secs() as i64)
}
}
impl Convert<chrono::DateTime<chrono::offset::Utc>> for std::time::SystemTime {
fn convert(self) -> chrono::DateTime<chrono::offset::Utc> {
chrono::DateTime::<chrono::offset::Utc>::from(self)
}
}
impl Convert<chrono::DateTime<chrono::offset::Utc>> for Timestamp {
fn convert(self) -> chrono::DateTime<chrono::offset::Utc> {
std::time::SystemTime::from(self).convert()
}
}
#[allow(clippy::redundant_pattern_matching)]
pub fn dump<W>(
input: &mut (dyn io::Read + Sync + Send),
output: &mut dyn io::Write,
mpis: bool,
hex: bool,
sk: Option<&SessionKey>,
width: W,
) -> Result<Kind>
where
W: Into<Option<usize>>,
{
let mut ppr = self::openpgp::parse::PacketParserBuilder::from_reader(input)?
.map(hex)
.build()?;
let mut message_encrypted = false;
let width = width.into().unwrap_or(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.encrypted() {
fields.push("Decryption failed".into());
} else {
fields.push("Decryption successful".into());
}
Some(fields)
}
_ => None,
};
let header = pp.header().clone();
let map = pp.take_map();
let recursion_depth = pp.recursion_depth();
let packet = pp.packet.clone();
dumper.packet(
output,
recursion_depth as usize,
header,
packet,
map,
additional_fields,
)?;
let (_, ppr_) = match pp.recurse() {
Ok(v) => Ok(v),
Err(e) => {
let _ = dumper.flush(output);
Err(e)
}
}?;
ppr = ppr_;
}
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_cert().is_ok() {
Ok(Kind::Cert)
} 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,
packet,
map,
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,
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,
mut output: &mut dyn io::Write,
i: &str,
header: Option<&Header>,
p: &Packet,
map: Option<&Map>,
additional_fields: Option<&Vec<String>>,
) -> Result<()> {
use self::openpgp::Packet::*;
if let Some(tag) = p.kind() {
write!(output, "{}", tag)?;
} else {
write!(output, "Unknown or Unsupported Packet")?;
}
if let Some(h) = header {
write!(
output,
", {} CTB, {}{}",
if let CTB::Old(_) = h.ctb() {
"old"
} else {
"new"
},
if let Some(map) = map {
format!(
"{} header bytes + ",
map.iter()
.take(2)
.map(|f| f.as_bytes().len())
.sum::<usize>()
)
} else {
// XXX: Mapping is disabled. No can do for
// now. Once we save the header in
// packet::Common, we can use this instead of
// relying on the map.
"".into()
},
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(),
}
)?;
}
writeln!(output)?;
fn dump_key<P, R>(
pd: &PacketDumper,
output: &mut dyn io::Write,
i: &str,
k: &Key<P, R>,
) -> Result<()>
where
P: key::KeyParts,
R: key::KeyRole,
{
writeln!(output, "{} Version: {}", i, k.version())?;
writeln!(
output,
"{} Creation time: {}",
i,
k.creation_time().convert()
)?;
writeln!(output, "{} Pk algo: {}", i, k.pk_algo())?;
if let Some(bits) = k.mpis().bits() {
writeln!(output, "{} Pk size: {} bits", i, bits)?;
}
writeln!(output, "{} Fingerprint: {}", i, k.fingerprint())?;
writeln!(output, "{} KeyID: {}", i, k.keyid())?;
if pd.mpis {
writeln!(output, "{}", i)?;
writeln!(output, "{} Public Key:", i)?;
let ii = format!("{} ", i);
match k.mpis() {
mpi::PublicKey::RSA { e, n } => {
pd.dump_mpis(output, &ii, &[e.value(), n.value()], &["e", "n"])?
}
mpi::PublicKey::DSA { p, q, g, y } => pd.dump_mpis(
output,
&ii,
&[p.value(), q.value(), g.value(), y.value()],
&["p", "q", "g", "y"],
)?,
mpi::PublicKey::ElGamal { p, g, y } => pd.dump_mpis(
output,
&ii,
&[p.value(), g.value(), y.value()],
&["p", "g", "y"],
)?,
mpi::PublicKey::EdDSA { curve, q } => {
writeln!(output, "{} Curve: {}", ii, curve)?;
pd.dump_mpis(output, &ii, &[q.value()], &["q"])?;
}
mpi::PublicKey::ECDSA { curve, q } => {
writeln!(output, "{} Curve: {}", ii, curve)?;
pd.dump_mpis(output, &ii, &[q.value()], &["q"])?;
}
mpi::PublicKey::ECDH {
curve,
q,
hash,
sym,
} => {
writeln!(output, "{} Curve: {}", ii, curve)?;
writeln!(output, "{} Hash algo: {}", ii, hash)?;
writeln!(output, "{} Symmetric algo: {}", ii, sym)?;
pd.dump_mpis(output, &ii, &[q.value()], &["q"])?;
}
mpi::PublicKey::Unknown { mpis, rest } => {
let keys: Vec<String> =
(0..mpis.len()).map(|i| format!("mpi{}", i)).collect();
pd.dump_mpis(
output,
&ii,
&mpis
.iter()
.map(|m| m.value().iter().as_slice())
.collect::<Vec<_>>()[..],
&keys.iter().map(|k| k.as_str()).collect::<Vec<_>>()[..],
)?;
pd.dump_mpis(output, &ii, &[&rest[..]], &["rest"])?;
}
// crypto::mpi:Publickey is non-exhaustive
u => writeln!(output, "{}Unknown variant: {:?}", ii, u)?,
}
}
if let Some(secrets) = k.optional_secret() {
writeln!(output, "{}", i)?;
writeln!(output, "{} Secret Key:", i)?;
let ii = format!("{} ", i);
match secrets {
SecretKeyMaterial::Unencrypted(ref u) => {
writeln!(output, "{}", i)?;
writeln!(output, "{} Unencrypted", ii)?;
if pd.mpis {
u.map(|mpis| -> Result<()> {
match mpis {
mpi::SecretKeyMaterial::RSA { d, p, q, u } => pd.dump_mpis(
output,
&ii,
&[d.value(), p.value(), q.value(), u.value()],
&["d", "p", "q", "u"],
)?,
mpi::SecretKeyMaterial::DSA { x } => {
pd.dump_mpis(output, &ii, &[x.value()], &["x"])?
}
mpi::SecretKeyMaterial::ElGamal { x } => {
pd.dump_mpis(output, &ii, &[x.value()], &["x"])?
}
mpi::SecretKeyMaterial::EdDSA { scalar } => {
pd.dump_mpis(output, &ii, &[scalar.value()], &["scalar"])?
}
mpi::SecretKeyMaterial::ECDSA { scalar } => {
pd.dump_mpis(output, &ii, &[scalar.value()], &["scalar"])?
}
mpi::SecretKeyMaterial::ECDH { scalar } => {
pd.dump_mpis(output, &ii, &[scalar.value()], &["scalar"])?
}
mpi::SecretKeyMaterial::Unknown { mpis, rest } => {
let keys: Vec<String> =
(0..mpis.len()).map(|i| format!("mpi{}", i)).collect();
pd.dump_mpis(
output,
&ii,
&mpis
.iter()
.map(|m| {
m.value().iter().as_slice()
})
.collect::<Vec<_>>()[..],
&keys
.iter()
.map(|k| k.as_str())
.collect::<Vec<_>>()[..],
)?;
pd.dump_mpis(output, &ii, &[rest], &["rest"])?;
}
// crypto::mpi::SecretKeyMaterial is non-exhaustive.
u => writeln!(output, "{}Unknown variant: {:?}", ii, u)?,
}
Ok(())
})?;
}
}
SecretKeyMaterial::Encrypted(ref e) => {
writeln!(output, "{}", i)?;
writeln!(output, "{} Encrypted", ii)?;
write!(output, "{} S2K: ", ii)?;
pd.dump_s2k(output, &ii, e.s2k())?;
writeln!(output, "{} Sym. algo: {}", ii, e.algo())?;
if pd.mpis {
if let Ok(ciphertext) = e.ciphertext() {
pd.dump_mpis(output, &ii, &[ciphertext], &["ciphertext"])?;
}
}
}
}
}
Ok(())
}
match p {
Unknown(ref u) => {
writeln!(output, "{} Tag: {}", i, u.tag())?;
writeln!(output, "{} Error: {}", i, u.error())?;
}
PublicKey(ref k) => dump_key(self, output, i, k)?,
PublicSubkey(ref k) => dump_key(self, output, i, k)?,
SecretKey(ref k) => dump_key(self, output, i, k)?,
SecretSubkey(ref k) => dump_key(self, output, i, k)?,
Signature(ref s) => {
writeln!(output, "{} Version: {}", i, s.version())?;
writeln!(output, "{} Type: {}", i, s.typ())?;
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,
"{} Digest prefix: {}",
i,
hex::encode(s.digest_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 {
writeln!(output, "{}", i)?;
writeln!(output, "{} Signature:", i)?;
let ii = format!("{} ", i);
match s.mpis() {
mpi::Signature::RSA { s } => {
self.dump_mpis(output, &ii, &[s.value()], &["s"])?
}
mpi::Signature::DSA { r, s } => {
self.dump_mpis(output, &ii, &[r.value(), s.value()], &["r", "s"])?
}
mpi::Signature::ElGamal { r, s } => {
self.dump_mpis(output, &ii, &[r.value(), s.value()], &["r", "s"])?
}
mpi::Signature::EdDSA { r, s } => {
self.dump_mpis(output, &ii, &[r.value(), s.value()], &["r", "s"])?
}
mpi::Signature::ECDSA { r, s } => {
self.dump_mpis(output, &ii, &[r.value(), s.value()], &["r", "s"])?
}
mpi::Signature::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"])?;
}
// crypto::mpi::Signature is non-exhaustive.
u => writeln!(output, "{}Unknown variant: {:?}", ii, u)?,
}
}
}
OnePassSig(ref o) => {
writeln!(output, "{} Version: {}", i, o.version())?;
writeln!(output, "{} Type: {}", i, o.typ())?;
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())?;
}
Trust(ref p) => {
writeln!(output, "{} Value:", i)?;
let mut hd = hex::Dumper::new(
&mut output,
self.indentation_for_hexdump(&format!("{} ", i), 16),
);
hd.write_ascii(p.value())?;
}
UserID(ref u) => {
writeln!(
output,
"{} Value: {}",
i,
String::from_utf8_lossy(u.value())
)?;
}
UserAttribute(ref u) => {
use self::openpgp::packet::user_attribute::{Image, Subpacket};
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(_) => {}
Literal(ref l) => {
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, timestamp.convert())?;
}
}
CompressedData(ref c) => {
writeln!(output, "{} Algorithm: {}", i, c.algo())?;
}
PKESK(ref p) => {
writeln!(output, "{} Version: {}", i, p.version())?;
writeln!(output, "{} Recipient: {}", i, p.recipient())?;
writeln!(output, "{} Pk algo: {}", i, p.pk_algo())?;
if self.mpis {
writeln!(output, "{}", i)?;
writeln!(output, "{} Encrypted session key:", i)?;
let ii = format!("{} ", i);
match p.esk() {
mpi::Ciphertext::RSA { c } => {
self.dump_mpis(output, &ii, &[c.value()], &["c"])?
}
mpi::Ciphertext::ElGamal { e, c } => {
self.dump_mpis(output, &ii, &[e.value(), c.value()], &["e", "c"])?
}
mpi::Ciphertext::ECDH { e, key } => {
self.dump_mpis(output, &ii, &[e.value(), key], &["e", "key"])?
}
mpi::Ciphertext::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"])?;
}
// crypto::mpi::Ciphertext is non-exhaustive.
u => writeln!(output, "{}Unknown variant: {:?}", ii, u)?,
}
}
}
SKESK(ref s) => {
writeln!(output, "{} Version: {}", i, s.version())?;
match s {
self::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 Ok(Some(esk)) = s.esk() {
writeln!(output, "{} ESK: {}", i, hex::encode(esk))?;
}
}
self::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())?;
if let Ok(iv) = s.aead_iv() {
writeln!(output, "{} IV: {}", i, hex::encode(iv))?;
}
if let Ok(Some(esk)) = s.esk() {
writeln!(output, "{} ESK: {}", i, hex::encode(esk))?;
}
writeln!(output, "{} Digest: {}", i, hex::encode(s.aead_digest()))?;
}
// SKESK is non-exhaustive.
u => writeln!(output, "{} Unknown variant: {:?}", i, u)?,
}
}
SEIP(ref s) => {
writeln!(output, "{} Version: {}", i, s.version())?;
}
MDC(ref m) => {
writeln!(output, "{} Digest: {}", i, hex::encode(m.digest()))?;
writeln!(
output,
"{} Computed digest: {}",
i,
hex::encode(m.computed_digest())
)?;
}
AED(ref a) => {
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()))?;
}
// openpgp::Packet is non-exhaustive.
u => writeln!(output, "{} Unknown variant: {:?}", i, u)?,
}
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| {
if f.name() == "body" {
16
} else {
f.name().len()
}
})
.max()
.expect("we always have one entry"),
),
);
for field in map.iter() {
if field.name() == "body" {
hd.write_ascii(field.as_bytes())?;
} else {
hd.write(field.as_bytes(), 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::*;
let hexdump_unknown = |output: &mut dyn io::Write, buf| -> Result<()> {
let mut hd = hex::Dumper::new(
output,
self.indentation_for_hexdump(&format!("{} ", i), 0),
);
hd.write_labeled(buf, |_, _| None)?;
Ok(())
};
match s.value() {
Unknown { body, .. } => {
writeln!(
output,
"{} {:?}{}:",
i,
s.tag(),
if s.critical() { " (critical)" } else { "" }
)?;
hexdump_unknown(output, body.as_slice())?;
}
SignatureCreationTime(t) => write!(
output,
"{} Signature creation time: {}",
i,
(*t).convert()
)?,
SignatureExpirationTime(t) => write!(
output,
"{} Signature expiration time: {} ({})",
i,
t.convert(),
if let Some(creation) = sig.signature_creation_time() {
(creation + std::time::Duration::from(*t))
.convert()
.to_string()
} 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(t) => {
write!(output, "{} Key expiration time: {}", i, t.convert())?
}
PreferredSymmetricAlgorithms(ref c) => write!(
output,
"{} Symmetric algo preferences: {}",
i,
c.iter()
.map(|c| format!("{:?}", c))
.collect::<Vec<String>>()
.join(", ")
)?,
RevocationKey(rk) => {
let (pk_algo, fp) = rk.revoker();
write!(output, "{} Revocation key: {}/{}", i, fp, pk_algo)?;
if rk.sensitive() {
write!(output, ", sensitive")?;
}
}
Issuer(ref is) => write!(output, "{} Issuer: {}", i, is)?,
NotationData(n) => {
if n.flags().human_readable() {
write!(output, "{} Notation: {}", i, n)?;
if s.critical() {
write!(output, " (critical)")?;
}
writeln!(output)?;
} else {
write!(output, "{} Notation: {}", i, n.name())?;
let flags = format!("{:?}", n.flags());
if !flags.is_empty() {
write!(output, "{}", flags)?;
}
if s.critical() {
write!(output, " (critical)")?;
}
writeln!(output)?;
hexdump_unknown(output, n.value())?;
}
}
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)?,
AttestedCertifications(digests) => {
write!(output, "{} Attested Certifications:", i)?;
if digests.is_empty() {
writeln!(output, " None")?;
} else {
writeln!(output)?;
for d in digests {
writeln!(output, "{} {}", i, hex::encode(d))?;
}
}
}
// SubpacketValue is non-exhaustive.
u => writeln!(output, "{} Unknown variant: {:?}", i, u)?,
}
match s.value() {
Unknown { .. } => (),
NotationData { .. } => (),
EmbeddedSignature(ref sig) => {
if s.critical() {
write!(output, " (critical)")?;
}
writeln!(output)?;
let indent = format!("{} ", i);
write!(output, "{}", indent)?;
self.dump_packet(output, &indent, None, &sig.clone().into(), None, None)?;
}
_ => {
if s.critical() {
write!(output, " (critical)")?;
}
writeln!(output)?;
}
}
Ok(())
}
fn dump_s2k(&self, output: &mut dyn io::Write, i: &str, s2k: &S2K) -> Result<()> {
use self::S2K::*;
#[allow(deprecated)]
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,
hash_bytes,
} => {
writeln!(output, "Iterated")?;
writeln!(output, "{} Hash: {}", i, hash)?;
writeln!(output, "{} Salt: {}", i, hex::encode(salt))?;
writeln!(output, "{} Hash bytes: {}", i, hash_bytes)?;
}
Private { tag, parameters } => {
writeln!(output, "Private")?;
writeln!(output, "{} Tag: {}", i, tag)?;
if let Some(p) = parameters.as_ref() {
writeln!(output, "{} Parameters: {:?}", i, p)?;
}
}
Unknown { tag, parameters } => {
writeln!(output, "Unknown")?;
writeln!(output, "{} Tag: {}", i, tag)?;
if let Some(p) = parameters.as_ref() {
writeln!(output, "{} Parameters: {:?}", i, p)?;
}
}
// S2K is non-exhaustive
u => writeln!(output, "{} Unknown variant: {:?}", i, u)?,
}
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.is_empty() {
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>())
}
}