diff --git a/card-functionality/src/tests.rs b/card-functionality/src/tests.rs index 285a1dc..7c9d917 100644 --- a/card-functionality/src/tests.rs +++ b/card-functionality/src/tests.rs @@ -454,7 +454,7 @@ pub fn test_pw_status( ca.verify_pw3("12345678")?; - pws.set_pw1_cds_multi(true); + pws.set_pw1_cds_valid_once(false); pws.set_pw1_pin_block(true); ca.set_pw_status_bytes(&pws, false)?; diff --git a/openpgp-card-sequoia/src/sq_util.rs b/openpgp-card-sequoia/src/sq_util.rs index 919d0c0..e1be025 100644 --- a/openpgp-card-sequoia/src/sq_util.rs +++ b/openpgp-card-sequoia/src/sq_util.rs @@ -18,7 +18,7 @@ use openpgp::parse::{ }; use openpgp::policy::Policy; use openpgp::serialize::stream::{Message, Signer}; -use openpgp::Cert; +use openpgp::{Cert, Fingerprint}; use sequoia_openpgp as openpgp; use openpgp_card::KeyType; @@ -60,6 +60,36 @@ pub fn get_subkey<'a>( } } +/// Retrieve a (sub)key from a Cert, with a specified fingerprint. +pub fn get_subkey_by_fingerprint<'a>( + cert: &'a Cert, + policy: &'a dyn Policy, + fingerprint: &str, +) -> Result>> { + let fp = Fingerprint::from_hex(fingerprint)?; + + // Find usable (sub)key with Fingerprint fp. + let mut vkas: Vec<_> = cert + .keys() + .with_policy(policy, None) + .secret() + .alive() + .revoked(false) + .filter(|vka| vka.fingerprint() == fp) + .collect(); + + if vkas.is_empty() { + Ok(None) + } else if vkas.len() == 1 { + Ok(Some(vkas.pop().unwrap())) + } else { + Err(anyhow::anyhow!( + "Unexpected number of suitable (sub)key found: {}", + vkas.len() + )) + } +} + /// Produce an armored signature from `input` and a Signer `s`. pub fn sign_helper(s: S, input: &mut dyn io::Read) -> Result where diff --git a/openpgp-card/src/card_do.rs b/openpgp-card/src/card_do.rs index 1a05473..47e5ca7 100644 --- a/openpgp-card/src/card_do.rs +++ b/openpgp-card/src/card_do.rs @@ -4,8 +4,10 @@ //! OpenPGP card data objects (DO) use anyhow::{anyhow, Result}; +use chrono::{DateTime, Utc}; use std::convert::TryFrom; use std::convert::TryInto; +use std::time::{Duration, UNIX_EPOCH}; use crate::{algorithm::Algo, tlv::Tlv, Error, KeySet, KeyType}; @@ -193,6 +195,13 @@ impl KeyGenerationTime { pub fn get(&self) -> u32 { self.0 } + + pub fn formatted(&self) -> String { + let d = UNIX_EPOCH + Duration::from_secs(self.get() as u64); + let datetime = DateTime::::from(d); + + datetime.format("%Y-%m-%d %H:%M:%S").to_string() + } } /// 4.2.1 Application Identifier (AID) @@ -312,7 +321,7 @@ impl From for Sex { /// PW status Bytes (see spec page 23) #[derive(Debug, PartialEq)] pub struct PWStatusBytes { - pub(crate) pw1_cds_multi: bool, + pub(crate) pw1_cds_valid_once: bool, pub(crate) pw1_pin_block: bool, pub(crate) pw1_len: u8, pub(crate) rc_len: u8, @@ -324,8 +333,8 @@ pub struct PWStatusBytes { } impl PWStatusBytes { - pub fn set_pw1_cds_multi(&mut self, val: bool) { - self.pw1_cds_multi = val; + pub fn set_pw1_cds_valid_once(&mut self, val: bool) { + self.pw1_cds_valid_once = val; } pub fn set_pw1_pin_block(&mut self, val: bool) { self.pw1_pin_block = val; @@ -333,12 +342,45 @@ impl PWStatusBytes { pub fn set_pw3_pin_block(&mut self, val: bool) { self.pw3_pin_block = val; } + + pub fn get_pw1_cds_valid_once(&self) -> bool { + self.pw1_cds_valid_once + } + + pub fn get_err_count_pw1(&self) -> u8 { + self.err_count_pw1 + } + pub fn get_err_count_rst(&self) -> u8 { + self.err_count_rst + } + pub fn get_err_count_pw3(&self) -> u8 { + self.err_count_pw3 + } } /// Fingerprint (see spec pg. 23) #[derive(Clone, Eq, PartialEq)] pub struct Fingerprint([u8; 20]); +impl Fingerprint { + pub fn to_spaced_hex(&self) -> String { + let mut fp = String::new(); + + for i in 0..20 { + fp.push_str(&format!("{:02X}", self.0[i])); + + if i < 19 && (i % 2 == 1) { + fp.push(' '); + } + if i == 9 { + fp.push(' '); + } + } + + fp + } +} + /// Helper fn for nom parsing pub(crate) fn complete( result: nom::IResult<&[u8], O>, diff --git a/openpgp-card/src/card_do/pw_status.rs b/openpgp-card/src/card_do/pw_status.rs index b129160..1af9cbf 100644 --- a/openpgp-card/src/card_do/pw_status.rs +++ b/openpgp-card/src/card_do/pw_status.rs @@ -18,7 +18,7 @@ impl PWStatusBytes { pub(crate) fn serialize_for_put(&self, long: bool) -> Vec { let mut data = vec![]; - data.push(if !self.pw1_cds_multi { 0 } else { 1 }); + data.push(if self.pw1_cds_valid_once { 0 } else { 1 }); if long { let mut b2 = self.pw1_len; @@ -45,7 +45,7 @@ impl TryFrom<&[u8]> for PWStatusBytes { fn try_from(input: &[u8]) -> Result { if input.len() == 7 { - let pw1_cds_multi = input[0] == 0x01; + let pw1_cds_valid_once = input[0] == 0x00; let pw1_pin_block = input[1] & 0x80 != 0; let pw1_len = input[1] & 0x7f; let rc_len = input[2]; @@ -56,7 +56,7 @@ impl TryFrom<&[u8]> for PWStatusBytes { let err_count_pw3 = input[6]; Ok(Self { - pw1_cds_multi, + pw1_cds_valid_once, pw1_pin_block, pw1_len, rc_len, @@ -90,7 +90,7 @@ mod test { assert_eq!( pws, PWStatusBytes { - pw1_cds_multi: false, + pw1_cds_valid_once: true, pw1_pin_block: false, pw1_len: 0x40, rc_len: 0x40,