diff --git a/Cargo.lock b/Cargo.lock index 65b7c68..a0371ba 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -314,7 +314,7 @@ dependencies = [ [[package]] name = "card-cli" -version = "1.6.0" +version = "1.7.0" dependencies = [ "authenticator", "base64 0.21.2", diff --git a/Cargo.toml b/Cargo.toml index 96841c5..e0c9078 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "card-cli" -version = "1.6.0" +version = "1.7.0" authors = ["Hatter Jiang "] edition = "2018" diff --git a/src/cmd_pivsummary.rs b/src/cmd_pivsummary.rs new file mode 100644 index 0000000..6d6c36e --- /dev/null +++ b/src/cmd_pivsummary.rs @@ -0,0 +1,78 @@ +use clap::{App, ArgMatches, SubCommand}; +use rust_util::util_clap::{Command, CommandError}; +use rust_util::XResult; +use spki::der::Encode; +use x509_parser::parse_x509_certificate; +use yubikey::{Certificate, YubiKey}; +use yubikey::piv::SlotId; + +use crate::pivutil::get_algorithm_id; + +pub struct CommandImpl; + +impl Command for CommandImpl { + fn name(&self) -> &str { "piv-summary" } + + fn subcommand<'a>(&self) -> App<'a, 'a> { + SubCommand::with_name(self.name()).about("PIV subcommand") + } + + fn run(&self, _arg_matches: &ArgMatches, _sub_arg_matches: &ArgMatches) -> CommandError { + let mut yk = opt_result!(YubiKey::open(), "YubiKey not found: {}"); + success!("Name: {}", yk.name()); + information!("Version: {}", yk.version()); + information!("Serial: {}", yk.serial()); + match yk.chuid() { + Ok(chuid) => information!("CHUID: {}",chuid.to_string()), + Err(e) => warning!("CHUID: {}", e), + } + match yk.cccid() { + Ok(cccid) => information!("CCCID: {}",cccid.to_string()), + Err(e) => warning!("CCCID: {}", e), + } + match yk.get_pin_retries() { + Ok(pin_retries) => information!("PIN retries: {}",pin_retries), + Err(e) => warning!("PIN retries: {}", e), + } + + match yk.piv_keys() { + Ok(keys) => { + information!("Found {} PIV keys", keys.len()); + } + Err(e) => failure!("Get PIV keys failed: {}", e) + } + + for slot in yubikey::piv::SLOTS { + print_summary_info(&mut yk, slot).ok(); + } + Ok(None) + } +} + +fn print_summary_info(yubikey: &mut YubiKey, slot: SlotId) -> XResult<()> { + let slot_id: u8 = slot.into(); + let cert = match Certificate::read(yubikey, slot) { + Ok(c) => c, + Err(e) => { + warning!("Slot: {:?}, id: {:x}, certificate not found", slot, slot_id); + return simple_error!("error reading certificate in slot {:?}: {}", slot, e); + } + }; + let buf_vec = cert.cert.to_der()?; + let buf: &[u8] = buf_vec.as_ref(); + if buf.is_empty() { + warning!("Slot: {:?}, id: {:x}, certificate buffer empty", slot, slot_id); + } else { + let slot_id: u8 = slot.into(); + let algorithm_id = get_algorithm_id(&cert.cert.tbs_certificate.subject_public_key_info) + .map(|aid| format!("{:?}", aid)) + .unwrap_or_else(|e| format!("Error: {}", e)); + let cert_subject = match parse_x509_certificate(buf) { + Ok((_rem, cert)) => cert.subject.to_string(), + _ => cert.cert.tbs_certificate.subject.to_string(), + }; + success!("Slot: {:?}, id: {:x}, algorithm: {}, subject: {}", slot, slot_id, algorithm_id, cert_subject); + } + + Ok(()) +} diff --git a/src/main.rs b/src/main.rs index 1235432..5bec406 100644 --- a/src/main.rs +++ b/src/main.rs @@ -25,6 +25,7 @@ mod cmd_pgpcardsign; mod cmd_pgpcarddecrypt; mod cmd_pgpcardmake; mod cmd_piv; +mod cmd_pivsummary; mod cmd_pivmeta; mod cmd_pivrsasign; mod cmd_pivecdh; @@ -70,6 +71,7 @@ fn inner_main() -> CommandError { Box::new(cmd_pgpcarddecrypt::CommandImpl), Box::new(cmd_pgpcardmake::CommandImpl), Box::new(cmd_piv::CommandImpl), + Box::new(cmd_pivsummary::CommandImpl), Box::new(cmd_pivmeta::CommandImpl), Box::new(cmd_pivrsasign::CommandImpl), Box::new(cmd_pivecdh::CommandImpl),