Reorganize 'status' output format

This commit is contained in:
Heiko Schaefer 2022-05-29 18:09:53 +02:00
parent 2c666c6857
commit 15d457864c
No known key found for this signature in database
GPG key ID: 4A849A1904CCBD7D
3 changed files with 148 additions and 91 deletions

View file

@ -56,36 +56,35 @@ Available OpenPGP cards:
0007:87654321 0007:87654321
``` ```
### Inspect cards ### Inspect card status
Print status information about the data on a card. Print status information about the data on a card.
The card is implicitly selected (if exactly one card is connected): The card is implicitly selected (if exactly one card is connected):
``` ```
$ opgpcard status $ opgpcard status
OpenPGP card ABCD:01234567 (card version 2.0) OpenPGP card ABCD:01234567 (card version 3.4)
Cardholder: Alice Adams Cardholder: Alice Adams
Language preferences: 'en'
Signature key Signature key
fingerprint: 1FE2 E8F1 9FE8 7D0D 8AAF 5579 8CB7 58BA 502F 2458 Fingerprint: 034B 348C EDA2 064C AA22 74E4 7563 E86F 5CAB C2A4
created: 2022-03-25 20:15:49 Algorithm: Ed25519 (EdDSA)
algorithm: Ed25519 (EdDSA) Created: 2022-05-21 13:15:19 UTC
Signatures made: 11
Decryption key Decryption key
fingerprint: 68CB 4EDD 4D49 90B8 2CEC 2D22 EF7E 5B6A 2012 694C Fingerprint: 338B EE09 3950 D831 A76F 0EB9 13D6 2DF6 8C9E 5176
created: 2022-03-25 20:15:49 Algorithm: Cv25519 (ECDH)
algorithm: Cv25519 (ECDH) Created: 2022-05-21 13:15:19 UTC
Authentication key Authentication key
fingerprint: 59A5 CD3E A88F 8707 D887 EAAE 1354 5F40 4E11 BE1C Fingerprint: 4881 A22E 7EC6 26D1 1202 50B0 A7D7 F0D5 0C8D F719
created: 2022-03-25 20:15:49 Algorithm: Ed25519 (EdDSA)
algorithm: Ed25519 (EdDSA) Created: 2022-05-21 13:15:19 UTC
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
``` ```
Explicitly print the status information for a specific card (this command syntax is needed, when more than one card 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 $ 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 3.4)
OpenPGP card ABCD:01234567 (card version 2.0)
Cardholder: Alice Adams Cardholder: Alice Adams
Language preferences: 'en'
Signature key Signature key
fingerprint: 1FE2 E8F1 9FE8 7D0D 8AAF 5579 8CB7 58BA 502F 2458 Fingerprint: 034B 348C EDA2 064C AA22 74E4 7563 E86F 5CAB C2A4
created: 2022-03-25 20:15:49 Algorithm: Ed25519 (EdDSA)
algorithm: Ed25519 (EdDSA) Created: 2022-05-21 13:15:19 UTC
public key material: ECC, data: 4C6364692AA4212AA95CF25FF31FD5F94CCAC173BFD77C918E443F09FAAFE3F5 Touch policy: Cached [Features: Button]
Key Status: generated
User PIN presentation valid for unlimited signatures
Signatures made: 11
Decryption key Decryption key
fingerprint: 68CB 4EDD 4D49 90B8 2CEC 2D22 EF7E 5B6A 2012 694C Fingerprint: 338B EE09 3950 D831 A76F 0EB9 13D6 2DF6 8C9E 5176
created: 2022-03-25 20:15:49 Algorithm: Cv25519 (ECDH)
algorithm: Cv25519 (ECDH) Created: 2022-05-21 13:15:19 UTC
public key material: ECC, data: B99202743227D87D5F24639937DF75C936AC7933CE3328F5BF6AFA174A4A8745 Touch policy: Off [Features: Button]
Key Status: generated
Authentication key Authentication key
fingerprint: 59A5 CD3E A88F 8707 D887 EAAE 1354 5F40 4E11 BE1C Fingerprint: 4881 A22E 7EC6 26D1 1202 50B0 A7D7 F0D5 0C8D F719
created: 2022-03-25 20:15:49 Algorithm: Ed25519 (EdDSA)
algorithm: Ed25519 (EdDSA) Created: 2022-05-21 13:15:19 UTC
public key material: ECC, data: BFE1E5EB31032E0F4320E163082BEDBAD2A6318EC368375F7A65D22AC7AB7444 Touch policy: Off [Features: Button]
Key Status: generated
Retry counters: User PIN: 3, Admin PIN: 3, Resetting Code: 3 Remaining PIN attempts: User: 3, Admin: 3, Reset Code: 0
Signature counter: 3
Signature PIN only valid once: true 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 ### Get an OpenPGP public key representation from a card
This command returns an OpenPGP public key representation of the keys on a card. This command returns an OpenPGP public key representation of the keys on a card.

View file

@ -29,6 +29,10 @@ pub enum Command {
#[clap(name = "verbose", short = 'v', long = "verbose")] #[clap(name = "verbose", short = 'v', long = "verbose")]
verbose: bool, 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 /// Show technical details about a card

View file

@ -40,8 +40,12 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("Available OpenPGP cards:"); println!("Available OpenPGP cards:");
list_cards()?; list_cards()?;
} }
cli::Command::Status { ident, verbose } => { cli::Command::Status {
print_status(ident, verbose)?; ident,
verbose,
pkm,
} => {
print_status(ident, verbose, pkm)?;
} }
cli::Command::Info { ident } => { cli::Command::Info { ident } => {
print_info(ident)?; print_info(ident)?;
@ -479,7 +483,7 @@ fn pick_card_for_reading(ident: Option<String>) -> Result<Box<dyn CardBackend +
} }
} }
fn print_status(ident: Option<String>, verbose: bool) -> Result<()> { fn print_status(ident: Option<String>, verbose: bool, pkm: bool) -> Result<()> {
let mut card = pick_card_for_reading(ident)?; let mut card = pick_card_for_reading(ident)?;
let mut pgp = OpenPgp::new(&mut *card); let mut pgp = OpenPgp::new(&mut *card);
@ -498,6 +502,9 @@ fn print_status(ident: Option<String>, verbose: bool) -> Result<()> {
// card / cardholder metadata // card / cardholder metadata
let crd = open.cardholder_related_data()?; 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() { if let Some(name) = crd.name() {
// FIXME: decoding as utf8 is wrong (the spec defines this field as latin1 encoded) // FIXME: decoding as utf8 is wrong (the spec defines this field as latin1 encoded)
let name = String::from_utf8_lossy(name).to_string(); let name = String::from_utf8_lossy(name).to_string();
@ -518,11 +525,14 @@ fn print_status(ident: Option<String>, verbose: bool) -> Result<()> {
let name = name.iter().cloned().rev().collect::<Vec<_>>().join(" "); let name = name.iter().cloned().rev().collect::<Vec<_>>().join(" ");
println!("{}", name); println!("{}", name);
card_holder_output = true;
} }
let url = open.url()?; let url = open.url()?;
if !url.is_empty() { if !url.is_empty() {
println!("URL: {}", url); println!("URL: {}", url);
card_holder_output = true;
} }
if let Some(lang) = crd.lang() { if let Some(lang) = crd.lang() {
@ -532,53 +542,110 @@ fn print_status(ident: Option<String>, verbose: bool) -> Result<()> {
.collect::<Vec<_>>() .collect::<Vec<_>>()
.join(", "); .join(", ");
println!("Language preferences: '{}'", l); 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 // information about subkeys
let fps = open.fingerprints()?; let fps = open.fingerprints()?;
let kgt = open.key_generation_times()?; let kgt = open.key_generation_times()?;
println!();
println!("Signature key"); println!("Signature key");
if let Some(fp) = fps.signature() { 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() { 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 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) { if let Ok(pkm) = open.public_key(KeyType::Signing) {
println! {" public key material: {}", pkm}; println! {" Public key material: {}", pkm};
} }
} }
println!(); println!();
println!("Decryption key"); println!("Decryption key");
if let Some(fp) = fps.decryption() { 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() { 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 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) { if let Ok(pkm) = open.public_key(KeyType::Decryption) {
println! {" public key material: {}", pkm}; println! {" Public key material: {}", pkm};
} }
} }
println!(); println!();
println!("Authentication key"); println!("Authentication key");
if let Some(fp) = fps.authentication() { 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() { 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 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) { if let Ok(pkm) = open.public_key(KeyType::Authentication) {
println! {" public key material: {}", pkm}; println! {" public key material: {}", pkm};
} }
@ -588,67 +655,45 @@ fn print_status(ident: Option<String>, verbose: bool) -> Result<()> {
println!(); println!();
let sst = open.security_support_template()?;
println!("Signatures made: {}", sst.signature_count());
println!();
let pws = open.pw_status_bytes()?;
println!( println!(
"Remaining tries: User PIN: {}, Admin PIN: {}, Reset Code: {}", "Remaining PIN attempts: User: {}, Admin: {}, Reset Code: {}",
pws.err_count_pw1(), pws.err_count_pw1(),
pws.err_count_pw3(), pws.err_count_pw3(),
pws.err_count_rc(), pws.err_count_rc(),
); );
println!(
"Signature PIN only valid once: {}",
pws.pw1_cds_valid_once()
);
if verbose { if verbose {
println!(); 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()? { if let Some(uif) = ard.uif_attestation()? {
println!( println!(
"Touch policy attestation: {} [Features: {}]", "Touch policy attestation: {} [Features: {}]",
uif.touch_policy(), uif.touch_policy(),
uif.features() 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() { if let Ok(fps) = ard.ca_fingerprints() {
println!(); for (num, fp) in fps.iter().enumerate() {
for x in fps.iter().enumerate() { if let Some(fp) = fp {
println!("CA fingerprint {}: {:x?}", x.0 + 1, x.1); println!("CA fingerprint {}: {:x?}", num + 1, fp);
}
} }
} }
} }