use std::{
io::{BufReader, stdin},
time::SystemTime,
};
use chrono::{DateTime, Utc};
use minipgp6_armor::{DataType, read::AutoDearmorer};
use minipgp6_packet::{
body::{public_key::PublicKey, secret_key::SecretKey},
transferable::{KeyPacket, TransferableKey},
types::time::Timestamp,
};
use minipgp6_validity::{Checked, Encrypt, KeyFlags, Status};
fn fmt_status(s: &Status) -> String {
match s {
Status::Valid { expires: None } => "✅ Active (no expiration)".to_string(),
Status::Valid { expires: Some(exp) } => {
format!(
"✅ Active, expires {}",
DateTime::<Utc>::from(SystemTime::from(*exp))
)
}
Status::Expired { time } => {
format!(
"🚫 Expired {}",
DateTime::<Utc>::from(SystemTime::from(*time))
)
}
Status::RevokedSoft { time, reason: msg } => {
format!(
"🚫 Revoked (soft) {}: {msg}",
DateTime::<Utc>::from(SystemTime::from(*time))
)
}
Status::RevokedHard { time, reason: msg } => {
format!(
"🚫 Revoked (hard) {}: {msg}",
DateTime::<Utc>::from(SystemTime::from(*time))
)
}
Status::Invalid { context } => format!("🚫 Invalid: {context}"),
}
}
fn fmt_key_flags(key_flags: &KeyFlags<'_>) -> String {
let mut flags = Vec::new();
if key_flags.certification_capable() {
flags.push("Certify")
}
if let Some(enc) = key_flags.encryption_capable() {
match enc {
Encrypt::Both => flags.push("Encrypt"),
Encrypt::Comms => flags.push("Encrypt (comms)"),
Encrypt::Storage => flags.push("Encrypt (storage)"),
}
}
if key_flags.data_signing_capable() {
flags.push("Sign")
}
flags.join(", ")
}
fn print_status<K: KeyPacket>(checked: Checked<'_, K>) {
let (pri, status, kf) = checked.primary_status_at(Timestamp::now());
println!(
"🔐 {:?} {}",
pri.component.public().public_key_algorithm(),
pri.component.public().fingerprint()
);
println!(
" ⏱️ Created {}",
DateTime::<Utc>::from(SystemTime::from(pri.component.public().creation_time))
);
println!(" {}", fmt_status(&status));
if let Some(key_flags) = kf {
println!(" 🏴 Key flags: {}", fmt_key_flags(&key_flags));
}
println!();
for (subkey, status, kf) in checked.subkeys_status() {
println!(
" 🔑 {:?} {}",
subkey.component.public().public_key_algorithm(),
subkey.component.public().fingerprint()
);
println!(
" ⏱️ Created {}",
DateTime::<Utc>::from(SystemTime::from(subkey.component.public().creation_time))
);
println!(" {}", fmt_status(&status));
if let Some(key_flags) = kf {
println!(" 🏴 Key flags: {}", fmt_key_flags(&key_flags));
}
println!();
}
for (user_id, status) in checked.user_ids_status() {
println!(" 🪪 ID {:?}", user_id.component.id());
println!(" {}", fmt_status(&status));
println!();
}
}
fn print<K: KeyPacket>(transferable: Vec<TransferableKey<K>>) {
if transferable.is_empty() {
eprintln!("WARN: No certificates found");
}
for cert in transferable {
let checked = Checked::from(&cert);
print_status(checked);
}
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let binary = AutoDearmorer::new(BufReader::new(stdin()), None)?;
match binary.data_type() {
Some(DataType::Private) => {
let tsks = TransferableKey::<SecretKey>::read(&mut BufReader::new(binary))?;
println!("*** Transferable Secret Key(s) ***");
println!();
print(tsks);
}
_ => {
let certs = TransferableKey::<PublicKey>::read(&mut BufReader::new(binary))?;
print(certs);
}
}
Ok(())
}