116 lines
4.5 KiB
Rust
116 lines
4.5 KiB
Rust
use std::time::Duration;
|
|
use pem::Pem;
|
|
use sha2::Sha256;
|
|
use digest::Digest;
|
|
use chrono::Local;
|
|
use clap::{ArgMatches, SubCommand, App, Arg};
|
|
use yubikey::{YubiKey, Certificate};
|
|
use yubikey::piv::SlotId;
|
|
use x509_parser::parse_x509_certificate;
|
|
use rust_util::XResult;
|
|
use rust_util::util_msg::MessageType;
|
|
use rust_util::util_clap::{Command, CommandError};
|
|
|
|
pub struct CommandImpl;
|
|
|
|
impl Command for CommandImpl {
|
|
fn name(&self) -> &str { "piv" }
|
|
|
|
fn subcommand<'a>(&self) -> App<'a, 'a> {
|
|
SubCommand::with_name(self.name()).about("PIV subcommand")
|
|
.arg(Arg::with_name("detail").long("detail").help("Detail output"))
|
|
.arg(Arg::with_name("show-config").long("show-config").help("Show config output"))
|
|
// .arg(Arg::with_name("json").long("json").help("JSON output"))
|
|
}
|
|
|
|
fn run(&self, _arg_matches: &ArgMatches, sub_arg_matches: &ArgMatches) -> CommandError {
|
|
let detail_output = sub_arg_matches.is_present("detail");
|
|
let mut yk = opt_result!(YubiKey::open(), "YubiKey not found: {}");
|
|
success!("Name: {}", yk.name());
|
|
success!("Version: {}", yk.version());
|
|
success!("Serial: {}", yk.serial());
|
|
match yk.chuid() {
|
|
Ok(chuid) => success!("CHUID: {}",chuid.to_string()),
|
|
Err(e) => warning!("CHUID: <none> {}", e),
|
|
}
|
|
match yk.cccid() {
|
|
Ok(cccid) => success!("CCCID: {}",cccid.to_string()),
|
|
Err(e) => warning!("CCCID: <none> {}", e),
|
|
}
|
|
match yk.get_pin_retries() {
|
|
Ok(pin_retries) => success!("PIN retries: {}",pin_retries),
|
|
Err(e) => warning!("PIN retries: <none> {}", e),
|
|
}
|
|
if sub_arg_matches.is_present("show-config") {
|
|
let cofnig = yk.config();
|
|
information!("Config: {:#?}", cofnig);
|
|
}
|
|
|
|
for slot in yubikey::piv::SLOTS.iter().cloned() {
|
|
print_cert_info(&mut yk, slot, detail_output).ok();
|
|
}
|
|
Ok(None)
|
|
}
|
|
}
|
|
|
|
fn print_cert_info(yubikey: &mut YubiKey, slot: SlotId, detail_output: bool) -> XResult<()> {
|
|
let cert = match Certificate::read(yubikey, slot) {
|
|
Ok(c) => c,
|
|
Err(e) => {
|
|
let slot_id: u8 = slot.into();
|
|
debugging!("error reading certificate in slot {:?}, id: {:x}: {}", slot, slot_id, e);
|
|
return simple_error!("error reading certificate in slot {:?}: {}", slot, e);
|
|
}
|
|
};
|
|
let buf = cert.into_buffer();
|
|
|
|
if !buf.is_empty() {
|
|
information!("{}", "-".repeat(88));
|
|
let fingerprint_sha256 = Sha256::digest(&buf);
|
|
|
|
let slot_id: u8 = slot.into();
|
|
success!("Slot: {:?}, id: {:x}", slot, slot_id);
|
|
|
|
let cert_pem_obj = Pem {
|
|
tag: String::from("CERTIFICATE"),
|
|
contents: buf.to_vec(),
|
|
};
|
|
if detail_output {
|
|
information!("{}", pem::encode(&cert_pem_obj).trim());
|
|
} else {
|
|
rust_util::util_msg::when(MessageType::DEBUG, || {
|
|
debugging!("{}", pem::encode(&cert_pem_obj).trim());
|
|
});
|
|
}
|
|
|
|
match parse_x509_certificate(&buf) {
|
|
Ok((_rem, cert)) => {
|
|
success!("Algorithm: {}", cert.tbs_certificate.subject_pki.algorithm.algorithm);
|
|
|
|
success!("Subject: {}", cert.tbs_certificate.subject);
|
|
success!("Issuer: {}", cert.tbs_certificate.issuer);
|
|
success!("Fingerprint(SHA256): {}", hex::encode(fingerprint_sha256));
|
|
success!("Not Before: {}", cert.tbs_certificate.validity.not_before.to_rfc2822());
|
|
|
|
let mut not_after_desc = String::new();
|
|
let not_after_timestamp = cert.tbs_certificate.validity.not_after.timestamp();
|
|
let now_timestamp = Local::now().timestamp();
|
|
if not_after_timestamp < now_timestamp {
|
|
let expired_time = simpledateformat::format_human(Duration::from_secs((now_timestamp - not_after_timestamp) as u64));
|
|
not_after_desc.push_str(&format!("(EXPIRED {})", expired_time));
|
|
} else {
|
|
let valid_time = simpledateformat::format_human(Duration::from_secs((not_after_timestamp - now_timestamp) as u64));
|
|
not_after_desc.push_str(&format!("(left {})", valid_time));
|
|
}
|
|
success!("Not After: {} {}", cert.tbs_certificate.validity.not_after.to_rfc2822(), not_after_desc);
|
|
}
|
|
_ => {
|
|
warning!("Failed to parse certificate");
|
|
return Ok(());
|
|
}
|
|
};
|
|
}
|
|
|
|
Ok(())
|
|
}
|