use std::io::{self, Read};
use sequoia_openpgp as openpgp;
use openpgp::KeyHandle;
use openpgp::armor::ReaderMode;
use self::openpgp::fmt::hex;
use self::openpgp::crypto::mpi;
use self::openpgp::{Cert, Packet, Result};
use self::openpgp::packet;
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::S2K;
use self::openpgp::parse::{
Dearmor,
Parse,
PacketParserBuilder,
PacketParserResult,
map::Map,
stream::DecryptionHelper,
};
use crate::Convert;
use crate::cli::types::SessionKey;
use crate::common::ui;
use crate::Sq;
#[derive(Debug)]
pub enum Kind {
Message {
#[allow(dead_code)]
encrypted: bool,
},
Keyring,
Cert,
Unknown,
}
#[allow(clippy::redundant_pattern_matching)]
pub fn dump<W>(sq: &crate::Sq,
secrets: Vec<Cert>,
input: &mut (dyn io::Read + Sync + Send),
output: &mut dyn io::Write,
mpis: bool, hex: bool,
session_keys: Vec<SessionKey>,
width: W)
-> Result<Kind>
where W: Into<Option<usize>>
{
let mut ppr
= self::openpgp::parse::PacketParserBuilder::from_reader(input)?;
if hex {
ppr = ppr.map(true).buffer_unread_content();
}
let mut ppr = ppr.build()?;
let width = width.into().unwrap_or(80);
let mut first_armor_block = true;
let mut is_keyring = true;
let mut helper = crate::commands::decrypt::Helper::new(
&sq, 0, Vec::new(), secrets, session_keys.clone(), false);
loop {
let mut dumper = PacketDumper::new(sq, width, mpis);
let mut message_encrypted = false;
let mut pkesks = vec![];
let mut skesks = vec![];
while let PacketParserResult::Some(mut pp) = ppr {
let additional_fields = match pp.packet {
Packet::PKESK(ref p) => {
pkesks.push(p.clone());
vec![]
},
Packet::SKESK(ref p) => {
skesks.push(p.clone());
vec![]
},
Packet::Literal(_) => {
let mut prefix = vec![0; 40];
let n = pp.read(&mut prefix)?;
vec![
format!("Content: {}{}",
ui::Safe(&prefix[..n]),
if n == prefix.len() { "..." } else { "" }),
]
},
Packet::SEIP(ref s) => {
message_encrypted = true;
let mut success = false;
let mut fields = Vec::new();
let sym_algo_hint = if let SEIP::V2(s2) = s {
Some(s2.symmetric_algo())
} else {
None
};
let mut decryption_proxy = |algo, secret: &_| {
let result = pp.decrypt(algo, secret);
if let Ok(_) = result {
fields.push(
format!("Session key: {}", &hex::encode(secret)));
let algo = sym_algo_hint.or(algo)
.expect("we know either or");
fields.push(format!("Symmetric algo: {}", algo));
fields.push("Decryption successful".into());
success = true;
true
} else {
false
}
};
if let Err(e) = helper.decrypt(&pkesks[..], &skesks[..],
sym_algo_hint, &mut decryption_proxy)
{
for algo in session_keys.iter()
.filter_map(|sk| sk.symmetric_algo)
{
fields.push(format!(
"Indicated Symmetric algo: {}", algo));
}
fields.push(format!("Decryption failed: {}", e));
}
fields
},
_ => Vec::new(),
};
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 {
let is_message = eof.is_message().is_ok() && first_armor_block;
let is_cert = eof.is_cert().is_ok() && first_armor_block;
is_keyring &= eof.is_keyring().is_ok();
first_armor_block = false;
match PacketParserBuilder::from_buffered_reader(eof.into_reader())
.and_then(
|builder| builder
.dearmor(Dearmor::Enabled(
ReaderMode::Tolerant(None)))
.build())
{
Ok(ppr_) => {
writeln!(output, "Note: There is another block of armored \
OpenPGP data.")?;
if is_message {
writeln!(output, "Note: Data concatenated to a message is \
likely an error.")?;
} else if is_cert || is_keyring {
writeln!(output, "Note: This is a non-standard extension \
to OpenPGP.")?;
}
writeln!(output)?;
ppr = ppr_;
continue;
},
Err(_) => break if is_message {
Ok(Kind::Message {
encrypted: message_encrypted,
})
} else if is_cert {
Ok(Kind::Cert)
} else if is_keyring {
Ok(Kind::Keyring)
} else {
Ok(Kind::Unknown)
}
}
} else {
unreachable!()
}
}
}
struct Node {
header: Header,
packet: Packet,
map: Option<Map>,
additional_fields: Vec<String>,
children: Vec<Node>,
}
impl Node {
fn new(header: Header, packet: Packet, map: Option<Map>,
additional_fields: 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<'a, 'b, 'c> {
sq: &'a Sq<'b, 'c>,
width: usize,
mpis: bool,
root: Option<Node>,
}
impl<'a, 'b, 'c> PacketDumper<'a, 'b, 'c> {
pub fn new(sq: &'a Sq<'b, 'c>, width: usize, mpis: bool) -> Self {
PacketDumper {
sq,
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: 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)?;
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: &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 {
"".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)?;
#[allow(deprecated)]
match p {
Unknown(ref u) => {
writeln!(output, "{} Tag: {}", i, u.tag())?;
writeln!(output, "{} Error: {}", i, u.error())?;
},
PublicKey(ref k) => self.dump_key(output, i, k)?,
PublicSubkey(ref k) => self.dump_key(output, i, k)?,
SecretKey(ref k) => self.dump_key(output, i, k)?,
SecretSubkey(ref k) => self.dump_key(output, i, k)?,
Signature(s) => self.dump_signature(output, i, s)?,
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())?;
if let packet::OnePassSig::V6(o) = o {
writeln!(output, "{} Salt: {}", i, hex::encode(o.salt()))?;
}
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,
ui::Safe(u.value()))?;
},
UserAttribute(ref u) => {
use self::openpgp::packet::user_attribute::{Subpacket, Image};
use openpgp::serialize::MarshalInto;
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())?,
_ =>
writeln!(output,
"{} Unknown image: {} bytes", i,
image.serialized_len())?,
},
Ok(Subpacket::Unknown(n, data)) =>
writeln!(output,
"{} Unknown subpacket({}): {} bytes", i,
n, data.len())?,
Ok(u) =>
writeln!(output,
"{} Unknown subpacket: {} bytes", i,
u.serialized_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,
ui::Safe(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().as_ref().map(ToString::to_string)
.unwrap_or_else(|| "<anonymous recipient>".into()))?;
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::X25519 { e, key } =>
self.dump_mpis(output, &ii,
&[&e[..], key],
&["e", "key"])?,
mpi::Ciphertext::X448 { e, key } =>
self.dump_mpis(output, &ii,
&[&e[..], key],
&["e", "key"])?,
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"])?;
},
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::V6(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()))?;
writeln!(output, "{} ESK: {}", i,
hex::encode(s.esk()))?;
},
u => writeln!(output, "{} Unknown variant: {:?}", i, u)?,
}
},
SEIP(ref s) => {
writeln!(output, "{} Version: {}", i, s.version())?;
match s {
packet::SEIP::V1(_) => (),
packet::SEIP::V2(s) => {
writeln!(output, "{} Symmetric algo: {}", i, s.symmetric_algo())?;
writeln!(output, "{} AEAD algo: {}", i, s.aead())?;
writeln!(output, "{} Chunk size: {}", i, s.chunk_size())?;
writeln!(output, "{} Salt: {}", i, hex::encode(s.salt()))?;
},
_ => (),
}
},
MDC(ref m) => {
writeln!(output, "{} Digest: {}",
i, hex::encode(m.digest()))?;
writeln!(output, "{} Computed digest: {}",
i, hex::encode(m.computed_digest()))?;
writeln!(output, "{} Valid: {}",
i, m.valid())?;
},
Padding(_) => {
}
u => writeln!(output, "{} Unknown variant: {:?}", i, u)?,
}
for field in additional_fields {
writeln!(output, "{} {}", i, field)?;
}
writeln!(output, "{}", i)?;
if let Some(map) = map {
if map.iter().next().is_none() {
return Ok(());
}
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 checked that there is 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)?;
}
Ok(())
}
fn dump_key<P, R>(&self, output: &mut dyn io::Write, i: &str,
k: &Key<P, R>)
-> Result<()>
where
P: key::KeyParts,
R: key::KeyRole,
{
self.dump_key_internal(
output, i, k.parts_as_unspecified().role_as_unspecified())
}
fn dump_key_internal(&self, output: &mut dyn io::Write, i: &str,
k: &Key<key::UnspecifiedParts, key::UnspecifiedRole>)
-> Result<()>
{
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 self.mpis {
writeln!(output, "{}", i)?;
writeln!(output, "{} Public Key:", i)?;
let ii = format!("{} ", i);
match k.mpis() {
mpi::PublicKey::X25519 { u } =>
self.dump_mpis(output, &ii, &[u], &["u"])?,
mpi::PublicKey::X448 { u } =>
self.dump_mpis(output, &ii, &[&u[..]], &["u"])?,
mpi::PublicKey::Ed25519 { a } =>
self.dump_mpis(output, &ii, &[a], &["a"])?,
mpi::PublicKey::Ed448 { a } =>
self.dump_mpis(output, &ii, &[&a[..]], &["a"])?,
mpi::PublicKey::RSA { e, n } =>
self.dump_mpis(output, &ii,
&[e.value(), n.value()],
&["e", "n"])?,
mpi::PublicKey::DSA { p, q, g, y } =>
self.dump_mpis(output, &ii,
&[p.value(), q.value(), g.value(),
y.value()],
&["p", "q", "g", "y"])?,
mpi::PublicKey::ElGamal { p, g, y } =>
self.dump_mpis(output, &ii,
&[p.value(), g.value(), y.value()],
&["p", "g", "y"])?,
mpi::PublicKey::EdDSA { curve, q } => {
writeln!(output, "{} Curve: {}", ii, curve)?;
self.dump_mpis(output, &ii, &[q.value()], &["q"])?;
},
mpi::PublicKey::ECDSA { curve, q } => {
writeln!(output, "{} Curve: {}", ii, curve)?;
self.dump_mpis(output, &ii, &[q.value()], &["q"])?;
},
mpi::PublicKey::ECDH { curve, q, hash, sym } => {
writeln!(output, "{} Curve: {}", ii, curve)?;
writeln!(output, "{} KDF hash algo: {}", ii, hash)?;
writeln!(output, "{} KEK symmetric algo: {}", ii,
sym)?;
self.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();
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"])?;
},
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 self.mpis {
u.map(|mpis| -> Result<()> {
match mpis
{
mpi::SecretKeyMaterial::X25519 { x } =>
self.dump_mpis(output, &ii,
&[x], &["x"])?,
mpi::SecretKeyMaterial::X448 { x } =>
self.dump_mpis(output, &ii,
&[&x[..]], &["x"])?,
mpi::SecretKeyMaterial::Ed25519 { x } =>
self.dump_mpis(output, &ii,
&[x], &["x"])?,
mpi::SecretKeyMaterial::Ed448 { x } =>
self.dump_mpis(output, &ii,
&[&x[..]], &["x"])?,
mpi::SecretKeyMaterial::RSA { d, p, q, u } =>
self.dump_mpis(output, &ii,
&[d.value(), p.value(),
q.value(), u.value()],
&["d", "p", "q", "u"])?,
mpi::SecretKeyMaterial::DSA { x } =>
self.dump_mpis(output, &ii, &[x.value()],
&["x"])?,
mpi::SecretKeyMaterial::ElGamal { x } =>
self.dump_mpis(output, &ii, &[x.value()],
&["x"])?,
mpi::SecretKeyMaterial::EdDSA { scalar } =>
self.dump_mpis(output, &ii,
&[scalar.value()],
&["scalar"])?,
mpi::SecretKeyMaterial::ECDSA { scalar } =>
self.dump_mpis(output, &ii,
&[scalar.value()],
&["scalar"])?,
mpi::SecretKeyMaterial::ECDH { scalar } =>
self.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();
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"])?;
},
u => writeln!(output, "{}Unknown variant: {:?}", ii, u)?,
}
Ok(())
})?;
}
}
SecretKeyMaterial::Encrypted(ref e) => {
writeln!(output, "{}", i)?;
writeln!(output, "{} Encrypted", ii)?;
write!(output, "{} S2K: ", ii)?;
self.dump_s2k(output, &ii, e.s2k())?;
writeln!(output, "{} Sym. algo: {}", ii,
e.algo())?;
if self.mpis {
if let Ok(ciphertext) = e.ciphertext() {
self.dump_mpis(output, &ii, &[ciphertext],
&["ciphertext"])?;
}
}
},
}
}
Ok(())
}
pub fn dump_signature(&self, output: &mut dyn io::Write, i: &str,
s: &Signature)
-> Result<()>
{
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()))?;
if let packet::Signature::V6(s) = s {
writeln!(output, "{} Salt: {}", i, hex::encode(s.salt()))?;
}
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::Ed25519 { s } =>
self.dump_mpis(output, &ii, &[&s[..]], &["s"])?,
mpi::Signature::Ed448 { s } =>
self.dump_mpis(output, &ii, &[&s[..]], &["s"])?,
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"])?;
},
u => writeln!(output, "{}Unknown variant: {:?}", ii, u)?,
}
}
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(())
};
#[allow(deprecated)]
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 + (*t).into()).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,
ui::Safe(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) => {
let (userid, cert)
= self.sq.best_userid_for(&KeyHandle::from(is), None, true);
write!(output, "{} Issuer: {}", i, is)?;
if cert.is_ok() {
writeln!(output)?;
write!(output, "{} {}", i, userid.display())?;
}
}
NotationData(n) => if n.flags().human_readable() {
write!(output, "{} Notation: {}", i, ui::Safe(n))?;
if s.critical() {
write!(output, " (critical)")?;
}
writeln!(output)?;
} else {
write!(output, "{} Notation: {}", i, ui::Safe(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,
ui::Safe(k))?,
PrimaryUserID(p) =>
write!(output, "{} Primary User ID: {}", i, p)?,
PolicyURI(ref p) =>
write!(output, "{} Policy URI: {}", i, ui::Safe(p))?,
KeyFlags(ref k) =>
write!(output, "{} Key flags: {:?}", i, k)?,
SignersUserID(ref u) =>
write!(output, "{} Signer's User ID: {}", i,
ui::Safe(u))?,
ReasonForRevocation{code, ref reason} => {
write!(output, "{} Reason for revocation: {}{}{}", i, code,
if reason.len() > 0 { ", " } else { "" },
ui::Safe(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(_) =>
write!(output, "{} Embedded signature: ", i)?,
IssuerFingerprint(ref fp) => {
let (userid, cert)
= self.sq.best_userid_for(&KeyHandle::from(fp), None, true);
write!(output, "{} Issuer Fingerprint: {}", i, fp)?;
if cert.is_ok() {
writeln!(output)?;
write!(output, "{} {}", i, userid.display())?;
}
}
IntendedRecipient(ref fp) =>
write!(output, "{} Intended Recipient: {}", i, fp)?,
ApprovedCertifications(digests) => {
write!(output, "{} Approved Certifications:", i)?;
if digests.is_empty() {
writeln!(output, " None")?;
} else {
writeln!(output)?;
for d in digests {
writeln!(output, "{} {}", i, hex::encode(d))?;
}
}
},
PreferredAEADCiphersuites(p) =>
write!(output, "{} AEAD preferences: {}", i,
p.iter().map(|(symm, aead)|
format!("{:?}+{:?}", symm, aead))
.collect::<Vec<String>>().join(", "))?,
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_signature(output, &indent, sig)?;
},
_ => {
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 {
Implicit => {
writeln!(output, "Implicit")?;
},
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)?;
},
Argon2 { salt, t, p, m } => {
writeln!(output, "Argon2")?;
writeln!(output, "{} Salt: {}", i, hex::encode(salt))?;
writeln!(output, "{} Passes: {}", i, t)?;
writeln!(output, "{} Parallelism: {}", i, p)?;
writeln!(output, "{} Memory: {} ({} KiB)", i, m,
2usize.pow((*m).into()))?;
},
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)?;
}
},
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(())
}
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 - max_label_len as isize,
i.len() as isize),
) as usize;
format!("{} ", &i.chars().take(amount).collect::<String>())
}
}