summaryrefslogtreecommitdiffstats
path: root/sq/src/commands/dump.rs
diff options
context:
space:
mode:
Diffstat (limited to 'sq/src/commands/dump.rs')
-rw-r--r--sq/src/commands/dump.rs946
1 files changed, 946 insertions, 0 deletions
diff --git a/sq/src/commands/dump.rs b/sq/src/commands/dump.rs
new file mode 100644
index 00000000..6f35f56d
--- /dev/null
+++ b/sq/src/commands/dump.rs
@@ -0,0 +1,946 @@
+use std::io::{self, Read};
+
+use sequoia_openpgp as openpgp;
+use self::openpgp::types::{Duration, Timestamp, SymmetricAlgorithm};
+use self::openpgp::fmt::hex;
+use self::openpgp::crypto::mpi;
+use self::openpgp::{Packet, Result};
+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};
+use self::openpgp::crypto::{SessionKey, S2K};
+use self::openpgp::parse::{map::Map, Parse, PacketParserResult};
+
+#[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()
+ }
+}
+
+pub fn dump<W>(input: &mut dyn io::Read, 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: 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 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"])?;
+ },
+ mpi::PublicKey::__Nonexhaustive => unreachable!(),
+ }
+ }
+
+ 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"])?;
+ },
+ mpi::SecretKeyMaterial::__Nonexhaustive =>
+ unreachable!(),
+ }
+ 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"])?;
+ },
+ mpi::Signature::__Nonexhaustive => unreachable!(),
+
+ }
+ }
+ },
+
+ 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, hex::encode(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,
+ 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"])?;
+ },
+ mpi::Ciphertext::__Nonexhaustive => unreachable!(),
+ }
+ }
+ },
+
+ 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()))?;
+ },
+
+ self::openpgp::packet::SKESK::__Nonexhaustive =>
+ unreachable!(),
+ }
+ },
+
+ 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()))?;
+ },
+
+ __Nonexhaustive => unreachable!(),
+ }
+
+ 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())?;