use anyhow::Result;
use chrono::{DateTime, Utc};
use clap::Parser;
use openpgp_card::ocard::KeyType;
use crate::output;
use crate::pick_card_for_reading;
use crate::versioned_output::{OutputBuilder, OutputFormat, OutputVersion};
#[derive(Parser, Debug)]
pub struct StatusCommand {
#[arg(
name = "card ident",
short = 'c',
long = "card",
help = "Identifier of the card to use"
)]
pub ident: Option<String>,
#[arg(
name = "verbose",
short = 'v',
long = "verbose",
help = "Use verbose output"
)]
pub verbose: bool,
#[arg(name = "pkm", short = 'K', long = "public-key-material")]
pub pkm: bool,
}
pub fn print_status(
format: OutputFormat,
output_version: OutputVersion,
command: StatusCommand,
) -> Result<()> {
let mut output = output::Status::default();
output.verbose(command.verbose);
output.pkm(command.pkm);
let mut open = pick_card_for_reading(command.ident)?;
let mut card = open.transaction()?;
output.ident(card.application_identifier()?.ident());
let name = card.cardholder_name()?;
if !name.is_empty() {
output.cardholder_name(name);
}
let url = card.url()?;
if !url.is_empty() {
output.certificate_url(url);
}
let login_data = card.login_data()?;
if !login_data.is_empty() {
output.login_data(String::from_utf8_lossy(&login_data).into());
}
if let Some(lang) = card.cardholder_related_data()?.lang() {
for lang in lang {
output.language_preference(format!("{lang}"));
}
}
let ki = card.key_information().ok().flatten();
let pws = card.pw_status_bytes()?;
let fps = card.fingerprints()?;
let kgt = card.key_generation_times()?;
let mut signature_key = output::KeySlotInfo::default();
if let Some(fp) = fps.signature() {
signature_key.fingerprint(fp.to_hex());
}
signature_key.algorithm(format!("{}", card.algorithm_attributes(KeyType::Signing)?));
if let Some(kgt) = kgt.signature() {
signature_key.creation_time(format!("{}", DateTime::<Utc>::from(kgt)));
}
if let Some(uif) = card.user_interaction_flag(KeyType::Signing)? {
signature_key.touch_policy(format!("{}", uif.touch_policy()));
signature_key.touch_features(format!("{}", uif.features()));
}
if let Some(ks) = ki.as_ref().map(|ki| ki.sig_status()) {
signature_key.status(format!("{ks}"));
}
if let Ok(pkm) = card.public_key_material(KeyType::Signing) {
signature_key.public_key_material(pkm.to_string());
}
output.signature_key(signature_key);
let dsc = card.digital_signature_count()?;
output.signature_count(dsc);
let mut decryption_key = output::KeySlotInfo::default();
if let Some(fp) = fps.decryption() {
decryption_key.fingerprint(fp.to_hex());
}
decryption_key.algorithm(format!(
"{}",
card.algorithm_attributes(KeyType::Decryption)?
));
if let Some(kgt) = kgt.decryption() {
decryption_key.creation_time(format!("{}", DateTime::<Utc>::from(kgt)));
}
if let Some(uif) = card.user_interaction_flag(KeyType::Decryption)? {
decryption_key.touch_policy(format!("{}", uif.touch_policy()));
decryption_key.touch_features(format!("{}", uif.features()));
}
if let Some(ks) = ki.as_ref().map(|ki| ki.dec_status()) {
decryption_key.status(format!("{ks}"));
}
if let Ok(pkm) = card.public_key_material(KeyType::Decryption) {
decryption_key.public_key_material(pkm.to_string());
}
output.decryption_key(decryption_key);
let mut authentication_key = output::KeySlotInfo::default();
if let Some(fp) = fps.authentication() {
authentication_key.fingerprint(fp.to_hex());
}
authentication_key.algorithm(format!(
"{}",
card.algorithm_attributes(KeyType::Authentication)?
));
if let Some(kgt) = kgt.authentication() {
authentication_key.creation_time(format!("{}", DateTime::<Utc>::from(kgt)));
}
if let Some(uif) = card.user_interaction_flag(KeyType::Authentication)? {
authentication_key.touch_policy(format!("{}", uif.touch_policy()));
authentication_key.touch_features(format!("{}", uif.features()));
}
if let Some(ks) = ki.as_ref().map(|ki| ki.aut_status()) {
authentication_key.status(format!("{ks}"));
}
if let Ok(pkm) = card.public_key_material(KeyType::Authentication) {
authentication_key.public_key_material(pkm.to_string());
}
output.authentication_key(authentication_key);
let mut attestation_key = output::KeySlotInfo::default();
if let Ok(Some(fp)) = card.fingerprint(KeyType::Attestation) {
attestation_key.fingerprint(fp.to_hex());
}
if let Ok(algo) = card.algorithm_attributes(KeyType::Attestation) {
attestation_key.algorithm(format!("{algo}"));
}
if let Ok(Some(kgt)) = card.key_generation_time(KeyType::Attestation) {
attestation_key.creation_time(format!("{}", DateTime::<Utc>::from(&kgt)));
}
if let Some(uif) = card.user_interaction_flag(KeyType::Attestation)? {
attestation_key.touch_policy(format!("{}", uif.touch_policy()));
attestation_key.touch_features(format!("{}", uif.features()));
}
if let Some(ki) = ki.as_ref() {
if let Some(n) = (0..ki.num_additional()).find(|&n| ki.additional_ref(n) == 0x81) {
let ks = ki.additional_status(n);
attestation_key.status(format!("{ks}"));
}
};
output.attestation_key(attestation_key);
output.user_pin_valid_for_only_one_signature(pws.pw1_cds_valid_once());
output.user_pin_remaining_attempts(pws.err_count_pw1());
output.admin_pin_remaining_attempts(pws.err_count_pw3());
output.reset_code_remaining_attempts(pws.err_count_rc());
if let Ok(kdf) = card.kdf_do() {
if kdf.kdf_algo() != 0 {
output.kdf_mode(true);
}
}
if let Some(ki) = ki {
let num = ki.num_additional();
for i in 0..num {
if ki.additional_ref(i) != 0x81 {
output.additional_key_status(
ki.additional_ref(i),
ki.additional_status(i).to_string(),
);
}
}
}
if let Ok(fps) = card.ca_fingerprints() {
for fp in fps.iter().flatten() {
output.ca_fingerprint(fp.to_string());
}
}
println!("{}", output.print(format, output_version)?);
Ok(())
}