summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLars Wirzenius <liw@liw.fi>2021-08-10 14:23:10 +0300
committerLars Wirzenius <liw@liw.fi>2021-08-19 17:28:28 +0300
commitea106c49c2f625725afd6fdbb1f2d0162320e406 (patch)
tree2186f32dc035c92aca9ccc3c1a195d665351ca9e
parente09134c41b3ed37e31a3cd8727119d427daadf0c (diff)
sq: allow user to request JSON output for "sq inspect"liw/inspect-json
Split the logic of parsing the OpenPGP package stream and the code to produce output so they're not as intermingled. Add the --output-format option to "sq inspect" to make output use the JSON format. Fixes #737
-rw-r--r--Cargo.lock5
-rw-r--r--sq/Cargo.toml2
-rw-r--r--sq/src/commands/inspect.rs574
-rw-r--r--sq/src/sq-usage.rs7
-rw-r--r--sq/src/sq_cli.rs5
5 files changed, 481 insertions, 112 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 0e4a5580..16deb2a1 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2199,6 +2199,8 @@ dependencies = [
"sequoia-autocrypt",
"sequoia-net",
"sequoia-openpgp",
+ "serde",
+ "serde_json",
"tempfile",
"term_size",
"tokio",
@@ -2228,6 +2230,9 @@ name = "serde"
version = "1.0.125"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "558dc50e1a5a5fa7112ca2ce4effcb321b0300c0d4ccf0776a9f60cd89031171"
+dependencies = [
+ "serde_derive",
+]
[[package]]
name = "serde_cbor"
diff --git a/sq/Cargo.toml b/sq/Cargo.toml
index 03cd657a..2bf3fa74 100644
--- a/sq/Cargo.toml
+++ b/sq/Cargo.toml
@@ -34,6 +34,8 @@ anyhow = "1.0.18"
chrono = "0.4.10"
clap = { version = "2.33", features = ["wrap_help"] }
itertools = "0.9"
+serde = { version = "1.0", features = ["derive"] }
+serde_json = { version = "1", features = ["std"] }
tempfile = "3.1"
term_size = "0.3"
tokio = { version = "0.2.19", features = ["rt-core", "io-util", "io-driver"], optional = true }
diff --git a/sq/src/commands/inspect.rs b/sq/src/commands/inspect.rs
index e6e86d01..73c4c876 100644
--- a/sq/src/commands/inspect.rs
+++ b/sq/src/commands/inspect.rs
@@ -1,14 +1,22 @@
use std::convert::TryFrom;
use std::io::{self, Read};
+use serde::Serialize;
+use std::collections::HashMap;
-
+use openpgp::types::{
+ PublicKeyAlgorithm,
+ ReasonForRevocation,
+ SignatureType,
+};
use sequoia_openpgp as openpgp;
use crate::openpgp::{KeyHandle, Packet, Result};
use crate::openpgp::cert::prelude::*;
use openpgp::packet::{
Signature,
key::PublicParts,
+ UserID,
+ UserAttribute,
};
use crate::openpgp::parse::{Parse, PacketParserResult};
use crate::openpgp::policy::Policy;
@@ -18,11 +26,13 @@ use super::dump::Convert;
pub fn inspect(m: &clap::ArgMatches, policy: &dyn Policy, output: &mut dyn io::Write)
-> Result<()> {
+ let mut buffer = Buffer::new();
+
let print_certifications = m.is_present("certifications");
let input = m.value_of("input");
let input_name = input.unwrap_or("-");
- write!(output, "{}: ", input_name)?;
+ buffer.filename(input_name);
let mut type_called = false; // Did we print the type yet?
let mut encrypted = false; // Is it an encrypted message?
@@ -41,14 +51,14 @@ pub fn inspect(m: &clap::ArgMatches, policy: &dyn Policy, output: &mut dyn io::W
&& pp.possible_keyring().is_ok()
{
if ! type_called {
- writeln!(output, "OpenPGP Keyring.")?;
- writeln!(output)?;
+ buffer.openpgp_keyring();
+ buffer.separator();
type_called = true;
}
let pp = openpgp::PacketPile::from(
::std::mem::replace(&mut packets, Vec::new()));
let cert = openpgp::Cert::try_from(pp)?;
- inspect_cert(policy, output, &cert,
+ inspect_cert(policy, &mut buffer, &cert,
print_certifications)?;
}
},
@@ -83,147 +93,152 @@ pub fn inspect(m: &clap::ArgMatches, policy: &dyn Policy, output: &mut dyn io::W
let is_keyring = eof.is_keyring();
if is_message.is_ok() {
- writeln!(output, "{}OpenPGP Message.",
- match (encrypted, ! sigs.is_empty()) {
- (false, false) => "",
- (false, true) => "Signed ",
- (true, false) => "Encrypted ",
- (true, true) => "Encrypted and signed ",
- })?;
- writeln!(output)?;
+ match (encrypted, ! sigs.is_empty()) {
+ (false, false) => buffer.openpgp_message(),
+ (false, true) => buffer.signed_openpgp_message(),
+ (true, false) => buffer.encrypted_openpgp_message(),
+ (true, true) => buffer.encrypted_signed_openpgp_msg(),
+ }
+ buffer.separator();
if n_skesks > 0 {
- writeln!(output, " Passwords: {}", n_skesks)?;
+ buffer.passwords(n_skesks);
}
for pkesk in pkesks.iter() {
- writeln!(output, " Recipient: {}", pkesk.recipient())?;
+ buffer.recipient(pkesk.recipient());
}
- inspect_signatures(output, &sigs)?;
+ inspect_signatures(&mut buffer, &sigs)?;
if ! literal_prefix.is_empty() {
- writeln!(output, " Data: {:?}{}",
- String::from_utf8_lossy(&literal_prefix),
- if literal_prefix.len() == 40 { "..." } else { "" })?;
+ let data = String::from_utf8_lossy(&literal_prefix);
+ let suffix = if literal_prefix.len() == 40 { "..." } else { "" };
+ buffer.data(&data, suffix);
}
} else if is_cert.is_ok() || is_keyring.is_ok() {
let pp = openpgp::PacketPile::from(packets);
let cert = openpgp::Cert::try_from(pp)?;
- inspect_cert(policy, output, &cert,
+ inspect_cert(policy, &mut buffer, &cert,
print_certifications)?;
} else if packets.is_empty() && ! sigs.is_empty() {
- writeln!(output, "Detached signature{}.",
- if sigs.len() > 1 { "s" } else { "" })?;
- writeln!(output)?;
- inspect_signatures(output, &sigs)?;
+ if sigs.len() == 1 {
+ buffer.detached_signature();
+ } else {
+ buffer.detached_signatures();
+ }
+ buffer.separator();
+ inspect_signatures(&mut buffer, &sigs)?;
} else if packets.is_empty() {
- writeln!(output, "No OpenPGP data.")?;
+ buffer.no_openpgp_data();
} else {
- writeln!(output, "Unknown sequence of OpenPGP packets.")?;
- writeln!(output, " Message: {}", is_message.unwrap_err())?;
- writeln!(output, " Cert: {}", is_cert.unwrap_err())?;
- writeln!(output, " Keyring: {}", is_keyring.unwrap_err())?;
- writeln!(output)?;
- writeln!(output, "Hint: Try 'sq packet dump {}'", input_name)?;
+ buffer.unknown_packet_sequence(is_message,
+ is_cert,
+ is_keyring,
+ input_name);
+
}
} else {
unreachable!()
}
+ if let Some("json") = m.value_of("output-format") {
+ buffer.write_json(output)?;
+ } else {
+ buffer.write_human(output)?;
+ }
Ok(())
}
fn inspect_cert(policy: &dyn Policy,
- output: &mut dyn io::Write, cert: &openpgp::Cert,
+ buffer: &mut Buffer,
+ cert: &openpgp::Cert,
print_certifications: bool) -> Result<()> {
if cert.is_tsk() {
- writeln!(output, "Transferable Secret Key.")?;
+ buffer.transferable_secret_key();
} else {
- writeln!(output, "OpenPGP Certificate.")?;
+ buffer.openpgp_certificate();
}
- writeln!(output)?;
- writeln!(output, " Fingerprint: {}", cert.fingerprint())?;
- inspect_revocation(output, "", cert.revocation_status(policy, None))?;
- inspect_key(policy, output, "", cert.keys().next().unwrap(),
+ buffer.separator();
+ buffer.fingerprint(cert.fingerprint());
+ inspect_revocation(buffer, cert.revocation_status(policy, None))?;
+ inspect_key(policy, buffer, cert.keys().next().unwrap(),
print_certifications)?;
- writeln!(output)?;
+ buffer.separator();
for vka in cert.keys().subkeys().with_policy(policy, None) {
- writeln!(output, " Subkey: {}", vka.key().fingerprint())?;
- inspect_revocation(output, "", vka.revocation_status())?;
- inspect_key(policy, output, "", vka.into_key_amalgamation().into(),
+ buffer.subkey(vka.key().fingerprint());
+ inspect_revocation(buffer, vka.revocation_status())?;
+ inspect_key(policy, buffer, vka.into_key_amalgamation().into(),
print_certifications)?;
- writeln!(output)?;
+ buffer.separator();
}
- fn print_error_chain(output: &mut dyn io::Write, err: &anyhow::Error)
+ fn print_error_chain(buffer: &mut Buffer, err: &anyhow::Error)
-> Result<()> {
- writeln!(output, " Invalid: {}", err)?;
+ buffer.invalid_certificate(err);
for cause in err.chain().skip(1) {
- writeln!(output, " because: {}", cause)?;
+ buffer.invalid_certificate_cause(cause);
}
Ok(())
}
for uidb in cert.userids() {
- writeln!(output, " UserID: {}", uidb.userid())?;
- inspect_revocation(output, "", uidb.revocation_status(policy, None))?;
+ buffer.userid(uidb.userid());
+ inspect_revocation(buffer, uidb.revocation_status(policy, None))?;
match uidb.binding_signature(policy, None) {
Ok(sig) => if let Err(e) =
sig.signature_alive(None, std::time::Duration::new(0, 0))
{
- print_error_chain(output, &e)?;
+ print_error_chain(buffer, &e)?;
}
- Err(e) => print_error_chain(output, &e)?,
+ Err(e) => print_error_chain(buffer, &e)?,
}
- inspect_certifications(output,
+ inspect_certifications(buffer,
uidb.certifications(),
print_certifications)?;
- writeln!(output)?;
+ buffer.separator();
}
for uab in cert.user_attributes() {
- writeln!(output, " User attribute: {:?}",
- uab.user_attribute())?;
- inspect_revocation(output, "", uab.revocation_status(policy, None))?;
+ buffer.user_attribute(uab.user_attribute());
+ inspect_revocation(buffer, uab.revocation_status(policy, None))?;
match uab.binding_signature(policy, None) {
Ok(sig) => if let Err(e) =
sig.signature_alive(None, std::time::Duration::new(0, 0))
{
- print_error_chain(output, &e)?;
+ print_error_chain(buffer, &e)?;
}
- Err(e) => print_error_chain(output, &e)?,
+ Err(e) => print_error_chain(buffer, &e)?,
}
- inspect_certifications(output,
+ inspect_certifications(buffer,
uab.certifications(),
print_certifications)?;
- writeln!(output)?;
+ buffer.separator();
}
for ub in cert.unknowns() {
- writeln!(output, " Unknown component: {:?}", ub.unknown())?;
+ buffer.unknown(ub.unknown());
match ub.binding_signature(policy, None) {
Ok(sig) => if let Err(e) =
sig.signature_alive(None, std::time::Duration::new(0, 0))
{
- print_error_chain(output, &e)?;
+ print_error_chain(buffer, &e)?;
}
- Err(e) => print_error_chain(output, &e)?,
+ Err(e) => print_error_chain(buffer, &e)?,
}
- inspect_certifications(output,
+ inspect_certifications(buffer,
ub.certifications(),
print_certifications)?;
- writeln!(output)?;
+ buffer.separator();
}
for bad in cert.bad_signatures() {
- writeln!(output, " Bad Signature: {:?}", bad)?;
+ buffer.bad_signature(bad);
}
Ok(())
}
fn inspect_key(policy: &dyn Policy,
- output: &mut dyn io::Write,
- indent: &str,
+ buffer: &mut Buffer,
ka: ErasedKeyAmalgamation<PublicParts>,
print_certifications: bool)
-> Result<()>
@@ -233,76 +248,69 @@ fn inspect_key(policy: &dyn Policy,
let vka = match ka.with_policy(policy, None) {
Ok(vka) => {
if let Err(e) = vka.alive() {
- writeln!(output, "{} Invalid: {}", indent, e)?;
+ buffer.invalid_key(&e);
}
Some(vka)
},
Err(e) => {
- writeln!(output, "{} Invalid: {}", indent, e)?;
+ buffer.invalid_key(&e);
None
},
};
- writeln!(output, "{}Public-key algo: {}", indent, key.pk_algo())?;
+ buffer.pk_algo(key.pk_algo());
if let Some(bits) = key.mpis().bits() {
- writeln!(output, "{}Public-key size: {} bits", indent, bits)?;
+ buffer.pk_bits(bits);
}
if let Some(secret) = key.optional_secret() {
- writeln!(output, "{} Secret key: {}",
- indent,
- if let SecretKeyMaterial::Unencrypted(_) = secret {
- "Unencrypted"
- } else {
- "Encrypted"
- })?;
- }
- writeln!(output, "{} Creation time: {}", indent,
- key.creation_time().convert())?;
+ if let SecretKeyMaterial::Unencrypted(_) = secret {
+ buffer.unencrypted_secret_key();
+ } else {
+ buffer.encrypted_secret_key();
+ }
+ }
+ buffer.creation_time(key.creation_time().convert().to_string());
if let Some(vka) = vka {
if let Some(expires) = vka.key_validity_period() {
let expiration_time = key.creation_time() + expires;
- writeln!(output, "{}Expiration time: {} (creation time + {})",
- indent,
- expiration_time.convert(),
- expires.convert())?;
+ buffer.expiration_time(format!("{} (creation time + {})",
+ expiration_time.convert(),
+ expires.convert()));
}
if let Some(flags) = vka.key_flags().and_then(inspect_key_flags) {
- writeln!(output, "{} Key flags: {}", indent, flags)?;
+ buffer.key_flags(flags);
}
}
- inspect_certifications(output, bundle.certifications().iter(),
+ inspect_certifications(buffer, bundle.certifications().iter(),
print_certifications)?;
Ok(())
}
-fn inspect_revocation(output: &mut dyn io::Write,
- indent: &str,
+fn inspect_revocation(buffer: &mut Buffer,
revoked: openpgp::types::RevocationStatus)
-> Result<()> {
use crate::openpgp::types::RevocationStatus::*;
- fn print_reasons(output: &mut dyn io::Write, indent: &str,
- sigs: &[&Signature])
+ fn print_reasons(buffer: &mut Buffer, sigs: &[&Signature])
-> Result<()> {
for sig in sigs {
if let Some((r, _)) = sig.reason_for_revocation() {
- writeln!(output, "{} - {}", indent, r)?;
+ buffer.revocation_reason(r);
} else {
- writeln!(output, "{} - No reason specified",
- indent)?;
+ buffer.unknown_revocation_reason();
}
}
Ok(())
}
match revoked {
Revoked(sigs) => {
- writeln!(output, "{} Revoked:", indent)?;
- print_reasons(output, indent, &sigs)?;
+ buffer.revoked();
+ print_reasons(buffer, &sigs)?;
},
CouldBe(sigs) => {
- writeln!(output, "{} Possibly revoked:", indent)?;
- print_reasons(output, indent, &sigs)?;
+ buffer.maybe_revoked();
+ print_reasons(buffer, &sigs)?;
},
NotAsFarAsWeKnow => (),
}
@@ -341,14 +349,14 @@ fn inspect_key_flags(flags: openpgp::types::KeyFlags) -> Option<String> {
}
}
-fn inspect_signatures(output: &mut dyn io::Write,
+fn inspect_signatures(buffer: &mut Buffer,
sigs: &[openpgp::packet::Signature]) -> Result<()> {
use crate::openpgp::types::SignatureType::*;
for sig in sigs {
match sig.typ() {
Binary | Text => (),
signature_type @ _ =>
- writeln!(output, " Kind: {}", signature_type)?,
+ buffer.signature_type(signature_type),
}
let mut fps: Vec<_> = sig.issuer_fingerprints().collect();
@@ -356,26 +364,25 @@ fn inspect_signatures(output: &mut dyn io::Write,
fps.dedup();
let fps: Vec<KeyHandle> = fps.into_iter().map(|fp| fp.into()).collect();
for fp in fps.iter() {
- writeln!(output, " Alleged signer: {}", fp)?;
+ buffer.alleged_signer_by_key_handle(fp);
}
let mut keyids: Vec<_> = sig.issuers().collect();
keyids.sort();
keyids.dedup();
for keyid in keyids {
if ! fps.iter().any(|fp| fp.aliases(&keyid.into())) {
- writeln!(output, " Alleged signer: {}", keyid)?;
+ buffer.alleged_signer_by_key_id(keyid);
}
}
}
if ! sigs.is_empty() {
- writeln!(output, " Note: \
- Signatures have NOT been verified!")?;
+ buffer.unverified_signers();
}
Ok(())
}
-fn inspect_certifications<'a, A>(output: &mut dyn io::Write,
+fn inspect_certifications<'a, A>(buffer: &mut Buffer,
certs: A,
print_certifications: bool) -> Result<()> where
A: std::iter::Iterator<Item=&'a openpgp::packet::Signature> {
@@ -388,28 +395,373 @@ fn inspect_certifications<'a, A>(output: &mut dyn io::Write,
fps.dedup();
let fps: Vec<KeyHandle> = fps.into_iter().map(|fp| fp.into()).collect();
for fp in fps.iter() {
- writeln!(output, "Alleged certifier: {}", fp)?;
+ buffer.alleged_certifier_by_key_handle(fp);
}
let mut keyids: Vec<_> = sig.issuers().collect();
keyids.sort();
keyids.dedup();
for keyid in keyids {
if ! fps.iter().any(|fp| fp.aliases(&keyid.into())) {
- writeln!(output, "Alleged certifier: {}", keyid)?;
+ buffer.alleged_certifier_by_key_id(keyid);
}
}
}
if emit_warning {
- writeln!(output, " Note: \
- Certifications have NOT been verified!")?;
+ buffer.not_certified();
}
} else {
let count = certs.count();
if count > 0 {
- writeln!(output, " Certifications: {}, \
- use --certifications to list", count)?;
+ buffer.num_certification(count);
}
}
Ok(())
}
+
+// An atomic bit of information for output.
+#[derive(Serialize)]
+enum Atom {
+ // A key/value pair.
+ KeyValue(String, String),
+
+ // A note.
+ Note(String),
+
+ // A separator between groups of atoms that belong together.
+ Separator,
+}
+
+impl Atom {
+ // Creates a key/value pair.
+ fn pair(a: &str, b: String) -> Self {
+ Self::KeyValue(a.to_string(), b)
+ }
+
+ // Creates a note: a value without a key.
+ fn note(txt: &str) -> Self {
+ Self::Note(txt.to_string())
+ }
+
+ // Creates a separator.
+ fn separator() -> Self {
+ Self::Separator
+ }
+}
+
+// A sequence of output atoms.
+//
+// The sequence can be written out in different formats. The logic to
+// parse a sequence of OpenPGP packets appends atoms to the output
+// buffer and the buffer can then be formatted in various ways for
+// output.
+#[derive(Serialize)]
+struct Buffer {
+ atoms: Vec<Atom>,
+}
+
+impl Buffer {
+ // Creates a new output buffer.
+ fn new() -> Self {
+ Self { atoms: vec![] }
+ }
+
+ // Writes output buffer as human-readable text.
+ fn write_human(&self, output: &mut dyn io::Write) -> Result<()> {
+ for atom in self.atoms.iter() {
+ match atom {
+ Atom::KeyValue(k, v) =>
+ write!(output, "{}: {}\n", k, v)?,
+ Atom::Note(note) => write!(output, "{}\n", note)?,
+ Atom::Separator => write!(output, "\n")?,
+ }
+ }
+ Ok(())
+ }
+
+ // Writes output buffer as JSON.
+ //
+ // Splits the output buffer into groups, based on separators. Each
+ // group results in an object (map), and the sequence of groups is
+ // a list.
+ fn write_json(&self, output: &mut dyn io::Write) -> Result<()> {
+ let note = "Note".to_string();
+ let mut maps = vec![];
+ for v in self.split_groups() {
+ let mut map = HashMap::new();
+ for atom in v.iter() {
+ match atom {
+ Atom::KeyValue(k, v) => {
+ map.insert(k, v);
+ },
+ Atom::Note(text) => {
+ map.insert(&note, text);
+ },
+ Atom::Separator => (),
+ }
+ }
+ maps.push(map);
+ }
+
+ write!(output, "{}", serde_json::to_string(&maps)?)?;
+ serde_json::to_writer(output, &maps)?;
+ Ok(())
+ }
+
+ // Splits output buffer at separators into a groups of atoms that
+ // belong together.
+ fn split_groups(&self) -> Vec<Vec<&Atom>> {
+ let mut vecs = vec![];
+ let mut cur = vec![];
+
+ for atom in self.atoms.iter() {
+ if let Atom::Separator = atom {
+ vecs.push(cur.clone());
+ cur.clear();
+ } else {
+ cur.push(atom.clone());
+ }
+ }
+
+ if ! cur.is_empty() {
+ vecs.push(cur);
+ }
+
+ vecs
+ }
+
+ // The rest of the functions append atoms of different kinds to
+ // the output buffer.
+
+ fn alleged_certifier_by_key_handle(&mut self, who: &KeyHandle) {
+ self.atoms.push(
+ Atom::pair("Alleged certifier", format!("{}", who)));
+ }
+
+ fn alleged_certifier_by_key_id(&mut self, who: &openpgp::KeyID) {
+ self.atoms.push(
+ Atom::pair("Alleged certifier", format!("{}", who)));
+ }
+
+ fn alleged_signer_by_key_handle(&mut self, who: &KeyHandle) {
+ self.atoms.push(
+ Atom::pair("Alleged signer", format!("{}", who)));
+ }
+
+ fn alleged_signer_by_key_id(&mut self, who: &openpgp::KeyID) {
+ self.atoms.push(
+ Atom::pair("Alleged signer", format!("{}", who)));
+ }
+
+ fn bad_signature(&mut self, bad: &openpgp::packet::Signature) {
+ self.atoms.push(
+ Atom::pair("Bad signature", format!("{:?}", bad)));
+ }
+
+ fn creation_time(&mut self, time: String) {
+ self.atoms.push(
+ Atom::pair("Creation time", format!("{}", time)));
+ }
+
+ fn data(&mut self, data: &str, suffix: &str) {
+ self.atoms.push(
+ Atom::pair("Data", format!("{}{}", data, suffix)));
+ }
+
+ fn detached_signature(&mut self) {
+ self.atoms.push(
+ Atom::pair("File type", "Detached signature".to_string()));
+ }
+
+ fn detached_signatures(&mut self) {
+ self.atoms.push(
+ Atom::pair("File type", "Detached signatures".to_string()));
+ }
+
+ fn encrypted_openpgp_message(&mut self) {
+ self.atoms.push(
+ Atom::pair("File type",
+ "Encrypted OpenPGP Message".to_string()));
+ }
+
+ fn encrypted_secret_key(&mut self) {
+ self.atoms.push(
+ Atom::pair("Secret key", "Encrypted".to_string()));
+ }
+
+ fn encrypted_signed_openpgp_msg(&mut self) {
+ let atom =Atom::pair(
+ "File type",
+ "Encrypted and signed OpenPGP Message"
+ .to_string());
+ self.atoms.push(atom);
+ }
+
+ fn expiration_time(&mut self, time: String) {
+ self.atoms.push(
+ Atom::pair("Expiration time", format!("{}", time)));
+ }
+
+ fn filename(&mut self, filename: &str) {
+ self.atoms.push(Atom::pair("Filename", filename.to_string()));
+ }
+
+ fn fingerprint(&mut self, fp: openpgp::Fingerprint) {
+ self.atoms.push(Atom::pair("Fingerprint", format!("{}", fp)));
+ }
+
+ fn invalid_certificate(&mut self, err: &anyhow::Error) {
+ self.atoms.push(Atom::pair("Invalid", format!("{}", err)));
+ }
+
+ fn invalid_certificate_cause(&mut self,
+ cause: &dyn std::error::Error) {
+ self.atoms.push(Atom::pair("because", format!("{}", cause)));
+ }
+
+ fn invalid_key(&mut self, error: &anyhow::Error) {
+ self.atoms.push(Atom::pair("Invalid", format!("{}", error)));
+ }
+
+ fn key_flags(&mut self, flags: String) {
+ self.atoms.push(Atom::pair("Key flags", flags.clone()));
+ }
+
+ fn maybe_revoked(&mut self) {
+ self.atoms.push(Atom::note("Possibly revoked"));
+ }
+
+ fn no_openpgp_data(&mut self) {
+ self.atoms.push(Atom::note("No OpenPGP data"));
+ }
+
+ fn not_certified(&mut self) {
+ self.atoms.push(
+ Atom::note("Certifications have NOT been verified"));
+ }
+
+ fn num_certification(&mut self, count: usize) {
+ self.atoms.push(
+ Atom::pair("Certifications", format!("{}", count)));
+ }
+
+ fn openpgp_certificate(&mut self) {
+ self.atoms.push(
+ Atom::pair("File type", "OpenPGP Certificate".to_string()));
+ }
+
+ fn openpgp_keyring(&mut self) {
+ self.atoms.push(
+ Atom::pair("File type", "OpenPGP keyring".to_string()));
+ }
+
+ fn openpgp_message(&mut self) {
+ self.atoms.push(
+ Atom::pair("File type", "OpenPGP Message".to_string()));
+ }
+
+ fn passwords(&mut self, n: usize) {
+ self.atoms.push(Atom::pair("Passwords", format!("{}", n)));
+ }
+
+ fn pk_algo(&mut self, algo: PublicKeyAlgorithm) {
+ self.atoms.push(
+ Atom::pair("Public key algorithm", format!("{}", algo)));
+ }
+
+ fn pk_bits(&mut self, bits: usize) {
+ self.atoms.push(
+ Atom::pair("Public key size", format!("{}", bits)));
+ }
+
+ fn recipient(&mut self, who: &openpgp::KeyID) {
+ self.atoms.push(Atom::pair("Recipient", format!("{}", who)));
+ }
+
+ fn revocation_reason(&mut self, reason: ReasonForRevocation) {
+ self.atoms.push(
+ Atom::pair("Revocation reason", format!("{}", reason)));
+ }
+
+ fn revoked(&mut self) {
+ self.atoms.push(Atom::note("Revoked"));
+ }
+
+ fn separator(&mut self) {
+ self.atoms.push(Atom::separator());
+ }
+
+ fn signature_type(&mut self, typ: SignatureType) {
+ self.atoms.push(Atom::pair("Kind", format!("{}", typ)));
+ }
+
+ fn signed_openpgp_message(&mut self) {
+ self.atoms.push(
+ Atom::pair("File type",
+ "Signed OpenPGP Message".to_string()));
+ }
+
+ fn subkey(&mut self, fp: openpgp::Fingerprint) {
+ self.atoms.push(
+ Atom::pair("Subkey fingerprint", format!("{}", fp)));
+ }
+
+ fn transferable_secret_key(&mut self) {
+ self.atoms.push(
+ Atom::pair("File type",
+ "Transferable Secret Key".to_string()));
+ }
+
+ fn unencrypted_secret_key(&mut self) {
+ self.atoms.push(
+ Atom::pair("Secret key", "Unencrypted".to_string()));