Merge branch 'heiko/attestation-key-status' into 'main'

opgpcard: Move attestation key metatdata into a separate KeySlotInfo struct

See merge request openpgp-card/openpgp-card!27
This commit is contained in:
Nora Widdecke 2022-10-28 10:29:50 +00:00
commit 19d7aa94bf
4 changed files with 102 additions and 31 deletions

View file

@ -419,6 +419,18 @@ impl<'a> Card<Transaction<'a>> {
self.state.opt.attestation_certificate() self.state.opt.attestation_certificate()
} }
pub fn attestation_key_fingerprint(&mut self) -> Result<Option<Fingerprint>, Error> {
self.state.ard.attestation_key_fingerprint()
}
pub fn attestation_key_algorithm_attributes(&mut self) -> Result<Option<Algo>, Error> {
self.state.ard.attestation_key_algorithm_attributes()
}
pub fn attestation_key_generation_time(&mut self) -> Result<Option<KeyGenerationTime>, Error> {
self.state.ard.attestation_key_generation_time()
}
/// Firmware Version, YubiKey specific (?) /// Firmware Version, YubiKey specific (?)
pub fn firmware_version(&mut self) -> Result<Vec<u8>, Error> { pub fn firmware_version(&mut self) -> Result<Vec<u8>, Error> {
self.state.opt.firmware_version() self.state.opt.firmware_version()

View file

@ -168,7 +168,7 @@ impl ApplicationRelatedData {
} }
/// Generation dates/times of key pairs /// Generation dates/times of key pairs
pub fn key_generation_times(&self) -> Result<KeySet<KeyGenerationTime>, crate::Error> { pub fn key_generation_times(&self) -> Result<KeySet<KeyGenerationTime>, Error> {
let kg = self.0.find(Tags::GenerationTimes); let kg = self.0.find(Tags::GenerationTimes);
if let Some(kg) = kg { if let Some(kg) = kg {
@ -219,6 +219,47 @@ impl ApplicationRelatedData {
} }
} }
/// Get Attestation key fingerprint.
pub fn attestation_key_fingerprint(&self) -> Result<Option<Fingerprint>, Error> {
match self.0.find(Tags::FingerprintAttestation) {
None => Ok(None),
Some(data) => {
// FIXME: move conversion logic to Fingerprint
if data.serialize().iter().any(|&b| b != 0) {
Ok(Some(Fingerprint::try_from(data.serialize().as_slice())?))
} else {
Ok(None)
}
}
}
}
/// Get Attestation key algorithm attributes.
pub fn attestation_key_algorithm_attributes(&mut self) -> Result<Option<Algo>, Error> {
match self.0.find(Tags::AlgorithmAttributesAttestation) {
None => Ok(None),
Some(data) => Ok(Some(Algo::try_from(data.serialize().as_slice())?)),
}
}
/// Get Attestation key generation time.
pub fn attestation_key_generation_time(&mut self) -> Result<Option<KeyGenerationTime>, Error> {
match self.0.find(Tags::GenerationTimeAttestation) {
None => Ok(None),
Some(data) => {
// FIXME: move conversion logic to KeyGenerationTime
// Generation time of key, binary. 4 bytes, Big Endian.
// Value shall be seconds since Jan 1, 1970. Default value is 00000000 (not specified).
assert_eq!(data.serialize().len(), 4);
match u32::from_be_bytes(data.serialize().try_into().unwrap()) {
0 => Ok(None),
kgt => Ok(Some(kgt.into())),
}
}
}
}
pub fn uif_attestation(&self) -> Result<Option<UIF>, Error> { pub fn uif_attestation(&self) -> Result<Option<UIF>, Error> {
let uif = self.0.find(Tags::UifAttestation); let uif = self.0.find(Tags::UifAttestation);

View file

@ -165,22 +165,40 @@ pub fn print_status(
} }
output.authentication_key(authentication_key); output.authentication_key(authentication_key);
// technical details about the card's state let mut attestation_key = output::KeySlotInfo::default();
if let Ok(Some(fp)) = card.attestation_key_fingerprint() {
attestation_key.fingerprint(fp.to_spaced_hex());
}
if let Ok(Some(algo)) = card.attestation_key_algorithm_attributes() {
attestation_key.algorithm(format!("{}", algo));
}
if let Ok(Some(kgt)) = card.attestation_key_generation_time() {
attestation_key.created(format!("{}", kgt.to_datetime()));
}
if let Some(uif) = card.uif_attestation()? {
attestation_key.touch_policy(format!("{}", uif.touch_policy()));
attestation_key.touch_features(format!("{}", uif.features()));
}
// TODO: get public key data for the attestation key from the card
// if command.pkm {
// if let Ok(pkm) = card.public_key(KeyType::Attestation) {
// attestation_key.public_key_material(pkm.to_string());
// }
// }
// TODO: clarify how to reliably map `card.key_information()` output into this field (see below)
// if let Some(ks) = ki.as_ref().map(|ki| ki.aut_status()) {
// attestation_key.status(format!("{}", ks));
// }
output.attestation_key(attestation_key);
// technical details about the card's state
output.user_pin_remaining_attempts(pws.err_count_pw1()); output.user_pin_remaining_attempts(pws.err_count_pw1());
output.admin_pin_remaining_attempts(pws.err_count_pw3()); output.admin_pin_remaining_attempts(pws.err_count_pw3());
output.reset_code_remaining_attempts(pws.err_count_rc()); output.reset_code_remaining_attempts(pws.err_count_rc());
// FIXME: Handle attestation key information as a separate
// KeySlotInfo! Attestation touch information should go into its
// own `Option<KeySlotInfo>`, and (if any information about the
// attestation key exists at all, which is not the case for most
// cards) it should be printed as a fourth KeySlot block.
if let Some(uif) = card.uif_attestation()? {
output.card_touch_policy(uif.touch_policy().to_string());
output.card_touch_features(uif.features().to_string());
}
if let Some(ki) = ki { if let Some(ki) = ki {
let num = ki.num_additional(); let num = ki.num_additional();
for i in 0..num { for i in 0..num {

View file

@ -18,11 +18,10 @@ pub struct Status {
signature_count: u32, signature_count: u32,
decryption_key: KeySlotInfo, decryption_key: KeySlotInfo,
authentication_key: KeySlotInfo, authentication_key: KeySlotInfo,
attestation_key: Option<KeySlotInfo>,
user_pin_remaining_attempts: u8, user_pin_remaining_attempts: u8,
admin_pin_remaining_attempts: u8, admin_pin_remaining_attempts: u8,
reset_code_remaining_attempts: u8, reset_code_remaining_attempts: u8,
card_touch_policy: String,
card_touch_features: String,
key_statuses: Vec<(u8, String)>, key_statuses: Vec<(u8, String)>,
ca_fingerprints: Vec<String>, ca_fingerprints: Vec<String>,
} }
@ -68,6 +67,10 @@ impl Status {
self.authentication_key = key; self.authentication_key = key;
} }
pub fn attestation_key(&mut self, key: KeySlotInfo) {
self.attestation_key = Some(key);
}
pub fn user_pin_remaining_attempts(&mut self, count: u8) { pub fn user_pin_remaining_attempts(&mut self, count: u8) {
self.user_pin_remaining_attempts = count; self.user_pin_remaining_attempts = count;
} }
@ -80,14 +83,6 @@ impl Status {
self.reset_code_remaining_attempts = count; self.reset_code_remaining_attempts = count;
} }
pub fn card_touch_policy(&mut self, policy: String) {
self.card_touch_policy = policy;
}
pub fn card_touch_features(&mut self, features: String) {
self.card_touch_features = features;
}
pub fn key_status(&mut self, keyref: u8, status: String) { pub fn key_status(&mut self, keyref: u8, status: String) {
self.key_statuses.push((keyref, status)); self.key_statuses.push((keyref, status));
} }
@ -150,6 +145,18 @@ impl Status {
} }
s.push('\n'); s.push('\n');
if self.verbose {
if let Some(attestation_key) = &self.attestation_key {
if attestation_key.touch_policy.is_some() || attestation_key.algorithm.is_some() {
s.push_str("Attestation key:\n");
for line in attestation_key.format(self.verbose) {
s.push_str(&format!(" {}\n", line));
}
s.push('\n');
}
}
}
s.push_str(&format!( s.push_str(&format!(
"Remaining PIN attempts: User: {}, Admin: {}, Reset Code: {}\n", "Remaining PIN attempts: User: {}, Admin: {}, Reset Code: {}\n",
self.user_pin_remaining_attempts, self.user_pin_remaining_attempts,
@ -158,11 +165,6 @@ impl Status {
)); ));
if self.verbose { if self.verbose {
s.push_str(&format!(
"Touch policy attestation: {}\n",
self.card_touch_policy
));
for (keyref, status) in self.key_statuses.iter() { for (keyref, status) in self.key_statuses.iter() {
s.push_str(&format!("Key status (#{}): {}\n", keyref, status)); s.push_str(&format!("Key status (#{}): {}\n", keyref, status));
} }
@ -183,11 +185,10 @@ impl Status {
signature_count: self.signature_count, signature_count: self.signature_count,
decryption_key: self.decryption_key.clone(), decryption_key: self.decryption_key.clone(),
authentication_key: self.authentication_key.clone(), authentication_key: self.authentication_key.clone(),
attestation_key: self.attestation_key.clone(),
user_pin_remaining_attempts: self.user_pin_remaining_attempts, user_pin_remaining_attempts: self.user_pin_remaining_attempts,
admin_pin_remaining_attempts: self.admin_pin_remaining_attempts, admin_pin_remaining_attempts: self.admin_pin_remaining_attempts,
reset_code_remaining_attempts: self.reset_code_remaining_attempts, reset_code_remaining_attempts: self.reset_code_remaining_attempts,
card_touch_policy: self.card_touch_policy.clone(),
card_touch_features: self.card_touch_features.clone(),
key_statuses: self.key_statuses.clone(), key_statuses: self.key_statuses.clone(),
ca_fingerprints: self.ca_fingerprints.clone(), ca_fingerprints: self.ca_fingerprints.clone(),
}) })
@ -232,11 +233,10 @@ pub struct StatusV0 {
signature_count: u32, signature_count: u32,
decryption_key: KeySlotInfo, decryption_key: KeySlotInfo,
authentication_key: KeySlotInfo, authentication_key: KeySlotInfo,
attestation_key: Option<KeySlotInfo>,
user_pin_remaining_attempts: u8, user_pin_remaining_attempts: u8,
admin_pin_remaining_attempts: u8, admin_pin_remaining_attempts: u8,
reset_code_remaining_attempts: u8, reset_code_remaining_attempts: u8,
card_touch_policy: String,
card_touch_features: String,
key_statuses: Vec<(u8, String)>, key_statuses: Vec<(u8, String)>,
ca_fingerprints: Vec<String>, ca_fingerprints: Vec<String>,
} }