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

991 lines
40 KiB
Rust
Raw Normal View History

use std::io::{self, Read};
2021-01-07 12:18:08 +00:00
use sequoia_openpgp as openpgp;
2020-01-29 22:47:29 +00:00
use self::openpgp::types::{Duration, Timestamp, SymmetricAlgorithm};
use self::openpgp::fmt::hex;
2020-11-04 21:21:00 +00:00
use self::openpgp::crypto::mpi;
use self::openpgp::{Packet, Result};
2020-01-29 22:47:29 +00:00
use self::openpgp::packet::prelude::*;
use self::openpgp::packet::header::CTB;
use self::openpgp::packet::{Header, header::BodyLength, Signature};
use self::openpgp::packet::signature::subpacket::{Subpacket, SubpacketValue};
2020-02-08 10:38:26 +00:00
use self::openpgp::crypto::{SessionKey, S2K};
use self::openpgp::parse::{map::Map, Parse, PacketParserResult};
#[derive(Debug)]
pub enum Kind {
Message {
encrypted: bool,
},
Keyring,
2020-01-29 22:47:29 +00:00
Cert,
Unknown,
}
2020-01-29 22:47:29 +00:00
/// 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)?
2020-11-04 21:21:00 +00:00
.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)));
2020-11-04 21:21:00 +00:00
if pp.encrypted() {
fields.push("Decryption failed".into());
2020-11-04 21:21:00 +00:00
} else {
fields.push("Decryption successful".into());
}
Some(fields)
},
_ => None,
};
let header = pp.header().clone();
let map = pp.take_map();
2020-11-04 21:21:00 +00:00
let recursion_depth = pp.recursion_depth();
let packet = pp.packet.clone();
dumper.packet(output, recursion_depth as usize,
header, packet, map, additional_fields)?;
2020-11-04 21:21:00 +00:00
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,
})
2020-01-29 22:47:29 +00:00
} 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(())
}
2021-06-08 10:52:02 +00:00
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::*;
2020-01-29 22:47:29 +00:00
if let Some(tag) = p.kind() {
write!(output, "{}", tag)?;
} else {
2021-01-07 12:18:08 +00:00
write!(output, "Unknown or Unsupported Packet")?;
2020-01-29 22:47:29 +00:00
}
if let Some(h) = header {
2020-11-04 21:21:00 +00:00
write!(output, ", {} CTB, {}{}",
2020-01-29 22:47:29 +00:00
if let CTB::Old(_) = h.ctb() { "old" } else { "new" },
2020-11-04 21:21:00 +00:00
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()
},
2020-01-29 22:47:29 +00:00
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(),
})?;
}
2020-01-29 22:47:29 +00:00
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() {
2020-11-04 21:21:00 +00:00
mpi::PublicKey::RSA { e, n } =>
2020-01-29 22:47:29 +00:00
pd.dump_mpis(output, &ii,
&[e.value(), n.value()],
&["e", "n"])?,
2020-11-04 21:21:00 +00:00
mpi::PublicKey::DSA { p, q, g, y } =>
2020-01-29 22:47:29 +00:00
pd.dump_mpis(output, &ii,
&[p.value(), q.value(), g.value(),
y.value()],
&["p", "q", "g", "y"])?,
2020-11-04 21:21:00 +00:00
mpi::PublicKey::ElGamal { p, g, y } =>
2020-01-29 22:47:29 +00:00
pd.dump_mpis(output, &ii,
&[p.value(), g.value(), y.value()],
&["p", "g", "y"])?,
2020-11-04 21:21:00 +00:00
mpi::PublicKey::EdDSA { curve, q } => {
2020-01-29 22:47:29 +00:00
writeln!(output, "{} Curve: {}", ii, curve)?;
pd.dump_mpis(output, &ii, &[q.value()], &["q"])?;
},
2020-11-04 21:21:00 +00:00
mpi::PublicKey::ECDSA { curve, q } => {
2020-01-29 22:47:29 +00:00
writeln!(output, "{} Curve: {}", ii, curve)?;
pd.dump_mpis(output, &ii, &[q.value()], &["q"])?;
},
2020-11-04 21:21:00 +00:00
mpi::PublicKey::ECDH { curve, q, hash, sym } => {
2020-01-29 22:47:29 +00:00
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"])?;
},
2020-11-04 21:21:00 +00:00
mpi::PublicKey::Unknown { mpis, rest } => {
2020-01-29 22:47:29 +00:00
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"])?;
},
2021-01-07 12:18:08 +00:00
// crypto::mpi:Publickey is non-exhaustive
2021-06-08 10:52:02 +00:00
u => writeln!(output, "{}Unknown variant: {:?}", ii, u)?,
2020-01-29 22:47:29 +00:00
}
2020-11-04 21:21:00 +00:00
}
2020-01-29 22:47:29 +00:00
2020-11-04 21:21:00 +00:00
if let Some(secrets) = k.optional_secret() {
writeln!(output, "{}", i)?;
writeln!(output, "{} Secret Key:", i)?;
2020-01-29 22:47:29 +00:00
2020-11-04 21:21:00 +00:00
let ii = format!("{} ", i);
match secrets {
SecretKeyMaterial::Unencrypted(ref u) => {
writeln!(output, "{}", i)?;
writeln!(output, "{} Unencrypted", ii)?;
if pd.mpis {
u.map(|mpis| -> Result<()> {
2020-01-29 22:47:29 +00:00
match mpis
{
2020-11-04 21:21:00 +00:00
mpi::SecretKeyMaterial::RSA { d, p, q, u } =>
2020-01-29 22:47:29 +00:00
pd.dump_mpis(output, &ii,
2020-11-04 21:21:00 +00:00
&[d.value(), p.value(),
q.value(), u.value()],
&["d", "p", "q", "u"])?,
mpi::SecretKeyMaterial::DSA { x } =>
2020-01-29 22:47:29 +00:00
pd.dump_mpis(output, &ii, &[x.value()],
2020-11-04 21:21:00 +00:00
&["x"])?,
mpi::SecretKeyMaterial::ElGamal { x } =>
2020-01-29 22:47:29 +00:00
pd.dump_mpis(output, &ii, &[x.value()],
2020-11-04 21:21:00 +00:00
&["x"])?,
mpi::SecretKeyMaterial::EdDSA { scalar } =>
2020-01-29 22:47:29 +00:00
pd.dump_mpis(output, &ii,
2020-11-04 21:21:00 +00:00
&[scalar.value()],
&["scalar"])?,
mpi::SecretKeyMaterial::ECDSA { scalar } =>
2020-01-29 22:47:29 +00:00
pd.dump_mpis(output, &ii,
2020-11-04 21:21:00 +00:00
&[scalar.value()],
&["scalar"])?,
mpi::SecretKeyMaterial::ECDH { scalar } =>
2020-01-29 22:47:29 +00:00
pd.dump_mpis(output, &ii,
2020-11-04 21:21:00 +00:00
&[scalar.value()],
&["scalar"])?,
mpi::SecretKeyMaterial::Unknown { mpis, rest } => {
2020-01-29 22:47:29 +00:00
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],
2020-11-04 21:21:00 +00:00
&["rest"])?;
2020-01-29 22:47:29 +00:00
},
2021-01-07 12:18:08 +00:00
// crypto::mpi::SecretKeyMaterial is non-exhaustive.
2021-06-08 10:52:02 +00:00
u => writeln!(output, "{}Unknown variant: {:?}", ii, u)?,
2020-11-04 21:21:00 +00:00
}
Ok(())
})?;
}
2020-01-29 22:47:29 +00:00
}
2020-11-04 21:21:00 +00:00
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"])?;
}
}
},
2020-01-29 22:47:29 +00:00
}
}
Ok(())
}
match p {
Unknown(ref u) => {
writeln!(output, "{} Tag: {}", i, u.tag())?;
writeln!(output, "{} Error: {}", i, u.error())?;
},
2020-01-29 22:47:29 +00:00
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())?;
2020-01-29 22:47:29 +00:00
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)?;
2020-01-29 22:47:29 +00:00
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)?;
2020-01-29 22:47:29 +00:00
for pkt in s.unhashed_area().iter() {
self.dump_subpacket(output, i, pkt, s)?;
}
}
2020-01-29 22:47:29 +00:00
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() {
2020-11-04 21:21:00 +00:00
mpi::Signature::RSA { s } =>
self.dump_mpis(output, &ii,
&[s.value()],
&["s"])?,
2020-11-04 21:21:00 +00:00
mpi::Signature::DSA { r, s } =>
self.dump_mpis(output, &ii,
&[r.value(), s.value()],
&["r", "s"])?,
2020-11-04 21:21:00 +00:00
mpi::Signature::ElGamal { r, s } =>
self.dump_mpis(output, &ii,
&[r.value(), s.value()],
&["r", "s"])?,
2020-11-04 21:21:00 +00:00
mpi::Signature::EdDSA { r, s } =>
self.dump_mpis(output, &ii,
&[r.value(), s.value()],
&["r", "s"])?,
2020-11-04 21:21:00 +00:00
mpi::Signature::ECDSA { r, s } =>
self.dump_mpis(output, &ii,
&[r.value(), s.value()],
&["r", "s"])?,
2020-11-04 21:21:00 +00:00
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"])?;
},
2020-11-04 21:21:00 +00:00
2021-01-07 12:18:08 +00:00
// crypto::mpi::Signature is non-exhaustive.
2021-06-08 10:52:02 +00:00
u => writeln!(output, "{}Unknown variant: {:?}", ii, u)?,
}
}
},
OnePassSig(ref o) => {
writeln!(output, "{} Version: {}", i, o.version())?;
2020-01-29 22:47:29 +00:00
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) => {
2021-06-08 10:52:02 +00:00
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::{Subpacket, Image};
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,
2020-01-29 22:47:29 +00:00
timestamp.convert())?;
}
},
CompressedData(ref c) => {
2020-02-08 10:38:26 +00:00
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() {
2020-11-04 21:21:00 +00:00
mpi::Ciphertext::RSA { c } =>
self.dump_mpis(output, &ii,
&[c.value()],
&["c"])?,
2020-11-04 21:21:00 +00:00
mpi::Ciphertext::ElGamal { e, c } =>
self.dump_mpis(output, &ii,
&[e.value(), c.value()],
&["e", "c"])?,
2020-11-04 21:21:00 +00:00
mpi::Ciphertext::ECDH { e, key } =>
self.dump_mpis(output, &ii,
&[e.value(), key],
&["e", "key"])?,
2020-11-04 21:21:00 +00:00
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"])?;
},
2021-01-07 12:18:08 +00:00
// crypto::mpi::Ciphertext is non-exhaustive.
2021-06-08 10:52:02 +00:00
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())?;
2020-11-04 21:21:00 +00:00
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())?;
2020-11-04 21:21:00 +00:00
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()))?;
},
2020-02-08 10:38:26 +00:00
2021-01-07 12:18:08 +00:00
// SKESK is non-exhaustive.
2021-06-08 10:52:02 +00:00
u => writeln!(output, "{} Unknown variant: {:?}", i, u)?,
}
},
SEIP(ref s) => {
writeln!(output, "{} Version: {}", i, s.version())?;
},
MDC(ref m) => {
2020-01-29 22:47:29 +00:00
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()))?;
},
2020-02-08 10:38:26 +00:00
2021-01-07 12:18:08 +00:00
// openpgp::Packet is non-exhaustive.
2021-06-08 10:52:02 +00:00
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(
2020-01-29 22:47:29 +00:00
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() {
2020-01-29 22:47:29 +00:00
if field.name() == "body" {
2020-11-04 21:21:00 +00:00
hd.write_ascii(field.as_bytes())?;
2020-01-29 22:47:29 +00:00
} else {
2020-11-04 21:21:00 +00:00
hd.write(field.as_bytes(), field.name())?;
2020-01-29 22:47:29 +00:00
}
}
let output = hd.into_inner();
writeln!(output, "{}", i)?;
} else {
writeln!(output, "{}", i)?;
}
Ok(())
}
fn dump_subpacket(&self, output: &mut dyn io::Write, i: &str,
2020-01-29 22:47:29 +00:00
s: &Subpacket, sig: &Signature)
-> Result<()> {
use self::SubpacketValue::*;
2020-01-29 22:47:29 +00:00
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() {
2020-02-08 10:38:26 +00:00
Unknown { body, .. } => {
2020-01-29 22:47:29 +00:00
writeln!(output, "{} {:?}{}:", i, s.tag(),
if s.critical() { " (critical)" } else { "" })?;
2021-06-08 10:52:02 +00:00
hexdump_unknown(output, body.as_slice())?;
2020-01-29 22:47:29 +00:00
},
SignatureCreationTime(t) =>
write!(output, "{} Signature creation time: {}", i,
2020-01-29 22:47:29 +00:00
(*t).convert())?,
SignatureExpirationTime(t) =>
write!(output, "{} Signature expiration time: {} ({})",
2020-01-29 22:47:29 +00:00
i, t.convert(),
if let Some(creation) = sig.signature_creation_time() {
2021-01-07 12:18:08 +00:00
(creation + std::time::Duration::from(t.clone()))
.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)?,
2020-01-29 22:47:29 +00:00
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(", "))?,
2020-11-04 21:21:00 +00:00
RevocationKey(rk) => {
let (pk_algo, fp) = rk.revoker();
write!(output,
2020-11-04 21:21:00 +00:00
"{} Revocation key: {}/{}", i,
fp, pk_algo)?;
if rk.sensitive() {
write!(output, ", sensitive")?;
}
},
Issuer(ref is) =>
write!(output, "{} Issuer: {}", i, is)?,
2021-06-08 10:52:02 +00:00
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)?,
2021-06-08 10:52:02 +00:00
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))?;
}
}
},
2021-01-07 12:18:08 +00:00
// SubpacketValue is non-exhaustive.
2021-06-08 10:52:02 +00:00
u => writeln!(output, "{} Unknown variant: {:?}", i, u)?,
}
2020-01-29 22:47:29 +00:00
match s.value() {
2020-02-08 10:38:26 +00:00
Unknown { .. } => (),
2021-06-08 10:52:02 +00:00
NotationData { .. } => (),
EmbeddedSignature(ref sig) => {
2020-01-29 22:47:29 +00:00
if s.critical() {
write!(output, " (critical)")?;
}
writeln!(output)?;
let indent = format!("{} ", i);
2020-01-29 22:47:29 +00:00
write!(output, "{}", indent)?;
self.dump_packet(output, &indent, None, &sig.clone().into(),
None, None)?;
},
2020-01-29 22:47:29 +00:00
_ => {
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::*;
2020-11-04 21:21:00 +00:00
#[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)?;
},
2020-11-04 21:21:00 +00:00
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)?;
}
},
2021-01-07 12:18:08 +00:00
// S2K is non-exhaustive
2021-06-08 10:52:02 +00:00
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());
2021-06-08 10:52:02 +00:00
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>())
}
2021-01-07 12:18:08 +00:00
}