From d55985807c712590ca57cfefe80980db39d0c8ca Mon Sep 17 00:00:00 2001 From: Heiko Schaefer Date: Thu, 11 Nov 2021 15:28:44 +0100 Subject: [PATCH] Change the API for interactions between openpgp-card and backends. The goal of this change is a cleaner structure, and in particular to make it the default for client-code to obtain a CardApp with pre-initialized "capabilities" (that is, init_caps() gets called implicitely). --- card-functionality/src/cards.rs | 9 +- card-functionality/src/list-cards.rs | 3 +- openpgp-card-examples/src/bin/decrypt.rs | 2 +- openpgp-card-examples/src/bin/detach-sign.rs | 2 +- openpgp-card-sequoia/src/main.rs | 9 +- openpgp-card/src/apdu.rs | 6 +- openpgp-card/src/card_app.rs | 123 +++++++++---------- openpgp-card/src/keys.rs | 9 +- openpgp-card/src/lib.rs | 17 ++- pcsc/src/lib.rs | 74 +++++------ scdc/src/lib.rs | 6 +- tools/src/bin/opgpcard-pin/main.rs | 16 ++- tools/src/bin/opgpcard/main.rs | 38 +++--- tools/src/bin/opgpcard/util.rs | 6 +- 14 files changed, 147 insertions(+), 173 deletions(-) diff --git a/card-functionality/src/cards.rs b/card-functionality/src/cards.rs index 4f6c0a8..c1b8f90 100644 --- a/card-functionality/src/cards.rs +++ b/card-functionality/src/cards.rs @@ -97,16 +97,12 @@ impl TestCard { let res = ScdClient::shutdown_scd(None); log::trace!(" Attempt to shutdown scd: {:?}", res); - for card_client in PcscClient::cards()? { - let mut ca = CardApp::from(card_client); - + for mut ca in PcscClient::cards()? { // Set Card Capabilities (chaining, command length, ..) let ard = ca.get_application_related_data()?; let app_id = ard.get_application_id()?; if app_id.ident().as_str() == ident.to_uppercase() { - ca.init_caps(&ard)?; - // println!("opened pcsc card {}", ident); return Ok(ca); @@ -116,8 +112,7 @@ impl TestCard { Err(anyhow!("Pcsc card {} not found", ident)) } Self::Scdc(serial) => { - let card_client = ScdClient::open_by_serial(None, serial)?; - let mut ca = CardApp::from(card_client); + let mut ca = ScdClient::open_by_serial(None, serial)?; // Set Card Capabilities (chaining, command length, ..) let ard = ca.get_application_related_data()?; diff --git a/card-functionality/src/list-cards.rs b/card-functionality/src/list-cards.rs index 3ce115c..0ff1477 100644 --- a/card-functionality/src/list-cards.rs +++ b/card-functionality/src/list-cards.rs @@ -9,8 +9,7 @@ use openpgp_card_sequoia::card::Open; fn main() -> Result<()> { println!("The following OpenPGP cards are connected to your system:"); - for ccb in PcscClient::cards()? { - let mut ca = ccb.into(); + for mut ca in PcscClient::cards()? { let open = Open::open(&mut ca)?; println!(" {}", open.application_identifier()?.ident()); } diff --git a/openpgp-card-examples/src/bin/decrypt.rs b/openpgp-card-examples/src/bin/decrypt.rs index e938563..ab16cab 100644 --- a/openpgp-card-examples/src/bin/decrypt.rs +++ b/openpgp-card-examples/src/bin/decrypt.rs @@ -22,7 +22,7 @@ fn main() -> Result<(), Box> { let pin_file = &args[1]; let cert_file = &args[2]; - let mut ca = PcscClient::open_by_ident(&card_ident)?.into(); + let mut ca = PcscClient::open_by_ident(card_ident)?; let mut open = Open::open(&mut ca)?; let pin = std::fs::read_to_string(pin_file)?; diff --git a/openpgp-card-examples/src/bin/detach-sign.rs b/openpgp-card-examples/src/bin/detach-sign.rs index f26981e..1ac0bd3 100644 --- a/openpgp-card-examples/src/bin/detach-sign.rs +++ b/openpgp-card-examples/src/bin/detach-sign.rs @@ -23,7 +23,7 @@ fn main() -> Result<(), Box> { let pin_file = &args[1]; let cert_file = &args[2]; - let mut ca = PcscClient::open_by_ident(&card_ident)?.into(); + let mut ca = PcscClient::open_by_ident(card_ident)?; let mut open = Open::open(&mut ca)?; let pin = std::fs::read_to_string(pin_file)?; diff --git a/openpgp-card-sequoia/src/main.rs b/openpgp-card-sequoia/src/main.rs index 1242f9e..43b6703 100644 --- a/openpgp-card-sequoia/src/main.rs +++ b/openpgp-card-sequoia/src/main.rs @@ -35,7 +35,7 @@ fn main() -> Result<(), Box> { let test_card_ident = env::var("TEST_CARD_IDENT"); if let Ok(test_card_ident) = test_card_ident { - let mut card = PcscClient::open_by_ident(&test_card_ident)?.into(); + let mut card = PcscClient::open_by_ident(&test_card_ident)?; let mut open = Open::open(&mut card)?; // card metadata @@ -150,7 +150,7 @@ fn main() -> Result<(), Box> { // ----------------------------- // Open fresh Card for decrypt // ----------------------------- - let mut card = PcscClient::open_by_ident(&test_card_ident)?.into(); + let mut card = PcscClient::open_by_ident(&test_card_ident)?; let mut open = Open::open(&mut card)?; // Check that we're still using the expected card @@ -189,7 +189,7 @@ fn main() -> Result<(), Box> { // ----------------------------- // Open fresh Card for signing // ----------------------------- - let mut card = PcscClient::open_by_ident(&test_card_ident)?.into(); + let mut card = PcscClient::open_by_ident(&test_card_ident)?; let mut open = Open::open(&mut card)?; // Sign @@ -219,8 +219,7 @@ fn main() -> Result<(), Box> { println!("The following OpenPGP cards are connected to your system:"); - for card in PcscClient::cards()? { - let mut card = card.into(); + for mut card in PcscClient::cards()? { let open = Open::open(&mut card)?; println!(" {}", open.application_identifier()?.ident()); } diff --git a/openpgp-card/src/apdu.rs b/openpgp-card/src/apdu.rs index 1180b6a..5d66f7a 100644 --- a/openpgp-card/src/apdu.rs +++ b/openpgp-card/src/apdu.rs @@ -13,7 +13,7 @@ use std::convert::TryFrom; use crate::apdu::command::Expect; use crate::apdu::{command::Command, response::RawResponse}; -use crate::{CardClientBox, Error, StatusBytes}; +use crate::{CardClient, Error, StatusBytes}; /// "Maximum amount of bytes in a short APDU command or response" (from pcsc) const MAX_BUFFER_SIZE: usize = 264; @@ -23,7 +23,7 @@ const MAX_BUFFER_SIZE: usize = 264; /// If the reply is truncated, this fn assembles all the parts and returns /// them as one aggregated Response. pub(crate) fn send_command( - card_client: &mut CardClientBox, + card_client: &mut dyn CardClient, cmd: Command, expect_reply: bool, ) -> Result { @@ -82,7 +82,7 @@ pub(crate) fn send_command( /// If the response is chained, this fn only returns one chunk, the caller /// needs to re-assemble the chained response-parts. fn send_command_low_level( - card_client: &mut CardClientBox, + card_client: &mut dyn CardClient, cmd: Command, expect_response: Expect, ) -> Result, Error> { diff --git a/openpgp-card/src/card_app.rs b/openpgp-card/src/card_app.rs index a5713bd..1e665ed 100644 --- a/openpgp-card/src/card_app.rs +++ b/openpgp-card/src/card_app.rs @@ -3,7 +3,6 @@ //! CardApp exposes functionality of the "OpenPGP card" application. -use std::borrow::BorrowMut; use std::convert::TryFrom; use std::convert::TryInto; @@ -19,7 +18,7 @@ use crate::crypto_data::{ CardUploadableKey, Cryptogram, Hash, PublicKeyMaterial, }; use crate::tlv::{tag::Tag, value::Value, Tlv}; -use crate::{apdu, keys, CardCaps, CardClientBox, KeyType}; +use crate::{apdu, keys, CardCaps, CardClient, CardClientBox, KeyType}; use crate::{Error, StatusBytes}; /// Low-level access to OpenPGP card functionality. @@ -33,22 +32,26 @@ pub struct CardApp { card_client: CardClientBox, } -impl From for CardApp { - fn from(card_client: CardClientBox) -> Self { - Self { card_client } - } -} - -impl From for CardClientBox { - fn from(card_app: CardApp) -> CardClientBox { - card_app.card_client - } -} - impl CardApp { + /// Get a CardApp based on a CardClient. + /// + /// It is expected that SELECT has already been performed on the card. + /// + /// This fn calls CardClient::init_caps(). It should probably only be used + /// by backend implementations, not by user code. User Code should get + /// a fully initialized CardApp from their backend implementation. + pub fn initialize(card_client: CardClientBox) -> Result { + let mut ca = Self { card_client }; + + let ard = ca.get_application_related_data()?; + ca.init_caps(&ard)?; + + Ok(ca) + } + /// Get the CardClient for this CardApp - pub(crate) fn get_card_client(&mut self) -> &mut CardClientBox { - &mut self.card_client + pub(crate) fn card_client(&mut self) -> &mut dyn CardClient { + &mut *self.card_client } /// Initialize the CardCaps settings in the underlying CardClient @@ -95,15 +98,6 @@ impl CardApp { Ok(()) } - // --- select --- - - /// Select the OpenPGP card application - pub fn select(&mut self) -> Result { - let select_openpgp = commands::select_openpgp(); - apdu::send_command(&mut self.card_client, select_openpgp, false)? - .try_into() - } - // --- get data --- /// Get the "application related data" from the card. @@ -115,7 +109,7 @@ impl CardApp { &mut self, ) -> Result { let ad = commands::get_application_data(); - let resp = apdu::send_command(&mut self.card_client, ad, true)?; + let resp = apdu::send_command(self.card_client(), ad, true)?; let value = Value::from(resp.data()?, true)?; log::debug!(" App data Value: {:x?}", value); @@ -130,7 +124,7 @@ impl CardApp { assert!((1..=4).contains(&num)); let cmd = commands::get_private_do(num); - let resp = apdu::send_command(&mut self.card_client, cmd, true)?; + let resp = apdu::send_command(self.card_client(), cmd, true)?; Ok(resp.data()?.to_vec()) } @@ -166,11 +160,8 @@ impl CardApp { // --- URL (5f50) --- pub fn get_url(&mut self) -> Result { - let resp = apdu::send_command( - &mut self.card_client, - commands::get_url(), - true, - )?; + let resp = + apdu::send_command(self.card_client(), commands::get_url(), true)?; Ok(String::from_utf8_lossy(resp.data()?).to_string()) } @@ -180,7 +171,7 @@ impl CardApp { &mut self, ) -> Result { let crd = commands::cardholder_related_data(); - let resp = apdu::send_command(&mut self.card_client, crd, true)?; + let resp = apdu::send_command(self.card_client(), crd, true)?; resp.check_ok()?; CardholderRelatedData::try_from(resp.data()?) @@ -191,7 +182,7 @@ impl CardApp { &mut self, ) -> Result { let sst = commands::get_security_support_template(); - let resp = apdu::send_command(&mut self.card_client, sst, true)?; + let resp = apdu::send_command(self.card_client(), sst, true)?; resp.check_ok()?; let tlv = Tlv::try_from(resp.data()?)?; @@ -219,13 +210,13 @@ impl CardApp { /// certificate (if the card supports multiple certificates). pub fn get_cardholder_certificate(&mut self) -> Result { let cmd = commands::get_cardholder_certificate(); - apdu::send_command(&mut self.card_client, cmd, true)?.try_into() + apdu::send_command(self.card_client(), cmd, true)?.try_into() } /// DO "Algorithm Information" pub fn get_algo_info(&mut self) -> Result> { let resp = apdu::send_command( - &mut self.card_client, + self.card_client(), commands::get_algo_list(), true, )?; @@ -250,7 +241,7 @@ impl CardApp { let data = tlv.serialize(); let cmd = commands::select_data(num, data); - apdu::send_command(&mut self.card_client, cmd, true)?.try_into() + apdu::send_command(self.card_client(), cmd, true)?.try_into() } // ---------- @@ -268,8 +259,7 @@ impl CardApp { // [apdu 00 20 00 81 08 40 40 40 40 40 40 40 40] for _ in 0..4 { let verify = commands::verify_pw1_81([0x40; 8].to_vec()); - let resp = - apdu::send_command(&mut self.card_client, verify, false)?; + let resp = apdu::send_command(self.card_client(), verify, false)?; if !(resp.status() == StatusBytes::SecurityStatusNotSatisfied || resp.status() == StatusBytes::AuthenticationMethodBlocked || matches!(resp.status(), StatusBytes::PasswordNotChecked(_))) @@ -282,8 +272,7 @@ impl CardApp { // [apdu 00 20 00 83 08 40 40 40 40 40 40 40 40] for _ in 0..4 { let verify = commands::verify_pw3([0x40; 8].to_vec()); - let resp = - apdu::send_command(&mut self.card_client, verify, false)?; + let resp = apdu::send_command(self.card_client(), verify, false)?; if !(resp.status() == StatusBytes::SecurityStatusNotSatisfied || resp.status() == StatusBytes::AuthenticationMethodBlocked @@ -295,12 +284,12 @@ impl CardApp { // terminate_df [apdu 00 e6 00 00] let term = commands::terminate_df(); - let resp = apdu::send_command(&mut self.card_client, term, false)?; + let resp = apdu::send_command(self.card_client(), term, false)?; resp.check_ok()?; // activate_file [apdu 00 44 00 00] let act = commands::activate_file(); - let resp = apdu::send_command(&mut self.card_client, act, false)?; + let resp = apdu::send_command(self.card_client(), act, false)?; resp.check_ok()?; Ok(()) @@ -317,7 +306,7 @@ impl CardApp { pin: &str, ) -> Result { let verify = commands::verify_pw1_81(pin.as_bytes().to_vec()); - apdu::send_command(&mut self.card_client, verify, false)?.try_into() + apdu::send_command(self.card_client(), verify, false)?.try_into() } /// Check the current access of PW1 for signing (mode 81). @@ -328,14 +317,14 @@ impl CardApp { /// e.g. yubikey 5) pub fn check_pw1_for_signing(&mut self) -> Result { let verify = commands::verify_pw1_81(vec![]); - apdu::send_command(&mut self.card_client, verify, false)?.try_into() + apdu::send_command(self.card_client(), verify, false)?.try_into() } /// Verify PW1 (user) and set an appropriate access status. /// (For operations except signing, mode 82). pub fn verify_pw1(&mut self, pin: &str) -> Result { let verify = commands::verify_pw1_82(pin.as_bytes().to_vec()); - apdu::send_command(&mut self.card_client, verify, false)?.try_into() + apdu::send_command(self.card_client(), verify, false)?.try_into() } /// Check the current access of PW1. @@ -347,13 +336,13 @@ impl CardApp { /// e.g. yubikey 5) pub fn check_pw1(&mut self) -> Result { let verify = commands::verify_pw1_82(vec![]); - apdu::send_command(&mut self.card_client, verify, false)?.try_into() + apdu::send_command(self.card_client(), verify, false)?.try_into() } /// Verify PW3 (admin) and set an appropriate access status. pub fn verify_pw3(&mut self, pin: &str) -> Result { let verify = commands::verify_pw3(pin.as_bytes().to_vec()); - apdu::send_command(&mut self.card_client, verify, false)?.try_into() + apdu::send_command(self.card_client(), verify, false)?.try_into() } /// Check the current access of PW3 (admin). @@ -364,7 +353,7 @@ impl CardApp { /// e.g. yubikey 5) pub fn check_pw3(&mut self) -> Result { let verify = commands::verify_pw3(vec![]); - apdu::send_command(&mut self.card_client, verify, false)?.try_into() + apdu::send_command(self.card_client(), verify, false)?.try_into() } /// Change the value of PW1 (user password). @@ -380,7 +369,7 @@ impl CardApp { data.extend(new.as_bytes()); let change = commands::change_pw1(data); - apdu::send_command(&mut self.card_client, change, false)?.try_into() + apdu::send_command(self.card_client(), change, false)?.try_into() } /// Change the value of PW3 (admin password). @@ -396,7 +385,7 @@ impl CardApp { data.extend(new.as_bytes()); let change = commands::change_pw3(data); - apdu::send_command(&mut self.card_client, change, false)?.try_into() + apdu::send_command(self.card_client(), change, false)?.try_into() } /// Reset the error counter for PW1 (user password) and set a new value @@ -412,7 +401,7 @@ impl CardApp { resetting_code: Option>, ) -> Result { let reset = commands::reset_retry_counter_pw1(resetting_code, new_pw1); - apdu::send_command(&mut self.card_client, reset, false)?.try_into() + apdu::send_command(self.card_client(), reset, false)?.try_into() } // --- decrypt --- @@ -460,7 +449,7 @@ impl CardApp { fn pso_decipher(&mut self, data: Vec) -> Result, Error> { // The OpenPGP card is already connected and PW1 82 has been verified let dec_cmd = commands::decryption(data); - let resp = apdu::send_command(&mut self.card_client, dec_cmd, true)?; + let resp = apdu::send_command(self.card_client(), dec_cmd, true)?; resp.check_ok()?; Ok(resp.data().map(|d| d.to_vec())?) @@ -522,7 +511,7 @@ impl CardApp { ) -> Result, Error> { let cds_cmd = commands::signature(data); - let resp = apdu::send_command(&mut self.card_client, cds_cmd, true)?; + let resp = apdu::send_command(self.card_client(), cds_cmd, true)?; Ok(resp.data().map(|d| d.to_vec())?) } @@ -553,7 +542,7 @@ impl CardApp { data: Vec, ) -> Result, Error> { let ia_cmd = commands::internal_authenticate(data); - let resp = apdu::send_command(&mut self.card_client, ia_cmd, true)?; + let resp = apdu::send_command(self.card_client(), ia_cmd, true)?; Ok(resp.data().map(|d| d.to_vec())?) } @@ -571,31 +560,29 @@ impl CardApp { assert!((1..=4).contains(&num)); let cmd = commands::put_private_do(num, data); - let resp = apdu::send_command(&mut self.card_client, cmd, true)?; + let resp = apdu::send_command(self.card_client(), cmd, true)?; Ok(resp.data()?.to_vec()) } pub fn set_name(&mut self, name: &str) -> Result { let put_name = commands::put_name(name.as_bytes().to_vec()); - apdu::send_command(&mut self.card_client, put_name, false)?.try_into() + apdu::send_command(self.card_client(), put_name, false)?.try_into() } pub fn set_lang(&mut self, lang: &str) -> Result { let put_lang = commands::put_lang(lang.as_bytes().to_vec()); - apdu::send_command(self.card_client.borrow_mut(), put_lang, false)? - .try_into() + apdu::send_command(self.card_client(), put_lang, false)?.try_into() } pub fn set_sex(&mut self, sex: Sex) -> Result { let put_sex = commands::put_sex((&sex).into()); - apdu::send_command(self.card_client.borrow_mut(), put_sex, false)? - .try_into() + apdu::send_command(self.card_client(), put_sex, false)?.try_into() } pub fn set_url(&mut self, url: &str) -> Result { let put_url = commands::put_url(url.as_bytes().to_vec()); - apdu::send_command(&mut self.card_client, put_url, false)?.try_into() + apdu::send_command(self.card_client(), put_url, false)?.try_into() } pub fn set_creation_time( @@ -617,7 +604,7 @@ impl CardApp { time_value, ); - apdu::send_command(&mut self.card_client, time_cmd, false)?.try_into() + apdu::send_command(self.card_client(), time_cmd, false)?.try_into() } pub fn set_fingerprint( @@ -630,7 +617,7 @@ impl CardApp { fp.as_bytes().to_vec(), ); - apdu::send_command(self.get_card_client(), fp_cmd, false)?.try_into() + apdu::send_command(self.card_client(), fp_cmd, false)?.try_into() } /// Set PW Status Bytes. @@ -652,7 +639,7 @@ impl CardApp { let data = pw_status.serialize_for_put(long); let cmd = commands::put_pw_status(data); - apdu::send_command(self.get_card_client(), cmd, false)?.try_into() + apdu::send_command(self.card_client(), cmd, false)?.try_into() } /// Set cardholder certificate (for AUT, DEC or SIG). @@ -664,7 +651,7 @@ impl CardApp { data: Vec, ) -> Result { let cmd = commands::put_cardholder_certificate(data); - apdu::send_command(&mut self.card_client, cmd, false)?.try_into() + apdu::send_command(self.card_client(), cmd, false)?.try_into() } /// Set algorithm attributes @@ -680,7 +667,7 @@ impl CardApp { algo.get_data()?, ); - apdu::send_command(&mut self.card_client, cmd, false)?.try_into() + apdu::send_command(self.card_client(), cmd, false)?.try_into() } /// Set resetting code @@ -690,7 +677,7 @@ impl CardApp { resetting_code: Vec, ) -> Result { let cmd = commands::put_data(&[0xd3], resetting_code); - apdu::send_command(&mut self.card_client, cmd, false)?.try_into() + apdu::send_command(self.card_client(), cmd, false)?.try_into() } /// Import an existing private key to the card. diff --git a/openpgp-card/src/keys.rs b/openpgp-card/src/keys.rs index 5247740..980a8d1 100644 --- a/openpgp-card/src/keys.rs +++ b/openpgp-card/src/keys.rs @@ -135,9 +135,7 @@ pub(crate) fn generate_asymmetric_key_pair( let crt = get_crt(key_type)?; let gen_key_cmd = commands::gen_key(crt.serialize().to_vec()); - let card_client = card_app.get_card_client(); - - let resp = apdu::send_command(card_client, gen_key_cmd, true)?; + let resp = apdu::send_command(card_app.card_client(), gen_key_cmd, true)?; resp.check_ok()?; let tlv = Tlv::try_from(resp.data()?)?; @@ -164,7 +162,7 @@ pub(crate) fn get_pub_key( let get_pub_key_cmd = commands::get_pub_key(crt.serialize().to_vec()); let resp = - apdu::send_command(card_app.get_card_client(), get_pub_key_cmd, true)?; + apdu::send_command(card_app.card_client(), get_pub_key_cmd, true)?; resp.check_ok()?; let tlv = Tlv::try_from(resp.data()?)?; @@ -216,8 +214,7 @@ pub(crate) fn key_import( card_app.set_algorithm_attributes(key_type, &algo)?; } - apdu::send_command(card_app.get_card_client(), key_cmd, false)? - .check_ok()?; + apdu::send_command(card_app.card_client(), key_cmd, false)?.check_ok()?; card_app.set_fingerprint(fp, key_type)?; card_app.set_creation_time(key.get_ts(), key_type)?; diff --git a/openpgp-card/src/lib.rs b/openpgp-card/src/lib.rs index afc3aca..ae036b7 100644 --- a/openpgp-card/src/lib.rs +++ b/openpgp-card/src/lib.rs @@ -25,10 +25,8 @@ //! crate offers a higher level wrapper based on the //! [Sequoia PGP](https://sequoia-pgp.org/) implementation. -use anyhow::Result; - pub mod algorithm; -mod apdu; +pub(crate) mod apdu; mod card_app; pub mod card_do; pub mod crypto_data; @@ -40,6 +38,11 @@ pub use crate::apdu::response::Response; pub use crate::card_app::CardApp; pub use crate::errors::{Error, SmartcardError, StatusBytes}; +use anyhow::Result; +use std::convert::TryInto; + +use crate::apdu::commands; + /// The CardClient trait defines communication with an OpenPGP card via a /// backend implementation (e.g. the pcsc backend in the crate /// [openpgp-card-pcsc](https://crates.io/crates/openpgp-card-pcsc)). @@ -75,6 +78,14 @@ pub trait CardClient { /// A boxed CardClient (which is Send+Sync). pub type CardClientBox = Box; +impl dyn CardClient { + /// Select the OpenPGP card application + pub fn select(&mut self) -> Result { + let select_openpgp = commands::select_openpgp(); + apdu::send_command(self, select_openpgp, false)?.try_into() + } +} + /// Configuration of the capabilities of the card. /// /// This configuration is used to determine e.g. if chaining or extended diff --git a/pcsc/src/lib.rs b/pcsc/src/lib.rs index d47f9a0..35f3c0e 100644 --- a/pcsc/src/lib.rs +++ b/pcsc/src/lib.rs @@ -4,9 +4,7 @@ use anyhow::{anyhow, Result}; use pcsc::{Card, Context, Protocols, Scope, ShareMode}; -use openpgp_card::{ - CardApp, CardCaps, CardClient, CardClientBox, Error, SmartcardError, -}; +use openpgp_card::{CardApp, CardCaps, CardClient, Error, SmartcardError}; pub struct PcscClient { card: Card, @@ -21,6 +19,11 @@ impl PcscClient { } } + /// Get an initialized CardApp from a card + fn into_card_app(self) -> Result { + CardApp::initialize(Box::new(self)) + } + /// Opened PCSC Cards without selecting the OpenPGP card application fn pcsc_cards() -> Result, SmartcardError> { let ctx = match Context::establish(Scope::User) { @@ -84,56 +87,43 @@ impl PcscClient { /// Return all cards on which the OpenPGP application could be selected. /// /// Each card is opened and has the OpenPGP application selected. - pub fn cards() -> Result> { - let cards = Self::unopened_cards()? - .into_iter() - .map(Self::select) - .map(|res| res.ok()) - .flatten() - .map(|ca| ca.into()) - .collect(); + /// Cards are initialized via init_caps(). + pub fn cards() -> Result> { + let mut cards = vec![]; + + for mut card in Self::unopened_cards()? { + if Self::select(&mut card).is_ok() { + if let Ok(ca) = card.into_card_app() { + cards.push(ca); + } + } + } Ok(cards) } /// Try to select the OpenPGP application on a card - fn select(card_client: PcscClient) -> Result { - let ccb = Box::new(card_client) as CardClientBox; - - let mut ca = CardApp::from(ccb); - if ca.select().is_ok() { - Ok(ca) + fn select(card_client: &mut PcscClient) -> Result<(), Error> { + if ::select(card_client).is_ok() { + Ok(()) } else { Err(Error::Smartcard(SmartcardError::SelectOpenPGPCardFailed)) } } - /// Get application related data from the card and check if 'ident' - /// matches - fn match_by_ident( - mut ca: CardApp, - ident: &str, - ) -> Result, Error> { - let ard = ca.get_application_related_data()?; - let aid = ard.get_application_id()?; - - if aid.ident() == ident { - Ok(Some(ca.into())) - } else { - Ok(None) - } - } - /// Returns the OpenPGP card that matches `ident`, if it is available. - /// The OpenPGP application of the `CardClientBox` has been selected. - pub fn open_by_ident(ident: &str) -> Result { - for card in Self::unopened_cards()? { - if let Ok(ca) = Self::select(card) { - if let Some(matched_card) = - PcscClient::match_by_ident(ca, ident)? - { - return Ok(matched_card); - } + /// A fully initialized CardApp is returned: application has been + /// selected, init_caps() has been performed. + pub fn open_by_ident(ident: &str) -> Result { + for mut card in Self::unopened_cards()? { + Self::select(&mut card)?; + let mut ca = card.into_card_app()?; + + let ard = ca.get_application_related_data()?; + let aid = ard.get_application_id()?; + + if aid.ident() == ident { + return Ok(ca); } } diff --git a/scdc/src/lib.rs b/scdc/src/lib.rs index 25f62b7..4bff66e 100644 --- a/scdc/src/lib.rs +++ b/scdc/src/lib.rs @@ -13,7 +13,7 @@ use sequoia_ipc::gnupg::{Agent, Context}; use std::sync::Mutex; use tokio::runtime::Runtime; -use openpgp_card::Error; +use openpgp_card::{CardApp, Error}; use openpgp_card::{CardCaps, CardClient, CardClientBox}; lazy_static! { @@ -118,11 +118,11 @@ impl ScdClient { pub fn open_by_serial( agent: Option, serial: &str, - ) -> Result { + ) -> Result { let mut card = ScdClient::new(agent, true)?; card.select_card(serial)?; - Ok(Box::new(card) as CardClientBox) + Ok(CardApp::initialize(Box::new(card))?) } /// Ask scdameon to switch to using a specific OpenPGP card, based on diff --git a/tools/src/bin/opgpcard-pin/main.rs b/tools/src/bin/opgpcard-pin/main.rs index d38a37b..c4770b8 100644 --- a/tools/src/bin/opgpcard-pin/main.rs +++ b/tools/src/bin/opgpcard-pin/main.rs @@ -13,7 +13,7 @@ mod cli; fn main() -> Result<(), Box> { let cli = cli::Cli::from_args(); - let mut card = PcscClient::open_by_ident(&cli.ident)?.into(); + let mut card = PcscClient::open_by_ident(&cli.ident)?; let mut open = Open::open(&mut card)?; match cli.cmd { @@ -145,15 +145,13 @@ fn main() -> Result<(), Box> { let res = if let Some(rst) = rst { // reset to new user pin open.reset_user_pin(&rst, &newpin1) + } else if let Some(mut admin) = open.admin_card() { + admin.reset_user_pin(&newpin1) } else { - if let Some(mut admin) = open.admin_card() { - admin.reset_user_pin(&newpin1) - } else { - return Err(anyhow::anyhow!( - "Failed to use card in admin-mode." - ) - .into()); - } + return Err(anyhow::anyhow!( + "Failed to use card in admin-mode." + ) + .into()); }; if res.is_err() { diff --git a/tools/src/bin/opgpcard/main.rs b/tools/src/bin/opgpcard/main.rs index 4bf37e2..6da2582 100644 --- a/tools/src/bin/opgpcard/main.rs +++ b/tools/src/bin/opgpcard/main.rs @@ -71,7 +71,7 @@ fn main() -> Result<(), Box> { admin_pin, cmd, } => { - let mut card = util::open_card(&ident)?.into(); + let mut card = util::open_card(&ident)?; let mut open = Open::open(&mut card)?; match cmd { @@ -137,8 +137,7 @@ fn list_cards() -> Result<()> { if !cards.is_empty() { println!("Available OpenPGP cards:"); - for card in cards { - let mut card = card.into(); + for mut card in cards { let open = Open::open(&mut card)?; println!(" {}", open.application_identifier()?.ident()); } @@ -149,18 +148,17 @@ fn list_cards() -> Result<()> { } fn print_status(ident: Option, verbose: bool) -> Result<()> { - let ccb = if let Some(ident) = ident { + let mut ca = if let Some(ident) = ident { util::open_card(&ident)? } else { let mut cards = util::cards()?; if cards.len() == 1 { cards.pop().unwrap() } else { - return Err(anyhow::anyhow!("Found {} cards", cards.len()).into()); + return Err(anyhow::anyhow!("Found {} cards", cards.len())); } }; - let mut card = ccb.into(); - let mut open = Open::open(&mut card)?; + let mut open = Open::open(&mut ca)?; print!("OpenPGP card {}", open.application_identifier()?.ident()); @@ -291,10 +289,10 @@ fn decrypt( let input = util::open_or_stdin(input.as_deref())?; - let mut card = util::open_card(&ident)?.into(); + let mut card = util::open_card(ident)?; let mut open = Open::open(&mut card)?; - let mut user = util::get_user(&mut open, &pin_file)?; + let mut user = util::get_user(&mut open, pin_file)?; let d = user.decryptor(&cert, &p)?; let db = DecryptorBuilder::from_reader(input)?; @@ -316,10 +314,10 @@ fn sign_detached( let mut input = util::open_or_stdin(input.as_deref())?; - let mut card = util::open_card(&ident)?.into(); + let mut card = util::open_card(ident)?; let mut open = Open::open(&mut card)?; - let mut sign = util::get_sign(&mut open, &pin_file)?; + let mut sign = util::get_sign(&mut open, pin_file)?; let s = sign.signer(&cert, &p)?; let message = Armorer::new(Message::new(std::io::stdout())).build()?; @@ -333,7 +331,7 @@ fn sign_detached( fn factory_reset(ident: &str) -> Result<()> { println!("Resetting Card {}", ident); - let mut card = util::open_card(ident)?.into(); + let mut card = util::open_card(ident)?; Open::open(&mut card)?.factory_reset() } @@ -341,16 +339,16 @@ fn key_import_yolo(mut admin: Admin, key: &Cert) -> Result<()> { let p = StandardPolicy::new(); let sig = - openpgp_card_sequoia::sq_util::get_subkey(&key, &p, KeyType::Signing)?; + openpgp_card_sequoia::sq_util::get_subkey(key, &p, KeyType::Signing)?; let dec = openpgp_card_sequoia::sq_util::get_subkey( - &key, + key, &p, KeyType::Decryption, )?; let auth = openpgp_card_sequoia::sq_util::get_subkey( - &key, + key, &p, KeyType::Authentication, )?; @@ -382,7 +380,7 @@ fn key_import_explicit( if let Some(sig_fp) = sig_fp { if let Some(sig) = - sq_util::get_subkey_by_fingerprint(&key, &p, &sig_fp)? + sq_util::get_subkey_by_fingerprint(key, &p, &sig_fp)? { println!("Uploading {} as signing key", sig.fingerprint()); admin.upload_key(sig, KeyType::Signing, None)?; @@ -393,7 +391,7 @@ fn key_import_explicit( if let Some(dec_fp) = dec_fp { if let Some(dec) = - sq_util::get_subkey_by_fingerprint(&key, &p, &dec_fp)? + sq_util::get_subkey_by_fingerprint(key, &p, &dec_fp)? { println!("Uploading {} as decryption key", dec.fingerprint()); admin.upload_key(dec, KeyType::Decryption, None)?; @@ -404,7 +402,7 @@ fn key_import_explicit( if let Some(auth_fp) = auth_fp { if let Some(auth) = - sq_util::get_subkey_by_fingerprint(&key, &p, &auth_fp)? + sq_util::get_subkey_by_fingerprint(key, &p, &auth_fp)? { println!("Uploading {} as authentication key", auth.fingerprint()); admin.upload_key(auth, KeyType::Authentication, None)?; @@ -478,7 +476,7 @@ fn generate_keys( // Write armored certificate to the output file (or stdout) let mut output = util::open_or_stdout(output.as_deref())?; - output.write(armored.as_bytes())?; + output.write_all(armored.as_bytes())?; Ok(()) } @@ -519,7 +517,7 @@ fn gen_subkeys( // If none of them work, fail and return all errors for (n, alg) in algos.iter().enumerate() { - let a = Some(alg.clone()); + let a = Some(*alg); log::info!(" Trying key generation with algo {:?}", alg); diff --git a/tools/src/bin/opgpcard/util.rs b/tools/src/bin/opgpcard/util.rs index a4eef2f..7fd45f5 100644 --- a/tools/src/bin/opgpcard/util.rs +++ b/tools/src/bin/opgpcard/util.rs @@ -3,16 +3,16 @@ use anyhow::{Context, Result}; -use openpgp_card::{CardClientBox, Error}; +use openpgp_card::{CardApp, Error}; use openpgp_card_pcsc::PcscClient; use openpgp_card_sequoia::card::{Admin, Open, Sign, User}; use std::path::Path; -pub(crate) fn cards() -> Result> { +pub(crate) fn cards() -> Result> { PcscClient::cards() } -pub(crate) fn open_card(ident: &str) -> Result { +pub(crate) fn open_card(ident: &str) -> Result { PcscClient::open_by_ident(ident) }