openpgp-card-sequoia: refactor, base API around a Card<State> type

This commit is contained in:
Heiko Schaefer 2022-10-27 09:46:14 +02:00
parent 9314a1bb1f
commit da65260736
No known key found for this signature in database
GPG key ID: 4A849A1904CCBD7D
4 changed files with 242 additions and 195 deletions

View file

@ -10,10 +10,10 @@ use sequoia_openpgp::policy::StandardPolicy;
use sequoia_openpgp::Cert; use sequoia_openpgp::Cert;
use openpgp_card::card_do::Sex; use openpgp_card::card_do::Sex;
use openpgp_card::{KeyType, OpenPgp}; use openpgp_card::KeyType;
use openpgp_card_pcsc::PcscBackend; use openpgp_card_pcsc::PcscBackend;
use openpgp_card_sequoia::card::Open; use openpgp_card_sequoia::card::{Card, Open};
use openpgp_card_sequoia::sq_util; use openpgp_card_sequoia::sq_util;
// Filename of test key and test message to use // Filename of test key and test message to use
@ -36,47 +36,47 @@ fn main() -> Result<(), Box<dyn Error>> {
if let Ok(test_card_ident) = test_card_ident { if let Ok(test_card_ident) = test_card_ident {
let backend = PcscBackend::open_by_ident(&test_card_ident, None)?; let backend = PcscBackend::open_by_ident(&test_card_ident, None)?;
let mut pgp = OpenPgp::new(backend);
let mut open = Open::new(pgp.transaction()?)?; let mut card: Card<Open> = backend.into();
let mut transaction = card.transaction()?;
// card metadata // card metadata
let app_id = open.application_identifier()?; let app_id = transaction.application_identifier()?;
println!("{:x?}\n", app_id); println!("{:x?}\n", app_id);
let eli = open.extended_length_information()?; let eli = transaction.extended_length_information()?;
println!("extended_length_info: {:?}\n", eli); println!("extended_length_info: {:?}\n", eli);
let hist = open.historical_bytes()?; let hist = transaction.historical_bytes()?;
println!("{:#x?}\n", hist); println!("{:#x?}\n", hist);
let ext = open.extended_capabilities()?; let ext = transaction.extended_capabilities()?;
println!("{:#x?}\n", ext); println!("{:#x?}\n", ext);
let pws = open.pw_status_bytes()?; let pws = transaction.pw_status_bytes()?;
println!("{:#x?}\n", pws); println!("{:#x?}\n", pws);
// cardholder // cardholder
let ch = open.cardholder_related_data()?; let ch = transaction.cardholder_related_data()?;
println!("{:#x?}\n", ch); println!("{:#x?}\n", ch);
// crypto-ish metadata // crypto-ish metadata
let fp = open.fingerprints()?; let fp = transaction.fingerprints()?;
println!("Fingerprint {:#x?}\n", fp); println!("Fingerprint {:#x?}\n", fp);
match open.algorithm_information() { match transaction.algorithm_information() {
Ok(Some(ai)) => println!("Algorithm information:\n{}", ai), Ok(Some(ai)) => println!("Algorithm information:\n{}", ai),
Ok(None) => println!("No Algorithm information found"), Ok(None) => println!("No Algorithm information found"),
Err(e) => println!("Error getting Algorithm information: {:?}", e), Err(e) => println!("Error getting Algorithm information: {:?}", e),
} }
println!("Current algorithm attributes on card:"); println!("Current algorithm attributes on card:");
let algo = open.algorithm_attributes(KeyType::Signing)?; let algo = transaction.algorithm_attributes(KeyType::Signing)?;
println!("Sig: {}", algo); println!("Sig: {}", algo);
let algo = open.algorithm_attributes(KeyType::Decryption)?; let algo = transaction.algorithm_attributes(KeyType::Decryption)?;
println!("Dec: {}", algo); println!("Dec: {}", algo);
let algo = open.algorithm_attributes(KeyType::Authentication)?; let algo = transaction.algorithm_attributes(KeyType::Authentication)?;
println!("Aut: {}", algo); println!("Aut: {}", algo);
println!(); println!();
@ -88,20 +88,20 @@ fn main() -> Result<(), Box<dyn Error>> {
assert_eq!(app_id.ident(), test_card_ident.to_ascii_uppercase()); assert_eq!(app_id.ident(), test_card_ident.to_ascii_uppercase());
let check = open.check_admin_verified(); let check = transaction.check_admin_verified();
println!("has admin (pw3) been verified yet?\n{:x?}\n", check); println!("has admin (pw3) been verified yet?\n{:x?}\n", check);
println!("factory reset\n"); println!("factory reset\n");
open.factory_reset()?; transaction.factory_reset()?;
open.verify_admin(b"12345678")?; transaction.verify_admin(b"12345678")?;
println!("verify for admin ok"); println!("verify for admin ok");
let check = open.check_user_verified(); let check = transaction.check_user_verified();
println!("has user (pw1/82) been verified yet? {:x?}", check); println!("has user (pw1/82) been verified yet? {:x?}", check);
// Use Admin access to card // Use Admin access to card
let mut admin = open.admin_card().expect("just verified"); let mut admin = transaction.admin_card().expect("just verified");
println!(); println!();
@ -141,25 +141,25 @@ fn main() -> Result<(), Box<dyn Error>> {
// Open fresh Card for decrypt // Open fresh Card for decrypt
// ----------------------------- // -----------------------------
let backend = PcscBackend::open_by_ident(&test_card_ident, None)?; let backend = PcscBackend::open_by_ident(&test_card_ident, None)?;
let mut pgp = OpenPgp::new(backend);
let mut open = Open::new(pgp.transaction()?)?; let mut card: Card<Open> = backend.into();
let mut transaction = card.transaction()?;
// Check that we're still using the expected card // Check that we're still using the expected card
let app_id = open.application_identifier()?; let app_id = transaction.application_identifier()?;
assert_eq!(app_id.ident(), test_card_ident.to_ascii_uppercase()); assert_eq!(app_id.ident(), test_card_ident.to_ascii_uppercase());
let check = open.check_user_verified(); let check = transaction.check_user_verified();
println!("has user (pw1/82) been verified yet?\n{:x?}\n", check); println!("has user (pw1/82) been verified yet?\n{:x?}\n", check);
open.verify_user(b"123456")?; transaction.verify_user(b"123456")?;
println!("verify for user (pw1/82) ok"); println!("verify for user (pw1/82) ok");
let check = open.check_user_verified(); let check = transaction.check_user_verified();
println!("has user (pw1/82) been verified yet?\n{:x?}\n", check); println!("has user (pw1/82) been verified yet?\n{:x?}\n", check);
// Use User access to card // Use User access to card
let mut user = open let mut user = transaction
.user_card() .user_card()
.expect("We just validated, this should not fail"); .expect("We just validated, this should not fail");
@ -181,16 +181,16 @@ fn main() -> Result<(), Box<dyn Error>> {
// Open fresh Card for signing // Open fresh Card for signing
// ----------------------------- // -----------------------------
let backend = PcscBackend::open_by_ident(&test_card_ident, None)?; let backend = PcscBackend::open_by_ident(&test_card_ident, None)?;
let mut pgp = OpenPgp::new(backend);
let mut open = Open::new(pgp.transaction()?)?; let mut card: Card<Open> = backend.into();
let mut transaction = card.transaction()?;
// Sign // Sign
open.verify_user_for_signing(b"123456")?; transaction.verify_user_for_signing(b"123456")?;
println!("verify for sign (pw1/81) ok\n"); println!("verify for sign (pw1/81) ok\n");
// Use Sign access to card // Use Sign access to card
let mut sign = open.signing_card().expect("just verified"); let mut sign = transaction.signing_card().expect("just verified");
let _cert = Cert::from_file(TEST_KEY_PATH)?; let _cert = Cert::from_file(TEST_KEY_PATH)?;
@ -213,9 +213,8 @@ fn main() -> Result<(), Box<dyn Error>> {
println!("The following OpenPGP cards are connected to your system:"); println!("The following OpenPGP cards are connected to your system:");
for backend in PcscBackend::cards(None)? { for backend in PcscBackend::cards(None)? {
let mut pgp = OpenPgp::new(backend); let mut card: Card<Open> = backend.into();
let open = card.transaction()?;
let open = Open::new(pgp.transaction()?)?;
println!(" {}", open.application_identifier()?.ident()); println!(" {}", open.application_identifier()?.ident());
} }

View file

@ -22,39 +22,33 @@ use crate::signer::CardSigner;
use crate::util::{public_to_fingerprint, vka_as_uploadable_key}; use crate::util::{public_to_fingerprint, vka_as_uploadable_key};
use crate::PublicKey; use crate::PublicKey;
pub trait State {}
impl State for Open {}
impl State for Transaction<'_> {}
impl State for User<'_, '_> {}
impl State for Sign<'_, '_> {}
impl State for Admin<'_, '_> {}
/// Representation of an opened OpenPGP card in its base state (i.e. no /// Representation of an opened OpenPGP card in its base state (i.e. no
/// passwords have been verified, default authorization applies). /// passwords have been verified, default authorization applies).
/// No transaction has been started. /// No transaction has been started.
pub struct Card { pub struct Open {
pgp: OpenPgp, pgp: OpenPgp,
} }
impl Card {
pub fn new<B>(backend: B) -> Self
where
B: Into<Box<dyn CardBackend + Send + Sync>>,
{
let pgp = OpenPgp::new(backend.into());
Self { pgp }
}
pub fn transaction(&mut self) -> Result<Open, Error> {
let t = self.pgp.transaction()?;
Open::new(t)
}
}
/// Representation of an opened OpenPGP card in its base state (i.e. no /// Representation of an opened OpenPGP card in its base state (i.e. no
/// passwords have been verified, default authorization applies). /// passwords have been verified, default authorization applies).
/// A transaction has been started. /// A transaction has been started.
pub struct Open<'a> { pub struct Transaction<'a> {
opt: OpenPgpTransaction<'a>, opt: OpenPgpTransaction<'a>,
// Cache of "application related data". // Cache of "application related data".
// //
// FIXME: Should be invalidated when changing data on the card! // FIXME: Should be invalidated when changing data on the card!
// (e.g. uploading keys, etc) // (e.g. uploading keys, etc)
//
// This field should probably be an Option<> that gets invalidated when appropriate and
// re-fetched lazily.
ard: ApplicationRelatedData, ard: ApplicationRelatedData,
// verify status of pw1 // verify status of pw1
@ -67,16 +61,67 @@ pub struct Open<'a> {
pw3: bool, pw3: bool,
} }
impl<'a> Open<'a> { /// An OpenPGP card after successfully verifying PW1 in mode 82
pub fn new(mut ca: OpenPgpTransaction<'a>) -> Result<Self, Error> { /// (verification for user operations other than signing)
let ard = ca.application_related_data()?; pub struct User<'app, 'open> {
tx: &'open mut Card<Transaction<'app>>,
}
/// An OpenPGP card after successfully verifying PW1 in mode 81
/// (verification for signing)
pub struct Sign<'app, 'open> {
tx: &'open mut Card<Transaction<'app>>,
}
/// An OpenPGP card after successful verification of PW3 ("Admin privileges")
pub struct Admin<'app, 'open> {
tx: &'open mut Card<Transaction<'app>>,
}
pub struct Card<S>
where
S: State,
{
state: S,
}
impl<B> From<B> for Card<Open>
where
B: Into<Box<dyn CardBackend + Send + Sync>>,
{
fn from(backend: B) -> Self {
let pgp = OpenPgp::new(backend.into());
Card::<Open> {
state: Open { pgp },
}
}
}
impl Card<Open> {
pub fn transaction(&mut self) -> Result<Card<Transaction>, Error> {
let opt = self.state.pgp.transaction()?;
Card::<Transaction>::new(opt)
}
}
impl<'a> Card<Transaction<'a>> {
/// Do not use!
///
/// FIXME: this interface is currently used in `card-functionality`, for testing.
/// It will be removed.
pub fn new(mut opt: OpenPgpTransaction<'a>) -> Result<Self, Error> {
let ard = opt.application_related_data()?;
Ok(Self { Ok(Self {
opt: ca, state: Transaction {
ard, opt,
pw1: false, ard,
pw1_sign: false, pw1: false,
pw3: false, pw1_sign: false,
pw3: false,
},
}) })
} }
@ -86,38 +131,39 @@ impl<'a> Open<'a> {
/// This is needed e.g. after importing or generating keys on a card, to /// This is needed e.g. after importing or generating keys on a card, to
/// see these changes reflected in `self.ard`. /// see these changes reflected in `self.ard`.
pub fn reload_ard(&mut self) -> Result<(), Error> { pub fn reload_ard(&mut self) -> Result<(), Error> {
self.ard = self.opt.application_related_data()?; // FIXME: this should be implemented internally, transparent to users
self.state.ard = self.state.opt.application_related_data()?;
Ok(()) Ok(())
} }
pub fn feature_pinpad_verify(&mut self) -> bool { pub fn feature_pinpad_verify(&mut self) -> bool {
self.opt.feature_pinpad_verify() self.state.opt.feature_pinpad_verify()
} }
pub fn feature_pinpad_modify(&mut self) -> bool { pub fn feature_pinpad_modify(&mut self) -> bool {
self.opt.feature_pinpad_modify() self.state.opt.feature_pinpad_modify()
} }
pub fn verify_user(&mut self, pin: &[u8]) -> Result<(), Error> { pub fn verify_user(&mut self, pin: &[u8]) -> Result<(), Error> {
self.opt.verify_pw1_user(pin)?; self.state.opt.verify_pw1_user(pin)?;
self.pw1 = true; self.state.pw1 = true;
Ok(()) Ok(())
} }
pub fn verify_user_pinpad(&mut self, pinpad_prompt: &dyn Fn()) -> Result<(), Error> { pub fn verify_user_pinpad(&mut self, pinpad_prompt: &dyn Fn()) -> Result<(), Error> {
pinpad_prompt(); pinpad_prompt();
self.opt.verify_pw1_user_pinpad()?; self.state.opt.verify_pw1_user_pinpad()?;
self.pw1 = true; self.state.pw1 = true;
Ok(()) Ok(())
} }
pub fn verify_user_for_signing(&mut self, pin: &[u8]) -> Result<(), Error> { pub fn verify_user_for_signing(&mut self, pin: &[u8]) -> Result<(), Error> {
self.opt.verify_pw1_sign(pin)?; self.state.opt.verify_pw1_sign(pin)?;
// FIXME: depending on card mode, pw1_sign is only usable once // FIXME: depending on card mode, pw1_sign is only usable once
self.pw1_sign = true; self.state.pw1_sign = true;
Ok(()) Ok(())
} }
@ -127,25 +173,25 @@ impl<'a> Open<'a> {
) -> Result<(), Error> { ) -> Result<(), Error> {
pinpad_prompt(); pinpad_prompt();
self.opt.verify_pw1_sign_pinpad()?; self.state.opt.verify_pw1_sign_pinpad()?;
// FIXME: depending on card mode, pw1_sign is only usable once // FIXME: depending on card mode, pw1_sign is only usable once
self.pw1_sign = true; self.state.pw1_sign = true;
Ok(()) Ok(())
} }
pub fn verify_admin(&mut self, pin: &[u8]) -> Result<(), Error> { pub fn verify_admin(&mut self, pin: &[u8]) -> Result<(), Error> {
self.opt.verify_pw3(pin)?; self.state.opt.verify_pw3(pin)?;
self.pw3 = true; self.state.pw3 = true;
Ok(()) Ok(())
} }
pub fn verify_admin_pinpad(&mut self, pinpad_prompt: &dyn Fn()) -> Result<(), Error> { pub fn verify_admin_pinpad(&mut self, pinpad_prompt: &dyn Fn()) -> Result<(), Error> {
pinpad_prompt(); pinpad_prompt();
self.opt.verify_pw3_pinpad()?; self.state.opt.verify_pw3_pinpad()?;
self.pw3 = true; self.state.pw3 = true;
Ok(()) Ok(())
} }
@ -153,65 +199,71 @@ impl<'a> Open<'a> {
/// ///
/// NOTE: on some cards this functionality seems broken. /// NOTE: on some cards this functionality seems broken.
pub fn check_user_verified(&mut self) -> Result<(), Error> { pub fn check_user_verified(&mut self) -> Result<(), Error> {
self.opt.check_pw1_user() self.state.opt.check_pw1_user()
} }
/// Ask the card if the admin password has been successfully verified. /// Ask the card if the admin password has been successfully verified.
/// ///
/// NOTE: on some cards this functionality seems broken. /// NOTE: on some cards this functionality seems broken.
pub fn check_admin_verified(&mut self) -> Result<(), Error> { pub fn check_admin_verified(&mut self) -> Result<(), Error> {
self.opt.check_pw3() self.state.opt.check_pw3()
} }
pub fn change_user_pin(&mut self, old: &[u8], new: &[u8]) -> Result<(), Error> { pub fn change_user_pin(&mut self, old: &[u8], new: &[u8]) -> Result<(), Error> {
self.opt.change_pw1(old, new) self.state.opt.change_pw1(old, new)
} }
pub fn change_user_pin_pinpad(&mut self, pinpad_prompt: &dyn Fn()) -> Result<(), Error> { pub fn change_user_pin_pinpad(&mut self, pinpad_prompt: &dyn Fn()) -> Result<(), Error> {
pinpad_prompt(); pinpad_prompt();
self.opt.change_pw1_pinpad() self.state.opt.change_pw1_pinpad()
} }
pub fn reset_user_pin(&mut self, rst: &[u8], new: &[u8]) -> Result<(), Error> { pub fn reset_user_pin(&mut self, rst: &[u8], new: &[u8]) -> Result<(), Error> {
self.opt.reset_retry_counter_pw1(new, Some(rst)) self.state.opt.reset_retry_counter_pw1(new, Some(rst))
} }
pub fn change_admin_pin(&mut self, old: &[u8], new: &[u8]) -> Result<(), Error> { pub fn change_admin_pin(&mut self, old: &[u8], new: &[u8]) -> Result<(), Error> {
self.opt.change_pw3(old, new) self.state.opt.change_pw3(old, new)
} }
pub fn change_admin_pin_pinpad(&mut self, pinpad_prompt: &dyn Fn()) -> Result<(), Error> { pub fn change_admin_pin_pinpad(&mut self, pinpad_prompt: &dyn Fn()) -> Result<(), Error> {
pinpad_prompt(); pinpad_prompt();
self.opt.change_pw3_pinpad() self.state.opt.change_pw3_pinpad()
} }
/// Get a view of the card authenticated for "User" commands. /// Get a view of the card authenticated for "User" commands.
pub fn user_card<'b>(&'b mut self) -> Option<User<'a, 'b>> { pub fn user_card<'b>(&'b mut self) -> Option<Card<User<'a, 'b>>> {
Some(User { oc: self }) Some(Card::<User> {
state: User { tx: self },
})
} }
/// Get a view of the card authenticated for Signing. /// Get a view of the card authenticated for Signing.
pub fn signing_card<'b>(&'b mut self) -> Option<Sign<'a, 'b>> { pub fn signing_card<'b>(&'b mut self) -> Option<Card<Sign<'a, 'b>>> {
Some(Sign { oc: self }) Some(Card::<Sign> {
state: Sign { tx: self },
})
} }
/// Get a view of the card authenticated for "Admin" commands. /// Get a view of the card authenticated for "Admin" commands.
pub fn admin_card<'b>(&'b mut self) -> Option<Admin<'a, 'b>> { pub fn admin_card<'b>(&'b mut self) -> Option<Card<Admin<'a, 'b>>> {
Some(Admin { oc: self }) Some(Card::<Admin> {
state: Admin { tx: self },
})
} }
// --- application data --- // --- application data ---
pub fn application_identifier(&self) -> Result<ApplicationIdentifier, Error> { pub fn application_identifier(&self) -> Result<ApplicationIdentifier, Error> {
self.ard.application_id() self.state.ard.application_id()
} }
pub fn historical_bytes(&self) -> Result<HistoricalBytes, Error> { pub fn historical_bytes(&self) -> Result<HistoricalBytes, Error> {
self.ard.historical_bytes() self.state.ard.historical_bytes()
} }
pub fn extended_length_information(&self) -> Result<Option<ExtendedLengthInfo>, Error> { pub fn extended_length_information(&self) -> Result<Option<ExtendedLengthInfo>, Error> {
self.ard.extended_length_information() self.state.ard.extended_length_information()
} }
#[allow(dead_code)] #[allow(dead_code)]
@ -225,48 +277,48 @@ impl<'a> Open<'a> {
} }
pub fn extended_capabilities(&self) -> Result<ExtendedCapabilities, Error> { pub fn extended_capabilities(&self) -> Result<ExtendedCapabilities, Error> {
self.ard.extended_capabilities() self.state.ard.extended_capabilities()
} }
pub fn algorithm_attributes(&self, key_type: KeyType) -> Result<Algo, Error> { pub fn algorithm_attributes(&self, key_type: KeyType) -> Result<Algo, Error> {
self.ard.algorithm_attributes(key_type) self.state.ard.algorithm_attributes(key_type)
} }
/// PW status Bytes /// PW status Bytes
pub fn pw_status_bytes(&self) -> Result<PWStatusBytes, Error> { pub fn pw_status_bytes(&self) -> Result<PWStatusBytes, Error> {
self.ard.pw_status_bytes() self.state.ard.pw_status_bytes()
} }
pub fn fingerprints(&self) -> Result<KeySet<Fingerprint>, Error> { pub fn fingerprints(&self) -> Result<KeySet<Fingerprint>, Error> {
self.ard.fingerprints() self.state.ard.fingerprints()
} }
pub fn ca_fingerprints(&self) -> Result<[Option<Fingerprint>; 3], Error> { pub fn ca_fingerprints(&self) -> Result<[Option<Fingerprint>; 3], Error> {
self.ard.ca_fingerprints() self.state.ard.ca_fingerprints()
} }
pub fn key_generation_times(&self) -> Result<KeySet<KeyGenerationTime>, Error> { pub fn key_generation_times(&self) -> Result<KeySet<KeyGenerationTime>, Error> {
self.ard.key_generation_times() self.state.ard.key_generation_times()
} }
pub fn key_information(&self) -> Result<Option<KeyInformation>, Error> { pub fn key_information(&self) -> Result<Option<KeyInformation>, Error> {
self.ard.key_information() self.state.ard.key_information()
} }
pub fn uif_signing(&self) -> Result<Option<UIF>, Error> { pub fn uif_signing(&self) -> Result<Option<UIF>, Error> {
self.ard.uif_pso_cds() self.state.ard.uif_pso_cds()
} }
pub fn uif_decryption(&self) -> Result<Option<UIF>, Error> { pub fn uif_decryption(&self) -> Result<Option<UIF>, Error> {
self.ard.uif_pso_dec() self.state.ard.uif_pso_dec()
} }
pub fn uif_authentication(&self) -> Result<Option<UIF>, Error> { pub fn uif_authentication(&self) -> Result<Option<UIF>, Error> {
self.ard.uif_pso_aut() self.state.ard.uif_pso_aut()
} }
pub fn uif_attestation(&self) -> Result<Option<UIF>, Error> { pub fn uif_attestation(&self) -> Result<Option<UIF>, Error> {
self.ard.uif_attestation() self.state.ard.uif_attestation()
} }
// --- optional private DOs (0101 - 0104) --- // --- optional private DOs (0101 - 0104) ---
@ -276,17 +328,17 @@ impl<'a> Open<'a> {
// --- URL (5f50) --- // --- URL (5f50) ---
pub fn url(&mut self) -> Result<String, Error> { pub fn url(&mut self) -> Result<String, Error> {
Ok(String::from_utf8_lossy(&self.opt.url()?).to_string()) Ok(String::from_utf8_lossy(&self.state.opt.url()?).to_string())
} }
// --- cardholder related data (65) --- // --- cardholder related data (65) ---
pub fn cardholder_related_data(&mut self) -> Result<CardholderRelatedData, Error> { pub fn cardholder_related_data(&mut self) -> Result<CardholderRelatedData, Error> {
self.opt.cardholder_related_data() self.state.opt.cardholder_related_data()
} }
/// Get cardholder name as a String (this also normalizes the "<" and "<<" filler chars) /// Get cardholder name as a String (this also normalizes the "<" and "<<" filler chars)
pub fn cardholder_name(&mut self) -> Result<String, Error> { pub fn cardholder_name(&mut self) -> Result<String, Error> {
let crd = self.opt.cardholder_related_data()?; let crd = self.state.opt.cardholder_related_data()?;
if let Some(name) = crd.name() { if let Some(name) = crd.name() {
let name = String::from_utf8_lossy(name).to_string(); let name = String::from_utf8_lossy(name).to_string();
@ -305,12 +357,12 @@ impl<'a> Open<'a> {
// --- security support template (7a) --- // --- security support template (7a) ---
pub fn security_support_template(&mut self) -> Result<SecuritySupportTemplate, Error> { pub fn security_support_template(&mut self) -> Result<SecuritySupportTemplate, Error> {
self.opt.security_support_template() self.state.opt.security_support_template()
} }
/// SELECT DATA ("select a DO in the current template"). /// SELECT DATA ("select a DO in the current template").
pub fn select_data(&mut self, num: u8, tag: &[u8], yk_workaround: bool) -> Result<(), Error> { pub fn select_data(&mut self, num: u8, tag: &[u8], yk_workaround: bool) -> Result<(), Error> {
self.opt.select_data(num, tag, yk_workaround) self.state.opt.select_data(num, tag, yk_workaround)
} }
/// Get cardholder certificate. /// Get cardholder certificate.
@ -318,7 +370,7 @@ impl<'a> Open<'a> {
/// Call select_data() before calling this fn to select a particular /// Call select_data() before calling this fn to select a particular
/// certificate (if the card supports multiple certificates). /// certificate (if the card supports multiple certificates).
pub fn cardholder_certificate(&mut self) -> Result<Vec<u8>, Error> { pub fn cardholder_certificate(&mut self) -> Result<Vec<u8>, Error> {
self.opt.cardholder_certificate() self.state.opt.cardholder_certificate()
} }
/// "GET NEXT DATA" for the DO cardholder certificate. /// "GET NEXT DATA" for the DO cardholder certificate.
@ -326,7 +378,7 @@ impl<'a> Open<'a> {
/// Cardholder certificate data for multiple slots can be read from the card by first calling /// Cardholder certificate data for multiple slots can be read from the card by first calling
/// cardholder_certificate(), followed by up to two calls to next_cardholder_certificate(). /// cardholder_certificate(), followed by up to two calls to next_cardholder_certificate().
pub fn next_cardholder_certificate(&mut self) -> Result<Vec<u8>, Error> { pub fn next_cardholder_certificate(&mut self) -> Result<Vec<u8>, Error> {
self.opt.next_cardholder_certificate() self.state.opt.next_cardholder_certificate()
} }
// DO "Algorithm Information" (0xFA) // DO "Algorithm Information" (0xFA)
@ -340,7 +392,7 @@ impl<'a> Open<'a> {
return Ok(None); return Ok(None);
} }
self.opt.algorithm_information() self.state.opt.algorithm_information()
} }
/// "MANAGE SECURITY ENVIRONMENT" /// "MANAGE SECURITY ENVIRONMENT"
@ -350,19 +402,21 @@ impl<'a> Open<'a> {
for_operation: KeyType, for_operation: KeyType,
key_ref: KeyType, key_ref: KeyType,
) -> Result<(), Error> { ) -> Result<(), Error> {
self.opt.manage_security_environment(for_operation, key_ref) self.state
.opt
.manage_security_environment(for_operation, key_ref)
} }
// ---------- // ----------
/// Get "Attestation Certificate (Yubico)" /// Get "Attestation Certificate (Yubico)"
pub fn attestation_certificate(&mut self) -> Result<Vec<u8>, Error> { pub fn attestation_certificate(&mut self) -> Result<Vec<u8>, Error> {
self.opt.attestation_certificate() self.state.opt.attestation_certificate()
} }
/// 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.opt.firmware_version() self.state.opt.firmware_version()
} }
/// Set "identity", Nitrokey Start specific (possible values: 0, 1, 2). /// Set "identity", Nitrokey Start specific (possible values: 0, 1, 2).
@ -374,7 +428,7 @@ impl<'a> Open<'a> {
/// Each virtual card identity behaves like a separate, independent OpenPGP card. /// Each virtual card identity behaves like a separate, independent OpenPGP card.
pub fn set_identity(&mut self, id: u8) -> Result<(), Error> { pub fn set_identity(&mut self, id: u8) -> Result<(), Error> {
// FIXME: what is in the returned data - is it ever useful? // FIXME: what is in the returned data - is it ever useful?
let _ = self.opt.set_identity(id)?; let _ = self.state.opt.set_identity(id)?;
Ok(()) Ok(())
} }
@ -382,36 +436,31 @@ impl<'a> Open<'a> {
// ---------- // ----------
pub fn public_key(&mut self, key_type: KeyType) -> Result<PublicKeyMaterial, Error> { pub fn public_key(&mut self, key_type: KeyType) -> Result<PublicKeyMaterial, Error> {
self.opt.public_key(key_type) self.state.opt.public_key(key_type)
} }
// ---------- // ----------
/// Delete all state on this OpenPGP card /// Delete all state on this OpenPGP card
pub fn factory_reset(&mut self) -> Result<(), Error> { pub fn factory_reset(&mut self) -> Result<(), Error> {
self.opt.factory_reset() self.state.opt.factory_reset()
} }
} }
/// An OpenPGP card after successfully verifying PW1 in mode 82 impl<'app, 'open> Card<User<'app, 'open>> {
/// (verification for user operations other than signing) /// Helper fn to easily access underlying openpgp_card object
pub struct User<'app, 'open> { fn card(&mut self) -> &mut OpenPgpTransaction<'app> {
oc: &'open mut Open<'app>, &mut self.state.tx.state.opt
} }
impl<'app, 'open> User<'app, 'open> {
pub fn decryptor( pub fn decryptor(
&mut self, &mut self,
touch_prompt: &'open (dyn Fn() + Send + Sync), touch_prompt: &'open (dyn Fn() + Send + Sync),
) -> Result<CardDecryptor<'_, 'app>, Error> { ) -> Result<CardDecryptor<'_, 'app>, Error> {
let pk = crate::util::key_slot(self.oc, KeyType::Decryption)? let pk = crate::util::key_slot(self.state.tx, KeyType::Decryption)?
.expect("Couldn't get decryption pubkey from card"); .expect("Couldn't get decryption pubkey from card");
Ok(CardDecryptor::with_pubkey( Ok(CardDecryptor::with_pubkey(self.card(), pk, touch_prompt))
&mut self.oc.opt,
pk,
touch_prompt,
))
} }
pub fn decryptor_from_public( pub fn decryptor_from_public(
@ -419,18 +468,18 @@ impl<'app, 'open> User<'app, 'open> {
pubkey: PublicKey, pubkey: PublicKey,
touch_prompt: &'open (dyn Fn() + Send + Sync), touch_prompt: &'open (dyn Fn() + Send + Sync),
) -> CardDecryptor<'_, 'app> { ) -> CardDecryptor<'_, 'app> {
CardDecryptor::with_pubkey(&mut self.oc.opt, pubkey, touch_prompt) CardDecryptor::with_pubkey(self.card(), pubkey, touch_prompt)
} }
pub fn authenticator( pub fn authenticator(
&mut self, &mut self,
touch_prompt: &'open (dyn Fn() + Send + Sync), touch_prompt: &'open (dyn Fn() + Send + Sync),
) -> Result<CardSigner<'_, 'app>, Error> { ) -> Result<CardSigner<'_, 'app>, Error> {
let pk = crate::util::key_slot(self.oc, KeyType::Authentication)? let pk = crate::util::key_slot(self.state.tx, KeyType::Authentication)?
.expect("Couldn't get authentication pubkey from card"); .expect("Couldn't get authentication pubkey from card");
Ok(CardSigner::with_pubkey_for_auth( Ok(CardSigner::with_pubkey_for_auth(
&mut self.oc.opt, self.card(),
pk, pk,
touch_prompt, touch_prompt,
)) ))
@ -440,17 +489,16 @@ impl<'app, 'open> User<'app, 'open> {
pubkey: PublicKey, pubkey: PublicKey,
touch_prompt: &'open (dyn Fn() + Send + Sync), touch_prompt: &'open (dyn Fn() + Send + Sync),
) -> CardSigner<'_, 'app> { ) -> CardSigner<'_, 'app> {
CardSigner::with_pubkey_for_auth(&mut self.oc.opt, pubkey, touch_prompt) CardSigner::with_pubkey_for_auth(self.card(), pubkey, touch_prompt)
} }
} }
/// An OpenPGP card after successfully verifying PW1 in mode 81 impl<'app, 'open> Card<Sign<'app, 'open>> {
/// (verification for signing) /// Helper fn to easily access underlying openpgp_card object
pub struct Sign<'app, 'open> { fn card(&mut self) -> &mut OpenPgpTransaction<'app> {
oc: &'open mut Open<'app>, &mut self.state.tx.state.opt
} }
impl<'app, 'open> Sign<'app, 'open> {
pub fn signer( pub fn signer(
&mut self, &mut self,
touch_prompt: &'open (dyn Fn() + Send + Sync), touch_prompt: &'open (dyn Fn() + Send + Sync),
@ -458,10 +506,10 @@ impl<'app, 'open> Sign<'app, 'open> {
// FIXME: depending on the setting in "PW1 Status byte", only one // FIXME: depending on the setting in "PW1 Status byte", only one
// signature can be made after verification for signing // signature can be made after verification for signing
let pk = crate::util::key_slot(self.oc, KeyType::Signing)? let pk = crate::util::key_slot(self.state.tx, KeyType::Signing)?
.expect("Couldn't get signing pubkey from card"); .expect("Couldn't get signing pubkey from card");
Ok(CardSigner::with_pubkey(&mut self.oc.opt, pk, touch_prompt)) Ok(CardSigner::with_pubkey(self.card(), pk, touch_prompt))
} }
pub fn signer_from_public( pub fn signer_from_public(
@ -472,7 +520,7 @@ impl<'app, 'open> Sign<'app, 'open> {
// FIXME: depending on the setting in "PW1 Status byte", only one // FIXME: depending on the setting in "PW1 Status byte", only one
// signature can be made after verification for signing // signature can be made after verification for signing
CardSigner::with_pubkey(&mut self.oc.opt, pubkey, touch_prompt) CardSigner::with_pubkey(self.card(), pubkey, touch_prompt)
} }
/// Generate Attestation (Yubico) /// Generate Attestation (Yubico)
@ -484,28 +532,28 @@ impl<'app, 'open> Sign<'app, 'open> {
// Touch is required if: // Touch is required if:
// - the card supports the feature // - the card supports the feature
// - and the policy is set to a value other than 'Off' // - and the policy is set to a value other than 'Off'
if let Some(uif) = self.oc.ard.uif_attestation()? { if let Some(uif) = self.state.tx.state.ard.uif_attestation()? {
if uif.touch_policy().touch_required() { if uif.touch_policy().touch_required() {
(touch_prompt)(); (touch_prompt)();
} }
} }
self.oc.opt.generate_attestation(key_type) self.card().generate_attestation(key_type)
} }
} }
/// An OpenPGP card after successful verification of PW3 ("Admin privileges") impl<'app, 'open> Card<Admin<'app, 'open>> {
pub struct Admin<'app, 'open> { pub fn as_open(&'_ mut self) -> &mut Card<Transaction<'app>> {
oc: &'open mut Open<'app>, self.state.tx
} }
impl<'app, 'open> Admin<'app, 'open> { /// Helper fn to easily access underlying openpgp_card object
pub fn as_open(&'_ mut self) -> &mut Open<'app> { fn card(&mut self) -> &mut OpenPgpTransaction<'app> {
self.oc &mut self.state.tx.state.opt
} }
} }
impl Admin<'_, '_> { impl Card<Admin<'_, '_>> {
pub fn set_name(&mut self, name: &str) -> Result<(), Error> { pub fn set_name(&mut self, name: &str) -> Result<(), Error> {
if name.len() >= 40 { if name.len() >= 40 {
return Err(Error::InternalError("name too long".into())); return Err(Error::InternalError("name too long".into()));
@ -516,7 +564,7 @@ impl Admin<'_, '_> {
return Err(Error::InternalError("Invalid char in name".into())); return Err(Error::InternalError("Invalid char in name".into()));
}; };
self.oc.opt.set_name(name.as_bytes()) self.card().set_name(name.as_bytes())
} }
pub fn set_lang(&mut self, lang: &[Lang]) -> Result<(), Error> { pub fn set_lang(&mut self, lang: &[Lang]) -> Result<(), Error> {
@ -524,11 +572,11 @@ impl Admin<'_, '_> {
return Err(Error::InternalError("lang too long".into())); return Err(Error::InternalError("lang too long".into()));
} }
self.oc.opt.set_lang(lang) self.card().set_lang(lang)
} }
pub fn set_sex(&mut self, sex: Sex) -> Result<(), Error> { pub fn set_sex(&mut self, sex: Sex) -> Result<(), Error> {
self.oc.opt.set_sex(sex) self.card().set_sex(sex)
} }
pub fn set_url(&mut self, url: &str) -> Result<(), Error> { pub fn set_url(&mut self, url: &str) -> Result<(), Error> {
@ -537,7 +585,7 @@ impl Admin<'_, '_> {
} }
// Check for max len // Check for max len
let ec = self.oc.extended_capabilities()?; let ec = self.state.tx.extended_capabilities()?;
if ec.max_len_special_do() == None || url.len() <= ec.max_len_special_do().unwrap() as usize if ec.max_len_special_do() == None || url.len() <= ec.max_len_special_do().unwrap() as usize
{ {
@ -545,7 +593,7 @@ impl Admin<'_, '_> {
// or if it's within the acceptable length: // or if it's within the acceptable length:
// send the url update to the card. // send the url update to the card.
self.oc.opt.set_url(url.as_bytes()) self.card().set_url(url.as_bytes())
} else { } else {
Err(Error::InternalError("URL too long".into())) Err(Error::InternalError("URL too long".into()))
} }
@ -553,10 +601,10 @@ impl Admin<'_, '_> {
pub fn set_uif(&mut self, key: KeyType, policy: TouchPolicy) -> Result<(), Error> { pub fn set_uif(&mut self, key: KeyType, policy: TouchPolicy) -> Result<(), Error> {
let uif = match key { let uif = match key {
KeyType::Signing => self.oc.ard.uif_pso_cds()?, KeyType::Signing => self.state.tx.state.ard.uif_pso_cds()?,
KeyType::Decryption => self.oc.ard.uif_pso_dec()?, KeyType::Decryption => self.state.tx.state.ard.uif_pso_dec()?,
KeyType::Authentication => self.oc.ard.uif_pso_aut()?, KeyType::Authentication => self.state.tx.state.ard.uif_pso_aut()?,
KeyType::Attestation => self.oc.ard.uif_attestation()?, KeyType::Attestation => self.state.tx.state.ard.uif_attestation()?,
_ => unimplemented!(), _ => unimplemented!(),
}; };
@ -564,10 +612,10 @@ impl Admin<'_, '_> {
uif.set_touch_policy(policy); uif.set_touch_policy(policy);
match key { match key {
KeyType::Signing => self.oc.opt.set_uif_pso_cds(&uif)?, KeyType::Signing => self.card().set_uif_pso_cds(&uif)?,
KeyType::Decryption => self.oc.opt.set_uif_pso_dec(&uif)?, KeyType::Decryption => self.card().set_uif_pso_dec(&uif)?,
KeyType::Authentication => self.oc.opt.set_uif_pso_aut(&uif)?, KeyType::Authentication => self.card().set_uif_pso_aut(&uif)?,
KeyType::Attestation => self.oc.opt.set_uif_attestation(&uif)?, KeyType::Attestation => self.card().set_uif_attestation(&uif)?,
_ => unimplemented!(), _ => unimplemented!(),
} }
} else { } else {
@ -580,15 +628,15 @@ impl Admin<'_, '_> {
} }
pub fn set_resetting_code(&mut self, pin: &[u8]) -> Result<(), Error> { pub fn set_resetting_code(&mut self, pin: &[u8]) -> Result<(), Error> {
self.oc.opt.set_resetting_code(pin) self.card().set_resetting_code(pin)
} }
pub fn set_pso_enc_dec_key(&mut self, key: &[u8]) -> Result<(), Error> { pub fn set_pso_enc_dec_key(&mut self, key: &[u8]) -> Result<(), Error> {
self.oc.opt.set_pso_enc_dec_key(key) self.card().set_pso_enc_dec_key(key)
} }
pub fn reset_user_pin(&mut self, new: &[u8]) -> Result<(), Error> { pub fn reset_user_pin(&mut self, new: &[u8]) -> Result<(), Error> {
self.oc.opt.reset_retry_counter_pw1(new, None) self.card().reset_retry_counter_pw1(new, None)
} }
/// Upload a ValidErasedKeyAmalgamation to the card as a specific KeyType. /// Upload a ValidErasedKeyAmalgamation to the card as a specific KeyType.
@ -601,7 +649,7 @@ impl Admin<'_, '_> {
password: Option<String>, password: Option<String>,
) -> Result<(), Error> { ) -> Result<(), Error> {
let key = vka_as_uploadable_key(vka, password); let key = vka_as_uploadable_key(vka, password);
self.oc.opt.key_import(key, key_type) self.card().key_import(key, key_type)
} }
/// Wrapper fn for `public_to_fingerprint` that uses SHA256/AES128 as default parameters. /// Wrapper fn for `public_to_fingerprint` that uses SHA256/AES128 as default parameters.
@ -628,8 +676,8 @@ impl Admin<'_, '_> {
algo: Option<AlgoSimple>, algo: Option<AlgoSimple>,
) -> Result<(PublicKeyMaterial, KeyGenerationTime), Error> { ) -> Result<(PublicKeyMaterial, KeyGenerationTime), Error> {
match algo { match algo {
Some(algo) => self.oc.opt.generate_key_simple(Self::ptf, key_type, algo), Some(algo) => self.card().generate_key_simple(Self::ptf, key_type, algo),
None => self.oc.opt.generate_key(Self::ptf, key_type, None), None => self.card().generate_key(Self::ptf, key_type, None),
} }
} }
} }

View file

@ -13,14 +13,14 @@
//! //!
//! ```no_run //! ```no_run
//! use openpgp_card_pcsc::PcscBackend; //! use openpgp_card_pcsc::PcscBackend;
//! use openpgp_card_sequoia::card::Card; //! use openpgp_card_sequoia::card::{Card, Open};
//! //!
//! # fn main() -> Result<(), Box<dyn std::error::Error>> { //! # fn main() -> Result<(), Box<dyn std::error::Error>> {
//! for backend in PcscBackend::cards(None)? { //! for backend in PcscBackend::cards(None)? {
//! let mut card = Card::new(backend); //! let mut card: Card<Open> = backend.into();
//! let mut open = card.transaction()?; //! let mut transaction = card.transaction()?;
//! println!("Found OpenPGP card with ident '{}'", //! println!("Found OpenPGP card with ident '{}'",
//! open.application_identifier()?.ident()); //! transaction.application_identifier()?.ident());
//! } //! }
//! # Ok(()) //! # Ok(())
//! # } //! # }
@ -30,12 +30,12 @@
//! //!
//! ```no_run //! ```no_run
//! use openpgp_card_pcsc::PcscBackend; //! use openpgp_card_pcsc::PcscBackend;
//! use openpgp_card_sequoia::card::Card; //! use openpgp_card_sequoia::card::{Card, Open};
//! //!
//! # fn main() -> Result<(), Box<dyn std::error::Error>> { //! # fn main() -> Result<(), Box<dyn std::error::Error>> {
//! let backend = PcscBackend::open_by_ident("abcd:12345678", None)?; //! let backend = PcscBackend::open_by_ident("abcd:12345678", None)?;
//! let mut card = Card::new(backend); //! let mut card: Card<Open> = backend.into();
//! let mut open = card.transaction()?; //! let mut transaction = card.transaction()?;
//! # Ok(()) //! # Ok(())
//! # } //! # }
//! ``` //! ```
@ -51,18 +51,18 @@
//! //!
//! ```no_run //! ```no_run
//! use openpgp_card_pcsc::PcscBackend; //! use openpgp_card_pcsc::PcscBackend;
//! use openpgp_card_sequoia::card::Card; //! use openpgp_card_sequoia::card::{Card, Open};
//! //!
//! # fn main() -> Result<(), Box<dyn std::error::Error>> { //! # fn main() -> Result<(), Box<dyn std::error::Error>> {
//! // Open card via PCSC //! // Open card via PCSC
//! use sequoia_openpgp::policy::StandardPolicy; //! use sequoia_openpgp::policy::StandardPolicy;
//! let backend = PcscBackend::open_by_ident("abcd:12345678", None)?; //! let backend = PcscBackend::open_by_ident("abcd:12345678", None)?;
//! let mut card = Card::new(backend); //! let mut card: Card<Open> = backend.into();
//! let mut open = card.transaction()?; //! let mut transaction = card.transaction()?;
//! //!
//! // Get authorization for user access to the card with password //! // Get authorization for user access to the card with password
//! open.verify_user(b"123456")?; //! transaction.verify_user(b"123456")?;
//! let mut user = open.user_card().expect("This should not fail"); //! let mut user = transaction.user_card().expect("This should not fail");
//! //!
//! // Get decryptor //! // Get decryptor
//! let decryptor = user.decryptor(&|| { println!("Touch confirmation needed for decryption") }); //! let decryptor = user.decryptor(&|| { println!("Touch confirmation needed for decryption") });
@ -88,17 +88,17 @@
//! //!
//! ```no_run //! ```no_run
//! use openpgp_card_pcsc::PcscBackend; //! use openpgp_card_pcsc::PcscBackend;
//! use openpgp_card_sequoia::card::Card; //! use openpgp_card_sequoia::card::{Card, Open};
//! //!
//! # fn main() -> Result<(), Box<dyn std::error::Error>> { //! # fn main() -> Result<(), Box<dyn std::error::Error>> {
//! // Open card via PCSC //! // Open card via PCSC
//! let backend = PcscBackend::open_by_ident("abcd:12345678", None)?; //! let backend = PcscBackend::open_by_ident("abcd:12345678", None)?;
//! let mut card = Card::new(backend); //! let mut card: Card<Open> = backend.into();
//! let mut open = card.transaction()?; //! let mut transaction = card.transaction()?;
//! //!
//! // Get authorization for signing access to the card with password //! // Get authorization for signing access to the card with password
//! open.verify_user_for_signing(b"123456")?; //! transaction.verify_user_for_signing(b"123456")?;
//! let mut user = open.signing_card().expect("This should not fail"); //! let mut user = transaction.signing_card().expect("This should not fail");
//! //!
//! // Get signer //! // Get signer
//! let signer = user.signer(&|| println!("Touch confirmation needed for signing")); //! let signer = user.signer(&|| println!("Touch confirmation needed for signing"));
@ -114,17 +114,17 @@
//! //!
//! ```no_run //! ```no_run
//! use openpgp_card_pcsc::PcscBackend; //! use openpgp_card_pcsc::PcscBackend;
//! use openpgp_card_sequoia::card::Card; //! use openpgp_card_sequoia::card::{Card, Open};
//! //!
//! # fn main() -> Result<(), Box<dyn std::error::Error>> { //! # fn main() -> Result<(), Box<dyn std::error::Error>> {
//! // Open card via PCSC //! // Open card via PCSC
//! let backend = PcscBackend::open_by_ident("abcd:12345678", None)?; //! let backend = PcscBackend::open_by_ident("abcd:12345678", None)?;
//! let mut card = Card::new(backend); //! let mut card: Card<Open> = backend.into();
//! let mut open = card.transaction()?; //! let mut transaction = card.transaction()?;
//! //!
//! // Get authorization for admin access to the card with password //! // Get authorization for admin access to the card with password
//! open.verify_admin(b"12345678")?; //! transaction.verify_admin(b"12345678")?;
//! let mut admin = open.admin_card().expect("This should not fail"); //! let mut admin = transaction.admin_card().expect("This should not fail");
//! //!
//! // Set the Name and URL fields on the card //! // Set the Name and URL fields on the card
//! admin.set_name("Bar<<Foo")?; //! admin.set_name("Bar<<Foo")?;

View file

@ -32,7 +32,7 @@ use openpgp_card::card_do::{Fingerprint, KeyGenerationTime};
use openpgp_card::crypto_data::{CardUploadableKey, PublicKeyMaterial}; use openpgp_card::crypto_data::{CardUploadableKey, PublicKeyMaterial};
use openpgp_card::{Error, KeyType}; use openpgp_card::{Error, KeyType};
use crate::card::Open; use crate::card::{Card, Transaction};
use crate::decryptor::CardDecryptor; use crate::decryptor::CardDecryptor;
use crate::privkey::SequoiaKey; use crate::privkey::SequoiaKey;
use crate::signer::CardSigner; use crate::signer::CardSigner;
@ -48,7 +48,7 @@ use crate::PublicKey;
/// FIXME: accept optional metadata for user_id(s)? /// FIXME: accept optional metadata for user_id(s)?
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
pub fn make_cert<'app>( pub fn make_cert<'app>(
open: &mut Open<'app>, open: &mut Card<Transaction<'app>>,
key_sig: PublicKey, key_sig: PublicKey,
key_dec: Option<PublicKey>, key_dec: Option<PublicKey>,
key_aut: Option<PublicKey>, key_aut: Option<PublicKey>,
@ -226,7 +226,7 @@ pub fn public_key_material_and_fp_to_key(
} }
/// Get a PublicKey representation for a key slot on the card /// Get a PublicKey representation for a key slot on the card
pub fn key_slot(open: &mut Open, kt: KeyType) -> Result<Option<PublicKey>, Error> { pub fn key_slot(open: &mut Card<Transaction>, kt: KeyType) -> Result<Option<PublicKey>, Error> {
// FIXME: only read these once, if multiple subkeys are retrieved from the card // FIXME: only read these once, if multiple subkeys are retrieved from the card
let times = open.key_generation_times()?; let times = open.key_generation_times()?;
let fps = open.fingerprints()?; let fps = open.fingerprints()?;