From 15d457864c19ed4973891d92a37da64626dc743b Mon Sep 17 00:00:00 2001 From: Heiko Schaefer Date: Sun, 29 May 2022 18:09:53 +0200 Subject: [PATCH] Reorganize 'status' output format --- tools/README.md | 74 ++++++++------- tools/src/bin/opgpcard/cli.rs | 4 + tools/src/bin/opgpcard/main.rs | 161 +++++++++++++++++++++------------ 3 files changed, 148 insertions(+), 91 deletions(-) diff --git a/tools/README.md b/tools/README.md index 09db6bf..0d0717b 100644 --- a/tools/README.md +++ b/tools/README.md @@ -56,36 +56,35 @@ Available OpenPGP cards: 0007:87654321 ``` -### Inspect cards +### Inspect card status Print status information about the data on a card. The card is implicitly selected (if exactly one card is connected): ``` $ opgpcard status -OpenPGP card ABCD:01234567 (card version 2.0) +OpenPGP card ABCD:01234567 (card version 3.4) Cardholder: Alice Adams +Language preferences: 'en' Signature key - fingerprint: 1FE2 E8F1 9FE8 7D0D 8AAF 5579 8CB7 58BA 502F 2458 - created: 2022-03-25 20:15:49 - algorithm: Ed25519 (EdDSA) + Fingerprint: 034B 348C EDA2 064C AA22 74E4 7563 E86F 5CAB C2A4 + Algorithm: Ed25519 (EdDSA) + Created: 2022-05-21 13:15:19 UTC + Signatures made: 11 Decryption key - fingerprint: 68CB 4EDD 4D49 90B8 2CEC 2D22 EF7E 5B6A 2012 694C - created: 2022-03-25 20:15:49 - algorithm: Cv25519 (ECDH) + Fingerprint: 338B EE09 3950 D831 A76F 0EB9 13D6 2DF6 8C9E 5176 + Algorithm: Cv25519 (ECDH) + Created: 2022-05-21 13:15:19 UTC Authentication key - fingerprint: 59A5 CD3E A88F 8707 D887 EAAE 1354 5F40 4E11 BE1C - created: 2022-03-25 20:15:49 - algorithm: Ed25519 (EdDSA) - -Retry counters: User PIN: 3, Admin PIN: 3, Resetting Code: 3 -Signature counter: 3 -Signature PIN only valid once: true + Fingerprint: 4881 A22E 7EC6 26D1 1202 50B0 A7D7 F0D5 0C8D F719 + Algorithm: Ed25519 (EdDSA) + Created: 2022-05-21 13:15:19 UTC +Remaining PIN attempts: User: 3, Admin: 3, Reset Code: 0 ``` Explicitly print the status information for a specific card (this command syntax is needed, when more than one card @@ -95,37 +94,46 @@ is plugged in): $ opgpcard status --card ABCD:01234567 ``` -Add `-v` for more verbose card status (this additionally outputs the raw public key data for each key slot): +Add `-v` for more verbose card status: ``` -$ opgpcard status -c ABCD:01234567 -v -OpenPGP card ABCD:01234567 (card version 2.0) +OpenPGP card ABCD:01234567 (card version 3.4) Cardholder: Alice Adams +Language preferences: 'en' Signature key - fingerprint: 1FE2 E8F1 9FE8 7D0D 8AAF 5579 8CB7 58BA 502F 2458 - created: 2022-03-25 20:15:49 - algorithm: Ed25519 (EdDSA) - public key material: ECC, data: 4C6364692AA4212AA95CF25FF31FD5F94CCAC173BFD77C918E443F09FAAFE3F5 + Fingerprint: 034B 348C EDA2 064C AA22 74E4 7563 E86F 5CAB C2A4 + Algorithm: Ed25519 (EdDSA) + Created: 2022-05-21 13:15:19 UTC + Touch policy: Cached [Features: Button] + Key Status: generated + User PIN presentation valid for unlimited signatures + Signatures made: 11 Decryption key - fingerprint: 68CB 4EDD 4D49 90B8 2CEC 2D22 EF7E 5B6A 2012 694C - created: 2022-03-25 20:15:49 - algorithm: Cv25519 (ECDH) - public key material: ECC, data: B99202743227D87D5F24639937DF75C936AC7933CE3328F5BF6AFA174A4A8745 + Fingerprint: 338B EE09 3950 D831 A76F 0EB9 13D6 2DF6 8C9E 5176 + Algorithm: Cv25519 (ECDH) + Created: 2022-05-21 13:15:19 UTC + Touch policy: Off [Features: Button] + Key Status: generated Authentication key - fingerprint: 59A5 CD3E A88F 8707 D887 EAAE 1354 5F40 4E11 BE1C - created: 2022-03-25 20:15:49 - algorithm: Ed25519 (EdDSA) - public key material: ECC, data: BFE1E5EB31032E0F4320E163082BEDBAD2A6318EC368375F7A65D22AC7AB7444 + Fingerprint: 4881 A22E 7EC6 26D1 1202 50B0 A7D7 F0D5 0C8D F719 + Algorithm: Ed25519 (EdDSA) + Created: 2022-05-21 13:15:19 UTC + Touch policy: Off [Features: Button] + Key Status: generated -Retry counters: User PIN: 3, Admin PIN: 3, Resetting Code: 3 -Signature counter: 3 -Signature PIN only valid once: true +Remaining PIN attempts: User: 3, Admin: 3, Reset Code: 0 + +Touch policy attestation: Cached [Features: Button] + +Key Status (#129): imported ``` +The `-p` flag additionally outputs the raw public key data for each key slot. + ### Get an OpenPGP public key representation from a card This command returns an OpenPGP public key representation of the keys on a card. diff --git a/tools/src/bin/opgpcard/cli.rs b/tools/src/bin/opgpcard/cli.rs index 33b117d..31efbfc 100644 --- a/tools/src/bin/opgpcard/cli.rs +++ b/tools/src/bin/opgpcard/cli.rs @@ -29,6 +29,10 @@ pub enum Command { #[clap(name = "verbose", short = 'v', long = "verbose")] verbose: bool, + + /// Print public key material for each key slot + #[clap(name = "pkm", short = 'p', long = "public-key-material")] + pkm: bool, }, /// Show technical details about a card diff --git a/tools/src/bin/opgpcard/main.rs b/tools/src/bin/opgpcard/main.rs index ad95bba..5243bed 100644 --- a/tools/src/bin/opgpcard/main.rs +++ b/tools/src/bin/opgpcard/main.rs @@ -40,8 +40,12 @@ fn main() -> Result<(), Box> { println!("Available OpenPGP cards:"); list_cards()?; } - cli::Command::Status { ident, verbose } => { - print_status(ident, verbose)?; + cli::Command::Status { + ident, + verbose, + pkm, + } => { + print_status(ident, verbose, pkm)?; } cli::Command::Info { ident } => { print_info(ident)?; @@ -479,7 +483,7 @@ fn pick_card_for_reading(ident: Option) -> Result, verbose: bool) -> Result<()> { +fn print_status(ident: Option, verbose: bool, pkm: bool) -> Result<()> { let mut card = pick_card_for_reading(ident)?; let mut pgp = OpenPgp::new(&mut *card); @@ -498,6 +502,9 @@ fn print_status(ident: Option, verbose: bool) -> Result<()> { // card / cardholder metadata let crd = open.cardholder_related_data()?; + // Remember if any cardholder information is printed (if so, we print a newline later) + let mut card_holder_output = false; + if let Some(name) = crd.name() { // FIXME: decoding as utf8 is wrong (the spec defines this field as latin1 encoded) let name = String::from_utf8_lossy(name).to_string(); @@ -518,11 +525,14 @@ fn print_status(ident: Option, verbose: bool) -> Result<()> { let name = name.iter().cloned().rev().collect::>().join(" "); println!("{}", name); + + card_holder_output = true; } let url = open.url()?; if !url.is_empty() { println!("URL: {}", url); + card_holder_output = true; } if let Some(lang) = crd.lang() { @@ -532,53 +542,110 @@ fn print_status(ident: Option, verbose: bool) -> Result<()> { .collect::>() .join(", "); println!("Language preferences: '{}'", l); + card_holder_output = true; } + if card_holder_output { + println!(); + } + + // key information (imported vs. generated on card) + let ki = ard.key_information().ok().flatten(); + + let pws = open.pw_status_bytes()?; + // information about subkeys let fps = open.fingerprints()?; let kgt = open.key_generation_times()?; - println!(); println!("Signature key"); if let Some(fp) = fps.signature() { - println!(" fingerprint: {}", fp.to_spaced_hex()); + println!(" Fingerprint: {}", fp.to_spaced_hex()); } + println! {" Algorithm: {}", open.algorithm_attributes(KeyType::Signing)?}; if let Some(kgt) = kgt.signature() { - println! {" created: {}", kgt.to_datetime()}; + println! {" Created: {}", kgt.to_datetime()}; } - println! {" algorithm: {}", open.algorithm_attributes(KeyType::Signing)?}; if verbose { + if let Some(uif) = ard.uif_pso_cds()? { + println!( + " Touch policy: {} [Features: {}]", + uif.touch_policy(), + uif.features() + ); + } + if let Some(ks) = ki.as_ref().map(|ki| ki.sig_status()) { + println!(" Key Status: {}", ks); + } + } + + if verbose { + if pws.pw1_cds_valid_once() { + println!(" User PIN presentation valid for one signature"); + } else { + println!(" User PIN presentation valid for unlimited signatures"); + } + } + + let sst = open.security_support_template()?; + println!(" Signatures made: {}", sst.signature_count()); + + if pkm { if let Ok(pkm) = open.public_key(KeyType::Signing) { - println! {" public key material: {}", pkm}; + println! {" Public key material: {}", pkm}; } } println!(); println!("Decryption key"); if let Some(fp) = fps.decryption() { - println!(" fingerprint: {}", fp.to_spaced_hex()); + println!(" Fingerprint: {}", fp.to_spaced_hex()); } + println! {" Algorithm: {}", open.algorithm_attributes(KeyType::Decryption)?}; if let Some(kgt) = kgt.decryption() { - println! {" created: {}", kgt.to_datetime()}; + println! {" Created: {}", kgt.to_datetime()}; } - println! {" algorithm: {}", open.algorithm_attributes(KeyType::Decryption)?}; if verbose { + if let Some(uif) = ard.uif_pso_dec()? { + println!( + " Touch policy: {} [Features: {}]", + uif.touch_policy(), + uif.features() + ); + } + if let Some(ks) = ki.as_ref().map(|ki| ki.dec_status()) { + println!(" Key Status: {}", ks); + } + } + if pkm { if let Ok(pkm) = open.public_key(KeyType::Decryption) { - println! {" public key material: {}", pkm}; + println! {" Public key material: {}", pkm}; } } println!(); println!("Authentication key"); if let Some(fp) = fps.authentication() { - println!(" fingerprint: {}", fp.to_spaced_hex()); + println!(" Fingerprint: {}", fp.to_spaced_hex()); } + println! {" Algorithm: {}", open.algorithm_attributes(KeyType::Authentication)?}; if let Some(kgt) = kgt.authentication() { - println! {" created: {}", kgt.to_datetime()}; + println! {" Created: {}", kgt.to_datetime()}; } - println! {" algorithm: {}", open.algorithm_attributes(KeyType::Authentication)?}; if verbose { + if let Some(uif) = ard.uif_pso_aut()? { + println!( + " Touch policy: {} [Features: {}]", + uif.touch_policy(), + uif.features() + ); + } + if let Some(ks) = ki.as_ref().map(|ki| ki.aut_status()) { + println!(" Key Status: {}", ks); + } + } + if pkm { if let Ok(pkm) = open.public_key(KeyType::Authentication) { println! {" public key material: {}", pkm}; } @@ -588,67 +655,45 @@ fn print_status(ident: Option, verbose: bool) -> Result<()> { println!(); - let sst = open.security_support_template()?; - println!("Signatures made: {}", sst.signature_count()); - - println!(); - - let pws = open.pw_status_bytes()?; - println!( - "Remaining tries: User PIN: {}, Admin PIN: {}, Reset Code: {}", + "Remaining PIN attempts: User: {}, Admin: {}, Reset Code: {}", pws.err_count_pw1(), pws.err_count_pw3(), pws.err_count_rc(), ); - println!( - "Signature PIN only valid once: {}", - pws.pw1_cds_valid_once() - ); if verbose { println!(); - if let Ok(Some(ki)) = ard.key_information() { - println!("Key Information:\n{}", ki); - } - - if let Some(uif) = ard.uif_pso_cds()? { - println!( - "Touch policy signing: {} [Features: {}]", - uif.touch_policy(), - uif.features() - ); - } - - if let Some(uif) = ard.uif_pso_dec()? { - println!( - "Touch policy decryption: {} [Features: {}]", - uif.touch_policy(), - uif.features() - ); - } - - if let Some(uif) = ard.uif_pso_dec()? { - println!( - "Touch policy authentication: {} [Features: {}]", - uif.touch_policy(), - uif.features() - ); - } - if let Some(uif) = ard.uif_attestation()? { println!( "Touch policy attestation: {} [Features: {}]", uif.touch_policy(), uif.features() ); + println!(); + } + + if let Some(ki) = ki { + let num = ki.num_additional(); + for i in 0..num { + println!( + "Key Status (#{}): {}", + ki.additional_ref(i), + ki.additional_status(i) + ); + } + + if num > 0 { + println!(); + } } if let Ok(fps) = ard.ca_fingerprints() { - println!(); - for x in fps.iter().enumerate() { - println!("CA fingerprint {}: {:x?}", x.0 + 1, x.1); + for (num, fp) in fps.iter().enumerate() { + if let Some(fp) = fp { + println!("CA fingerprint {}: {:x?}", num + 1, fp); + } } } }