Implement TxClient to use pcsc with transactions (transaction opening code is duplicated)

This commit is contained in:
Heiko Schaefer 2022-01-01 17:52:49 +01:00
parent 0baf36df67
commit 748c334403
No known key found for this signature in database
GPG key ID: 4A849A1904CCBD7D
2 changed files with 253 additions and 67 deletions

View file

@ -42,6 +42,10 @@ use anyhow::Result;
use std::convert::TryInto;
use crate::apdu::commands;
use crate::card_do::ApplicationRelatedData;
use crate::tlv::tag::Tag;
use crate::tlv::value::Value;
use crate::tlv::Tlv;
/// The CardClient trait defines communication with an OpenPGP card via a
/// backend implementation (e.g. the pcsc backend in the crate
@ -94,12 +98,25 @@ pub trait CardClient {
/// A boxed CardClient (which is Send+Sync).
pub type CardClientBox = Box<dyn CardClient + Send + Sync>;
impl dyn CardClient {
impl<'a> dyn CardClient + 'a {
/// Select the OpenPGP card application
pub fn select(&mut self) -> Result<Response, Error> {
let select_openpgp = commands::select_openpgp();
apdu::send_command(self, select_openpgp, false)?.try_into()
}
// FIXME: this is a duplicate from card_app
pub fn application_related_data(
&mut self,
) -> Result<ApplicationRelatedData> {
let ad = commands::application_related_data();
let resp = apdu::send_command(self, ad, true)?;
let value = Value::from(resp.data()?, true)?;
log::debug!(" App data Value: {:x?}", value);
Ok(ApplicationRelatedData(Tlv::new(Tag::from([0x6E]), value)))
}
}
/// Configuration of the capabilities of a card.

View file

@ -7,10 +7,13 @@
use anyhow::{anyhow, Result};
use iso7816_tlv::simple::Tlv;
use pcsc::{Card, Context, Protocols, Scope, ShareMode};
use pcsc::{
Card, Context, Disposition, Protocols, Scope, ShareMode, Transaction,
};
use std::collections::HashMap;
use std::convert::TryInto;
use openpgp_card::card_do::ApplicationRelatedData;
use openpgp_card::{CardApp, CardCaps, CardClient, Error, SmartcardError};
const FEATURE_VERIFY_PIN_DIRECT: u8 = 0x06;
@ -24,6 +27,90 @@ pub struct PcscClient {
reader_caps: HashMap<u8, Tlv>,
}
struct TxClient<'a, 'b> {
tx: &'a mut Transaction<'b>,
}
impl<'a, 'b> TxClient<'a, 'b> {
pub fn new(tx: &'a mut Transaction<'b>) -> Self {
TxClient { tx }
}
}
impl<'a, 'b> TxClient<'a, 'b> {
/// Try to select the OpenPGP application on a card
fn select(card_client: &'a mut TxClient) -> Result<(), Error> {
if <dyn CardClient>::select(card_client).is_ok() {
Ok(())
} else {
Err(Error::Smartcard(SmartcardError::SelectOpenPGPCardFailed))
}
}
/// Get application_related_data from card
fn application_related_data(
card_client: &mut TxClient,
) -> Result<ApplicationRelatedData, Error> {
<dyn CardClient>::application_related_data(card_client).map_err(|e| {
Error::Smartcard(SmartcardError::Error(format!(
"TxClient: failed to get application_related_data {:x?}",
e
)))
})
}
}
impl CardClient for TxClient<'_, '_> {
fn transmit(
&mut self,
cmd: &[u8],
buf_size: usize,
) -> Result<Vec<u8>, Error> {
let mut resp_buffer = vec![0; buf_size];
let resp =
self.tx
.transmit(cmd, &mut resp_buffer)
.map_err(|e| match e {
pcsc::Error::NotTransacted => {
Error::Smartcard(SmartcardError::NotTransacted)
}
_ => Error::Smartcard(SmartcardError::Error(format!(
"Transmit failed: {:?}",
e
))),
})?;
log::debug!(" <- APDU response: {:x?}", resp);
Ok(resp.to_vec())
}
fn init_card_caps(&mut self, caps: CardCaps) {
self.card_caps = Some(caps);
}
fn card_caps(&self) -> Option<&CardCaps> {
None
}
fn feature_pinpad_verify(&self) -> bool {
todo!()
}
fn feature_pinpad_modify(&self) -> bool {
todo!()
}
fn pinpad_verify(&mut self, _id: u8) -> Result<Vec<u8>> {
todo!()
}
fn pinpad_modify(&mut self, _id: u8) -> Result<Vec<u8>> {
todo!()
}
}
impl PcscClient {
/// A list of "raw" opened PCSC Cards (without selecting the OpenPGP card
/// application)
@ -90,30 +177,66 @@ impl PcscClient {
}
}
/// All PCSC cards, wrapped as PcscClient
fn unopened_cards() -> Result<Vec<PcscClient>> {
Ok(Self::raw_pcsc_cards()
.map_err(|err| anyhow!(err))?
.into_iter()
.map(PcscClient::new)
.collect())
}
fn cards_filter(ident: Option<&str>) -> Result<Vec<CardApp>> {
let mut cards = vec![];
let mut cas: Vec<CardApp> = vec![];
for mut card in Self::unopened_cards()? {
for mut card in Self::raw_pcsc_cards()? {
log::debug!("cards_filter: next card");
// FIXME: start transaction
// let tx = card.card.transaction()?;
let stat = card.status2_owned();
log::debug!("cards_filter, status2: {:x?}", stat);
if let Err(e) = Self::select(&mut card) {
log::debug!("cards_filter: error during select: {:?}", e);
let mut store_card = false;
{
// start transaction
log::debug!("1");
let mut tx = loop {
let res = card.transaction();
match res {
Ok(tx) => break tx,
Err(pcsc::Error::ResetCard) => {
// Card was reset, need to reconnect
drop(res);
log::debug!("1a");
{
card.reconnect(
ShareMode::Shared,
Protocols::ANY,
Disposition::ResetCard,
)?;
}
log::debug!("1b");
// try again
}
Err(e) => {
return Err(e.into());
}
};
};
log::debug!("2");
let mut txc = TxClient::new(&mut tx);
log::debug!("3");
{
if let Err(e) = TxClient::select(&mut txc) {
log::debug!("4a");
log::debug!(
"cards_filter: error during select: {:?}",
e
);
} else {
if let Ok(mut ca) = card.into_card_app() {
log::debug!("4b");
// successfully opened the OpenPGP application
if let Some(ident) = ident {
let ard = ca.application_related_data()?;
let ard =
TxClient::application_related_data(&mut txc)?;
let aid = ard.application_id()?;
if aid.ident() == ident.to_ascii_uppercase() {
@ -123,31 +246,38 @@ impl PcscClient {
ident
);
cards.push(ca);
// we want to return this one card
store_card = true;
} else {
log::debug!(
"open_by_ident: Found, but won't use {:?}",
aid.ident()
);
}
} else {
// return all cards
cards.push(ca);
}
}
}
// FIXME: end transaction
// tx.end(Disposition::LeaveCard).map_err(|(_, e)| {
// Error::Smartcard(SmartcardError::Error(
// format!("PCSC Transaction::end failed: {:?}", e)
// ))
// })?;
// txc.end();
}
} else {
// we want to return all cards
store_card = true;
}
}
}
log::debug!("cards_filter: found {} cards", cards.len());
// transaction ends
// drop(txc);
// drop(tx);
}
Ok(cards)
if store_card {
let pcsc = PcscClient::new(card);
cas.push(pcsc.into_card_app()?);
}
}
log::debug!("cards_filter: found {} cards", cas.len());
Ok(cas)
}
/// Return all cards on which the OpenPGP application could be selected.
@ -200,15 +330,6 @@ impl PcscClient {
CardApp::initialize(Box::new(self))
}
/// Try to select the OpenPGP application on a card
fn select(card_client: &mut PcscClient) -> Result<(), Error> {
if <dyn CardClient>::select(card_client).is_ok() {
Ok(())
} else {
Err(Error::Smartcard(SmartcardError::SelectOpenPGPCardFailed))
}
}
/// Get the minimum pin length for pin_id.
fn min_pin_len(&self, pin_id: u8) -> Result<u8> {
match pin_id {
@ -256,24 +377,72 @@ impl CardClient for PcscClient {
cmd: &[u8],
buf_size: usize,
) -> Result<Vec<u8>, Error> {
let mut resp_buffer = vec![0; buf_size];
let mut was_reset = false;
let resp =
self.card.transmit(cmd, &mut resp_buffer).map_err(
|e| match e {
pcsc::Error::NotTransacted => {
Error::Smartcard(SmartcardError::NotTransacted)
let stat = self.card.status2_owned();
log::debug!("PcscClient transmit - status2: {:x?}", stat);
let mut tx = loop {
let res = self.card.transaction();
match res {
Ok(mut tx) => {
// A transaction has been successfully started
if was_reset {
log::debug!("card was reset, select() openpgp");
let mut txc = TxClient::new(&mut tx);
TxClient::select(&mut txc)?;
}
_ => Error::Smartcard(SmartcardError::Error(format!(
"Transmit failed: {:?}",
e
))),
},
)?;
log::debug!(" <- APDU response: {:x?}", resp);
break tx;
}
Err(pcsc::Error::ResetCard) => {
// Error getting Transaction.
// Card was reset -> need to reconnect.
Ok(resp.to_vec())
was_reset = true;
drop(res);
log::debug!("PcscClient transmit 1a");
{
self.card
.reconnect(
ShareMode::Shared,
Protocols::ANY,
Disposition::ResetCard,
)
.map_err(|e| {
Error::Smartcard(SmartcardError::Error(
format!("Reconnect failed: {:?}", e),
))
})?;
}
log::debug!("PcscClient transmit 1b");
// try again
}
Err(e) => {
return Err(Error::Smartcard(SmartcardError::Error(
format!("Error: {:?}", e),
)));
}
};
};
log::debug!("PcscClient transmit 2");
let mut txc = TxClient::new(&mut tx);
log::debug!("PcscClient transmit 3 (got TxClient!)");
let res = txc.transmit(cmd, buf_size);
log::debug!("PcscClient transmit res {:x?}", res);
res
}
fn init_card_caps(&mut self, caps: CardCaps) {