use std::{
io::{BufReader, stdin},
time::SystemTime,
};
use chrono::{DateTime, Utc};
use minipgp6_armor::{DataType, read::AutoDearmorer};
use minipgp6_packet::{body::public_key::PublicKey, transferable::TransferableKey};
use minipgp6_validity::{Checked, 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 key_flags.encryption_capable() {
flags.push("Encrypt")
}
if key_flags.data_signing_capable() {
flags.push("Sign")
}
flags.join(", ")
}
fn print_status(checked: Checked<'_, PublicKey>) {
let (pri, status, kf) = checked.primary_status();
println!(
"🔐 {:?} {}",
pri.key.public_key_algorithm(),
pri.key.fingerprint()
);
println!(
" ⏱️ Created {}",
DateTime::<Utc>::from(SystemTime::from(pri.key.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.key.public_key_algorithm(),
subkey.key.fingerprint()
);
println!(
" ⏱️ Created {}",
DateTime::<Utc>::from(SystemTime::from(subkey.key.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.user_id.id);
println!(" {}", fmt_status(&status));
println!();
}
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let binary = AutoDearmorer::new(BufReader::new(stdin()), Some(DataType::Public))?;
let certs = TransferableKey::<PublicKey>::load(&mut BufReader::new(binary))?;
if certs.len() > 1 {
eprintln!("WARN: found more than one certificate")
}
if let Some(cert) = certs.into_iter().next() {
let checked = Checked::from(&cert);
print_status(checked);
} else {
eprintln!("WARN: No certificates found");
}
Ok(())
}