From 0e94871189f877ef7acd600f4dc7dfcf31f3dc66 Mon Sep 17 00:00:00 2001 From: Heiko Schaefer Date: Tue, 15 Feb 2022 12:16:32 +0100 Subject: [PATCH] Implement PcscCard::transaction() to replace the transaction!() macro. (This currently requires unreleased pcsc from git) --- card-functionality/Cargo.toml | 2 +- card-functionality/src/list-cards.rs | 4 +- card-functionality/src/tests.rs | 13 +- openpgp-card-examples/Cargo.toml | 1 - openpgp-card-examples/src/bin/decrypt.rs | 4 +- openpgp-card-examples/src/bin/detach-sign.rs | 4 +- openpgp-card-sequoia/Cargo.toml | 1 - openpgp-card-sequoia/src/lib.rs | 20 +- openpgp-card-sequoia/src/main.rs | 10 +- pcsc/Cargo.toml | 2 +- pcsc/src/lib.rs | 215 +++++++++---------- tools/Cargo.toml | 1 - tools/src/bin/opgpcard-pin/main.rs | 4 +- tools/src/bin/opgpcard/main.rs | 15 +- 14 files changed, 141 insertions(+), 155 deletions(-) diff --git a/card-functionality/Cargo.toml b/card-functionality/Cargo.toml index fa56f68..4216cf2 100644 --- a/card-functionality/Cargo.toml +++ b/card-functionality/Cargo.toml @@ -32,7 +32,7 @@ openpgp-card = { path = "../openpgp-card" } openpgp-card-sequoia = { path = "../openpgp-card-sequoia" } openpgp-card-scdc = { path = "../scdc" } openpgp-card-pcsc = { path = "../pcsc" } -pcsc = "2" +pcsc = { git = "https://github.com/bluetech/pcsc-rust/", rev = "ac1feee16d54bd6ba1681a201fc8f25808aa346d" } sequoia-openpgp = "1.3" anyhow = "1" thiserror = "1.0" diff --git a/card-functionality/src/list-cards.rs b/card-functionality/src/list-cards.rs index c6d7e30..d94fdd0 100644 --- a/card-functionality/src/list-cards.rs +++ b/card-functionality/src/list-cards.rs @@ -3,14 +3,14 @@ use anyhow::Result; -use openpgp_card_pcsc::{transaction, PcscCard, TxClient}; +use openpgp_card_pcsc::PcscCard; use openpgp_card_sequoia::card::Open; fn main() -> Result<()> { println!("The following OpenPGP cards are connected to your system:"); for mut card in PcscCard::cards(None)? { - let mut txc: TxClient = transaction!(card)?; + let mut txc = card.transaction()?; let open = Open::new(&mut txc)?; println!(" {}", open.application_identifier()?.ident()); diff --git a/card-functionality/src/tests.rs b/card-functionality/src/tests.rs index f5df3b9..bded3a1 100644 --- a/card-functionality/src/tests.rs +++ b/card-functionality/src/tests.rs @@ -1,7 +1,7 @@ // SPDX-FileCopyrightText: 2021 Heiko Schaefer // SPDX-License-Identifier: MIT OR Apache-2.0 -use anyhow::Result; +use anyhow::{anyhow, Result}; use std::convert::TryFrom; use std::str::FromStr; use std::string::FromUtf8Error; @@ -16,7 +16,6 @@ use openpgp_card; use openpgp_card::algorithm::AlgoSimple; use openpgp_card::card_do::{KeyGenerationTime, Sex}; use openpgp_card::{CardClient, Error, KeyType, StatusBytes}; -use openpgp_card_pcsc::{transaction, TxClient}; use openpgp_card_sequoia::card::Open; use openpgp_card_sequoia::util::{ make_cert, public_key_material_to_key, public_to_fingerprint, @@ -671,19 +670,15 @@ pub fn test_reset_retry_counter( } pub fn run_test( - card: &mut TestCardData, + tc: &mut TestCardData, t: fn( &mut (dyn CardClient + Send + Sync), &[&str], ) -> Result, param: &[&str], ) -> Result { - let mut card_client = card.get_card()?; - - use anyhow::anyhow; - - let mut txc = transaction!(card_client).map_err(|e| anyhow!(e))?; - // let mut txc = TxClient::new(&mut tx, card_caps, reader_caps); + let mut card = tc.get_card()?; + let mut txc = card.transaction().map_err(|e| anyhow!(e))?; t(&mut txc, param) } diff --git a/openpgp-card-examples/Cargo.toml b/openpgp-card-examples/Cargo.toml index 4b53124..670fc31 100644 --- a/openpgp-card-examples/Cargo.toml +++ b/openpgp-card-examples/Cargo.toml @@ -16,7 +16,6 @@ sequoia-openpgp = "1.3" nettle = "7" openpgp-card = { path = "../openpgp-card", version = "0.1" } openpgp-card-pcsc = { path = "../pcsc", version = "0.1" } -pcsc="2" openpgp-card-sequoia = { path = "../openpgp-card-sequoia", version = "0.0.7" } chrono = "0.4" anyhow = "1" diff --git a/openpgp-card-examples/src/bin/decrypt.rs b/openpgp-card-examples/src/bin/decrypt.rs index e26216e..89819f1 100644 --- a/openpgp-card-examples/src/bin/decrypt.rs +++ b/openpgp-card-examples/src/bin/decrypt.rs @@ -1,7 +1,7 @@ // SPDX-FileCopyrightText: 2021 Wiktor Kwapisiewicz // SPDX-License-Identifier: MIT OR Apache-2.0 -use openpgp_card_pcsc::{transaction, PcscCard, TxClient}; +use openpgp_card_pcsc::PcscCard; use openpgp_card_sequoia::card::Open; @@ -23,7 +23,7 @@ fn main() -> Result<(), Box> { let cert_file = &args[2]; let mut card = PcscCard::open_by_ident(card_ident, None)?; - let mut txc = transaction!(card)?; + let mut txc = card.transaction()?; let mut open = Open::new(&mut txc)?; diff --git a/openpgp-card-examples/src/bin/detach-sign.rs b/openpgp-card-examples/src/bin/detach-sign.rs index c4f547a..114f9a9 100644 --- a/openpgp-card-examples/src/bin/detach-sign.rs +++ b/openpgp-card-examples/src/bin/detach-sign.rs @@ -1,7 +1,7 @@ // SPDX-FileCopyrightText: 2021 Wiktor Kwapisiewicz // SPDX-License-Identifier: MIT OR Apache-2.0 -use openpgp_card_pcsc::{transaction, PcscCard, TxClient}; +use openpgp_card_pcsc::PcscCard; use openpgp_card_sequoia::card::Open; @@ -23,7 +23,7 @@ fn main() -> Result<(), Box> { let cert_file = &args[2]; let mut card = PcscCard::open_by_ident(card_ident, None)?; - let mut txc = transaction!(card)?; + let mut txc = card.transaction()?; let mut open = Open::new(&mut txc)?; diff --git a/openpgp-card-sequoia/Cargo.toml b/openpgp-card-sequoia/Cargo.toml index 0ac3ae6..b75df54 100644 --- a/openpgp-card-sequoia/Cargo.toml +++ b/openpgp-card-sequoia/Cargo.toml @@ -16,7 +16,6 @@ sequoia-openpgp = "1.4" nettle = "7" openpgp-card = { path = "../openpgp-card", version = "0.1" } openpgp-card-pcsc = { path = "../pcsc", version = "0.1" } -pcsc = "2" openpgp-card-scdc = { path = "../scdc", version = "0.1" } chrono = "0.4" anyhow = "1" diff --git a/openpgp-card-sequoia/src/lib.rs b/openpgp-card-sequoia/src/lib.rs index 8787bba..7c02bad 100644 --- a/openpgp-card-sequoia/src/lib.rs +++ b/openpgp-card-sequoia/src/lib.rs @@ -13,11 +13,11 @@ //! //! ```no_run //! use openpgp_card_sequoia::card::Open; -//! use openpgp_card_pcsc::{transaction, PcscCard, TxClient}; +//! use openpgp_card_pcsc::PcscCard; //! //! # fn main() -> Result<(), Box> { //! for mut card in PcscCard::cards(None)? { -//! let mut txc = transaction!(card)?; +//! let mut txc = card.transaction()?; //! let open = Open::new(&mut txc)?; //! println!("Found OpenPGP card with ident '{}'", //! open.application_identifier()?.ident()); @@ -30,11 +30,11 @@ //! //! ```no_run //! use openpgp_card_sequoia::card::Open; -//! use openpgp_card_pcsc::{transaction, PcscCard, TxClient}; +//! use openpgp_card_pcsc::PcscCard; //! //! # fn main() -> Result<(), Box> { //! let mut card = PcscCard::open_by_ident("abcd:12345678", None)?; -//! let mut txc = transaction!(card)?; +//! let mut txc = card.transaction()?; //! let mut open = Open::new(&mut txc)?; //! # Ok(()) //! # } @@ -51,13 +51,13 @@ //! //! ```no_run //! use openpgp_card_sequoia::card::Open; -//! use openpgp_card_pcsc::{transaction, PcscCard, TxClient}; +//! use openpgp_card_pcsc::PcscCard; //! //! # fn main() -> Result<(), Box> { //! // Open card via PCSC //! use sequoia_openpgp::policy::StandardPolicy; //! let mut card = PcscCard::open_by_ident("abcd:12345678", None)?; -//! let mut txc = transaction!(card)?; +//! let mut txc = card.transaction()?; //! let mut open = Open::new(&mut txc)?; //! //! // Get authorization for user access to the card with password @@ -93,13 +93,13 @@ //! //! ```no_run //! use openpgp_card_sequoia::card::Open; -//! use openpgp_card_pcsc::{transaction, PcscCard, TxClient}; +//! use openpgp_card_pcsc::PcscCard; //! //! # fn main() -> Result<(), Box> { //! // Open card via PCSC //! use sequoia_openpgp::policy::StandardPolicy; //! let mut card = PcscCard::open_by_ident("abcd:12345678", None)?; -//! let mut txc = transaction!(card)?; +//! let mut txc = card.transaction()?; //! let mut open = Open::new(&mut txc)?; //! //! // Get authorization for signing access to the card with password @@ -125,12 +125,12 @@ //! //! ```no_run //! use openpgp_card_sequoia::card::Open; -//! use openpgp_card_pcsc::{transaction, PcscCard, TxClient}; +//! use openpgp_card_pcsc::PcscCard; //! //! # fn main() -> Result<(), Box> { //! // Open card via PCSC //! let mut card = PcscCard::open_by_ident("abcd:12345678", None)?; -//! let mut txc = transaction!(card)?; +//! let mut txc = card.transaction()?; //! let mut open = Open::new(&mut txc)?; //! //! // Get authorization for admin access to the card with password diff --git a/openpgp-card-sequoia/src/main.rs b/openpgp-card-sequoia/src/main.rs index d4cc26a..558c133 100644 --- a/openpgp-card-sequoia/src/main.rs +++ b/openpgp-card-sequoia/src/main.rs @@ -11,7 +11,7 @@ use sequoia_openpgp::Cert; use openpgp_card::card_do::Sex; use openpgp_card::KeyType; -use openpgp_card_pcsc::{transaction, PcscCard, TxClient}; +use openpgp_card_pcsc::PcscCard; use openpgp_card_sequoia::card::Open; use openpgp_card_sequoia::sq_util; @@ -36,7 +36,7 @@ fn main() -> Result<(), Box> { if let Ok(test_card_ident) = test_card_ident { let mut card = PcscCard::open_by_ident(&test_card_ident, None)?; - let mut txc = transaction!(card)?; + let mut txc = card.transaction()?; let mut open = Open::new(&mut txc)?; @@ -147,7 +147,7 @@ fn main() -> Result<(), Box> { // Open fresh Card for decrypt // ----------------------------- let mut card = PcscCard::open_by_ident(&test_card_ident, None)?; - let mut txc = transaction!(card)?; + let mut txc = card.transaction()?; let mut open = Open::new(&mut txc)?; @@ -188,7 +188,7 @@ fn main() -> Result<(), Box> { // Open fresh Card for signing // ----------------------------- let mut card = PcscCard::open_by_ident(&test_card_ident, None)?; - let mut txc = transaction!(card)?; + let mut txc = card.transaction()?; let mut open = Open::new(&mut txc)?; @@ -220,7 +220,7 @@ fn main() -> Result<(), Box> { println!("The following OpenPGP cards are connected to your system:"); for mut card in PcscCard::cards(None)? { - let mut txc = transaction!(card)?; + let mut txc = card.transaction()?; let open = Open::new(&mut txc)?; println!(" {}", open.application_identifier()?.ident()); diff --git a/pcsc/Cargo.toml b/pcsc/Cargo.toml index 91b777a..bd8e991 100644 --- a/pcsc/Cargo.toml +++ b/pcsc/Cargo.toml @@ -14,6 +14,6 @@ documentation = "https://docs.rs/crate/openpgp-card-pcsc" [dependencies] openpgp-card = { path = "../openpgp-card", version = "0.1" } iso7816-tlv = "0.4" -pcsc = "2" +pcsc = { git = "https://github.com/bluetech/pcsc-rust/", rev = "ac1feee16d54bd6ba1681a201fc8f25808aa346d" } anyhow = "1" log = "0.4" diff --git a/pcsc/src/lib.rs b/pcsc/src/lib.rs index eb9d8ed..91e5dec 100644 --- a/pcsc/src/lib.rs +++ b/pcsc/src/lib.rs @@ -16,101 +16,6 @@ use openpgp_card::{CardCaps, CardClient, Error, SmartcardError}; const FEATURE_VERIFY_PIN_DIRECT: u8 = 0x06; const FEATURE_MODIFY_PIN_DIRECT: u8 = 0x07; -/// Get a TxClient from a PcscCard (this starts a transaction on the card -/// in PcscCard) -#[macro_export] -macro_rules! transaction { - ( $card:expr, $reselect:expr ) => {{ - use openpgp_card::{Error, SmartcardError}; - use pcsc::{Disposition, Protocols}; - - let mut was_reset = false; - - let card_caps = $card.card_caps(); - let reader_caps = $card.reader_caps().clone(); - let mode = $card.mode(); - - let c = $card.card(); - - loop { - let res = c.transaction(); - - match res { - Ok(mut tx) => { - // A transaction has been successfully started - - if was_reset { - log::debug!( - "start_tx: card was reset, select() openpgp" - ); - - let mut txc = - TxClient::new(tx, card_caps, reader_caps.clone()); - - // In contexts where the caller of this macro - // expects that the card has already been opened, - // re-open the card here. - // For initial card-opening, we don't do this, then - // the caller always expects a card that has not - // been "select"ed yet. - if $reselect { - match TxClient::select(&mut txc) { - Ok(_) => {} - Err(err) => break Err(err), - } - } - - tx = txc.tx(); - } - - let txc = TxClient::new(tx, card_caps, reader_caps); - - break Ok(txc); - } - Err(pcsc::Error::ResetCard) => { - // Card was reset, need to reconnect - was_reset = true; - - drop(res); - - log::debug!("start_tx: do reconnect"); - - { - match c.reconnect( - mode, - Protocols::ANY, - Disposition::ResetCard, - ) { - Ok(_) => {} - Err(err) => { - break Err(Error::Smartcard( - SmartcardError::Error(format!( - "Reconnect failed: {:?}", - err - )), - )) - } - } - } - - log::debug!("start_tx: reconnected."); - - // -> try opening a transaction again - } - Err(e) => { - log::debug!("start_tx: error {:?}", e); - break Err(Error::Smartcard(SmartcardError::Error( - format!("Error: {:?}", e), - ))); - } - }; - } - }}; - ( $card:expr ) => { - transaction!($card, true) - }; -} - fn default_mode(mode: Option) -> ShareMode { if let Some(mode) = mode { mode @@ -124,7 +29,7 @@ fn default_mode(mode: Option) -> ShareMode { /// /// This struct can be used to hold on to a Card, even while no operations /// are performed on the Card. To perform operations on the card, a -/// `TxClient` object needs to be obtained (via the `get_txc!()` macro). +/// `TxClient` object needs to be obtained (via PcscCard::transaction()). pub struct PcscCard { card: Card, mode: ShareMode, @@ -136,8 +41,8 @@ pub struct PcscCard { /// middleware to access the OpenPGP card application on smart cards, via a /// PCSC "transaction". /// -/// This struct is created from a PcscCard by opening a transaction, using the -/// `start_tx!` macro. +/// This struct is created from a PcscCard by opening a transaction, using +/// PcscCard::transaction(). /// /// Transactions on a card cannot be opened and left idle /// (e.g. Microsoft documents that on Windows, they will be closed after @@ -150,15 +55,95 @@ pub struct TxClient<'b> { } impl<'b> TxClient<'b> { - pub fn new( - tx: Transaction<'b>, - card_caps: Option, - reader_caps: HashMap, - ) -> Self { - TxClient { - tx, - card_caps, - reader_caps, + /// Start a transaction on `card`. + /// + /// `reselect` set to `false` is only used internally in this crate, + /// during initial setup of cards. Otherwise it must be `true`, to + /// cause a select() call on cards that have been reset. + fn new(card: &'b mut PcscCard, reselect: bool) -> Result { + use pcsc::Disposition; + + let mut was_reset = false; + + let card_caps = card.card_caps(); + let reader_caps = card.reader_caps().clone(); + let mode = card.mode(); + + let mut c = card.card(); + + loop { + match c.transaction2() { + Ok(mut tx) => { + // A transaction has been successfully started + + if was_reset { + log::debug!( + "start_tx: card was reset, select() openpgp" + ); + + let mut txc = Self { + tx, + card_caps, + reader_caps: reader_caps.clone(), + }; + + // In contexts where the caller of this fn + // expects that the card has already been opened, + // re-open the card here. + // For initial card-opening, we don't do this, then + // the caller always expects a card that has not + // been "select"ed yet. + if reselect { + TxClient::select(&mut txc)?; + } + + tx = txc.tx(); + } + + let txc = Self { + tx, + card_caps, + reader_caps: reader_caps.clone(), + }; + + break Ok(txc); + } + Err((c_, pcsc::Error::ResetCard)) => { + // Card was reset, need to reconnect + was_reset = true; + + // drop(res); + + c = c_; + + log::debug!("start_tx: do reconnect"); + + { + c.reconnect( + mode, + Protocols::ANY, + Disposition::ResetCard, + ) + .map_err(|e| { + Error::Smartcard(SmartcardError::Error(format!( + "Reconnect failed: {:?}", + e + ))) + })?; + } + + log::debug!("start_tx: reconnected."); + + // -> try opening a transaction again + } + Err((_, e)) => { + log::debug!("start_tx: error {:?}", e); + break Err(Error::Smartcard(SmartcardError::Error( + format!("Error: {:?}", e), + )) + .into()); + } + }; } } @@ -472,6 +457,11 @@ impl PcscCard { self.mode } + /// Get a TxClient for this PcscCard (this starts a transaction) + pub fn transaction(&mut self) -> Result { + TxClient::new(self, true) + } + /// A list of "raw" opened PCSC Cards (without selecting the OpenPGP card /// application) fn raw_pcsc_cards(mode: ShareMode) -> Result, SmartcardError> { @@ -563,7 +553,7 @@ impl PcscCard { // start transaction log::debug!("1"); let mut p = PcscCard::new(card, mode); - let mut txc: TxClient = transaction!(p, false)?; + let mut txc = TxClient::new(&mut p, false)?; log::debug!("3"); { @@ -588,7 +578,12 @@ impl PcscCard { ))) })?; log::debug!("4b card status: {:x?}", stat); - txc = TxClient::new(tx, None, HashMap::default()); + txc = TxClient { + tx, + card_caps: None, + reader_caps: HashMap::default(), + }; + // -- /debug: status -- if let Some(ident) = ident { @@ -689,7 +684,7 @@ impl PcscCard { let mut h: HashMap = HashMap::default(); - let mut txc: TxClient = transaction!(self, true)?; + let mut txc = self.transaction()?; // Get Features from reader (pinpad verify/modify) if let Ok(feat) = txc.features() { diff --git a/tools/Cargo.toml b/tools/Cargo.toml index 4d9b534..5d7efe6 100644 --- a/tools/Cargo.toml +++ b/tools/Cargo.toml @@ -16,7 +16,6 @@ sequoia-openpgp = "1.3" nettle = "7" openpgp-card = { path = "../openpgp-card", version = "0.1" } openpgp-card-pcsc = { path = "../pcsc", version = "0.1" } -pcsc = "2" openpgp-card-sequoia = { path = "../openpgp-card-sequoia", version = "0.0.7" } sshkeys = "0.3" rpassword = "5" diff --git a/tools/src/bin/opgpcard-pin/main.rs b/tools/src/bin/opgpcard-pin/main.rs index 1b3ce01..8a6895b 100644 --- a/tools/src/bin/opgpcard-pin/main.rs +++ b/tools/src/bin/opgpcard-pin/main.rs @@ -5,7 +5,7 @@ use anyhow::Result; use structopt::StructOpt; use openpgp_card::{CardClient, Error, StatusBytes}; -use openpgp_card_pcsc::{transaction, PcscCard, TxClient}; +use openpgp_card_pcsc::PcscCard; use openpgp_card_sequoia::card::Open; mod cli; @@ -16,7 +16,7 @@ fn main() -> Result<(), Box> { let cli = cli::Cli::from_args(); let mut card = PcscCard::open_by_ident(&cli.ident, None)?; - let mut txc = transaction!(card)?; + let mut txc = card.transaction()?; let pinpad_verify = txc.feature_pinpad_verify(); let pinpad_modify = txc.feature_pinpad_modify(); diff --git a/tools/src/bin/opgpcard/main.rs b/tools/src/bin/opgpcard/main.rs index 955a790..e45ea6f 100644 --- a/tools/src/bin/opgpcard/main.rs +++ b/tools/src/bin/opgpcard/main.rs @@ -13,7 +13,6 @@ use sequoia_openpgp::Cert; use openpgp_card::algorithm::AlgoSimple; use openpgp_card::{card_do::Sex, CardClient, KeyType}; -use openpgp_card_pcsc::{transaction, TxClient}; use openpgp_card_sequoia::card::{Admin, Open}; use openpgp_card_sequoia::util::{make_cert, public_key_material_to_key}; @@ -72,7 +71,7 @@ fn main() -> Result<(), Box> { cmd, } => { let mut card = util::open_card(&ident)?; - let mut txc = transaction!(card)?; + let mut txc = card.transaction()?; let mut open = Open::new(&mut txc)?; @@ -139,7 +138,7 @@ fn list_cards() -> Result<()> { println!("Available OpenPGP cards:"); for mut card in cards { - let mut txc = transaction!(card)?; + let mut txc = card.transaction()?; let open = Open::new(&mut txc)?; println!(" {}", open.application_identifier()?.ident()); @@ -155,7 +154,7 @@ fn set_identity( id: u8, ) -> Result<(), Box> { let mut card = util::open_card(ident)?; - let mut txc = transaction!(card)?; + let mut txc = card.transaction()?; ::set_identity(&mut txc, id)?; @@ -174,7 +173,7 @@ fn print_status(ident: Option, verbose: bool) -> Result<()> { } }; - let mut txc = transaction!(card)?; + let mut txc = card.transaction()?; let mut open = Open::new(&mut txc)?; @@ -338,7 +337,7 @@ fn decrypt( let input = util::open_or_stdin(input.as_deref())?; let mut card = util::open_card(ident)?; - let mut txc = transaction!(card)?; + let mut txc = card.transaction()?; let mut open = Open::new(&mut txc)?; @@ -364,7 +363,7 @@ fn sign_detached( let mut input = util::open_or_stdin(input.as_deref())?; let mut card = util::open_card(ident)?; - let mut txc = transaction!(card)?; + let mut txc = card.transaction()?; let mut open = Open::new(&mut txc)?; @@ -383,7 +382,7 @@ fn sign_detached( fn factory_reset(ident: &str) -> Result<()> { println!("Resetting Card {}", ident); let mut card = util::open_card(ident)?; - let mut txc = transaction!(card)?; + let mut txc = card.transaction()?; Open::new(&mut txc)?.factory_reset() }