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
```
### 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.

View file

@ -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

View file

@ -40,8 +40,12 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
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<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 pgp = OpenPgp::new(&mut *card);
@ -498,6 +502,9 @@ fn print_status(ident: Option<String>, 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<String>, verbose: bool) -> Result<()> {
let name = name.iter().cloned().rev().collect::<Vec<_>>().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<String>, verbose: bool) -> Result<()> {
.collect::<Vec<_>>()
.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<String>, 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);
}
}
}
}