Don't implement CardClient for PcscClient (users should always use transactions)

This commit is contained in:
Heiko Schaefer 2022-01-18 13:18:30 +01:00
parent 2480745088
commit 431da53b28
No known key found for this signature in database
GPG key ID: 4A849A1904CCBD7D
2 changed files with 275 additions and 259 deletions

View file

@ -2,15 +2,22 @@
// SPDX-License-Identifier: MIT OR Apache-2.0 // SPDX-License-Identifier: MIT OR Apache-2.0
use anyhow::Result; use anyhow::Result;
use pcsc::Transaction;
use openpgp_card_pcsc::PcscClient; use openpgp_card_pcsc::{PcscClient, PcscTxClient};
use openpgp_card_sequoia::card::Open; use openpgp_card_sequoia::card::Open;
fn main() -> Result<()> { fn main() -> Result<()> {
println!("The following OpenPGP cards are connected to your system:"); println!("The following OpenPGP cards are connected to your system:");
for mut ca in PcscClient::cards()? { for mut card in PcscClient::cards()? {
let open = Open::new(&mut ca)?; let cc = card.card_caps();
let mut tx: Transaction =
openpgp_card_pcsc::start_tx!(card.card(), true)?;
let mut txc = PcscTxClient::new(&mut tx, cc);
let open = Open::new(&mut txc)?;
println!(" {}", open.application_identifier()?.ident()); println!(" {}", open.application_identifier()?.ident());
} }

View file

@ -20,7 +20,9 @@ const FEATURE_MODIFY_PIN_DIRECT: u8 = 0x07;
#[macro_export] #[macro_export]
macro_rules! start_tx { macro_rules! start_tx {
($card:expr, $reselect:expr) => {{ ($card:expr, $reselect:expr) => {{
use pcsc::{Disposition, Protocols, ShareMode}; use anyhow::anyhow;
use openpgp_card::{Error, SmartcardError};
use pcsc::{Disposition, Protocols, ShareMode, Transaction};
let mut was_reset = false; let mut was_reset = false;
@ -101,9 +103,7 @@ impl<'a, 'b> PcscTxClient<'a, 'b> {
) -> Self { ) -> Self {
PcscTxClient { tx, card_caps } PcscTxClient { tx, card_caps }
} }
}
impl<'a, 'b> PcscTxClient<'a, 'b> {
/// Try to select the OpenPGP application on a card /// Try to select the OpenPGP application on a card
pub fn select(card_client: &'a mut PcscTxClient) -> Result<(), Error> { pub fn select(card_client: &'a mut PcscTxClient) -> Result<(), Error> {
if <dyn CardClient>::select(card_client).is_ok() { if <dyn CardClient>::select(card_client).is_ok() {
@ -124,6 +124,25 @@ impl<'a, 'b> PcscTxClient<'a, 'b> {
))) )))
}) })
} }
/// GET_FEATURE_REQUEST
/// (see http://pcscworkgroup.com/Download/Specifications/pcsc10_v2.02.09.pdf)
fn features(&mut self) -> Result<Vec<Tlv>, Error> {
let mut recv = vec![0; 1024];
let cm_ioctl_get_feature_request = pcsc::ctl_code(3400);
let res = self
.tx
.control(cm_ioctl_get_feature_request, &[], &mut recv)
.map_err(|e| {
Error::Smartcard(SmartcardError::Error(format!(
"GET_FEATURE_REQUEST control call failed: {:?}",
e
)))
})?;
Ok(Tlv::parse_all(res))
}
} }
impl CardClient for PcscTxClient<'_, '_> { impl CardClient for PcscTxClient<'_, '_> {
@ -380,13 +399,17 @@ impl PcscClient {
} }
} }
/// Make an initialized CardApp from a PcscClient. /// Make an initialized CardApp from a PcscClient:
/// Obtain and store feature lists from reader (pinpad functionality). /// - Obtain and store feature lists from reader (pinpad functionality).
/// - Get ARD from card, set CardCaps based on ARD.
fn initialize_card(mut self) -> Result<Self> { fn initialize_card(mut self) -> Result<Self> {
log::debug!("pcsc initialize_card"); log::debug!("pcsc initialize_card");
let mut tx: Transaction = start_tx!(self.card, true)?;
let mut txc = PcscTxClient::new(&mut tx, self.card_caps);
// Get Features from reader (pinpad verify/modify) // Get Features from reader (pinpad verify/modify)
if let Ok(feat) = self.features() { if let Ok(feat) = txc.features() {
for tlv in feat { for tlv in feat {
log::debug!("Found reader feature {:?}", tlv); log::debug!("Found reader feature {:?}", tlv);
self.reader_caps.insert(tlv.tag().into(), tlv); self.reader_caps.insert(tlv.tag().into(), tlv);
@ -394,7 +417,12 @@ impl PcscClient {
} }
// Initalize CardClient (set CardCaps from ARD) // Initalize CardClient (set CardCaps from ARD)
<dyn CardClient>::initialize(&mut self)?; <dyn CardClient>::initialize(&mut txc)?;
self.card_caps = txc.card_caps().cloned();
drop(txc);
drop(tx);
Ok(self) Ok(self)
} }
@ -420,257 +448,238 @@ impl PcscClient {
} }
} }
/// GET_FEATURE_REQUEST
/// (see http://pcscworkgroup.com/Download/Specifications/pcsc10_v2.02.09.pdf)
fn features(&mut self) -> Result<Vec<Tlv>, Error> {
let mut recv = vec![0; 1024];
let cm_ioctl_get_feature_request = pcsc::ctl_code(3400);
let res = self
.card
.control(cm_ioctl_get_feature_request, &[], &mut recv)
.map_err(|e| {
Error::Smartcard(SmartcardError::Error(format!(
"GET_FEATURE_REQUEST control call failed: {:?}",
e
)))
})?;
Ok(Tlv::parse_all(res))
}
pub fn card_caps(&self) -> Option<CardCaps> { pub fn card_caps(&self) -> Option<CardCaps> {
self.card_caps self.card_caps
} }
} }
impl CardClient for PcscClient { // impl CardClient for PcscClient {
fn transmit( // fn transmit(
&mut self, // &mut self,
cmd: &[u8], // cmd: &[u8],
buf_size: usize, // buf_size: usize,
) -> Result<Vec<u8>, Error> { // ) -> Result<Vec<u8>, Error> {
let stat = self.card.status2_owned(); // let stat = self.card.status2_owned();
log::debug!("PcscClient transmit - status2: {:x?}", stat); // log::debug!("PcscClient transmit - status2: {:x?}", stat);
//
let mut tx: Transaction = start_tx!(self.card, true)?; // let mut tx: Transaction = start_tx!(self.card, true)?;
//
log::debug!("PcscClient transmit 2"); // log::debug!("PcscClient transmit 2");
let mut txc = PcscTxClient::new(&mut tx, self.card_caps); // let mut txc = PcscTxClient::new(&mut tx, self.card_caps);
log::debug!("PcscClient transmit 3 (got TxClient!)"); // log::debug!("PcscClient transmit 3 (got TxClient!)");
//
let res = txc.transmit(cmd, buf_size); // let res = txc.transmit(cmd, buf_size);
//
log::debug!("PcscClient transmit res {:x?}", res); // log::debug!("PcscClient transmit res {:x?}", res);
//
res // res
} // }
//
fn init_card_caps(&mut self, caps: CardCaps) { // fn init_card_caps(&mut self, caps: CardCaps) {
self.card_caps = Some(caps); // self.card_caps = Some(caps);
} // }
//
fn card_caps(&self) -> Option<&CardCaps> { // fn card_caps(&self) -> Option<&CardCaps> {
self.card_caps.as_ref() // self.card_caps.as_ref()
} // }
//
fn feature_pinpad_verify(&self) -> bool { // fn feature_pinpad_verify(&self) -> bool {
self.reader_caps.contains_key(&FEATURE_VERIFY_PIN_DIRECT) // self.reader_caps.contains_key(&FEATURE_VERIFY_PIN_DIRECT)
} // }
//
fn feature_pinpad_modify(&self) -> bool { // fn feature_pinpad_modify(&self) -> bool {
self.reader_caps.contains_key(&FEATURE_MODIFY_PIN_DIRECT) // self.reader_caps.contains_key(&FEATURE_MODIFY_PIN_DIRECT)
} // }
//
fn pinpad_verify(&mut self, pin_id: u8) -> Result<Vec<u8>> { // fn pinpad_verify(&mut self, pin_id: u8) -> Result<Vec<u8>> {
let pin_min_size = self.min_pin_len(pin_id)?; // let pin_min_size = self.min_pin_len(pin_id)?;
let pin_max_size = self.max_pin_len(pin_id)?; // let pin_max_size = self.max_pin_len(pin_id)?;
//
// Default to varlen, for now. // // Default to varlen, for now.
// (NOTE: Some readers don't support varlen, and need explicit length // // (NOTE: Some readers don't support varlen, and need explicit length
// information. Also see https://wiki.gnupg.org/CardReader/PinpadInput) // // information. Also see https://wiki.gnupg.org/CardReader/PinpadInput)
let fixedlen: u8 = 0; // let fixedlen: u8 = 0;
//
// APDU: 00 20 00 pin_id <len> (ff)* // // APDU: 00 20 00 pin_id <len> (ff)*
let mut ab_data = vec![ // let mut ab_data = vec![
0x00, /* CLA */ // 0x00, /* CLA */
0x20, /* INS: VERIFY */ // 0x20, /* INS: VERIFY */
0x00, /* P1 */ // 0x00, /* P1 */
pin_id, /* P2 */ // pin_id, /* P2 */
fixedlen, /* Lc: 'fixedlen' data bytes */ // fixedlen, /* Lc: 'fixedlen' data bytes */
]; // ];
ab_data.extend([0xff].repeat(fixedlen as usize)); // ab_data.extend([0xff].repeat(fixedlen as usize));
//
// PC/SC v2.02.05 Part 10 PIN verification data structure // // PC/SC v2.02.05 Part 10 PIN verification data structure
let mut send: Vec<u8> = vec![ // let mut send: Vec<u8> = vec![
// 0 bTimeOut BYTE timeout in seconds (00 means use default // // 0 bTimeOut BYTE timeout in seconds (00 means use default
// timeout) // // timeout)
0x00, // 0x00,
// 1 bTimeOut2 BYTE timeout in seconds after first key stroke // // 1 bTimeOut2 BYTE timeout in seconds after first key stroke
0x00, // 0x00,
// 2 bmFormatString BYTE formatting options USB_CCID_PIN_FORMAT_xxx // // 2 bmFormatString BYTE formatting options USB_CCID_PIN_FORMAT_xxx
0x82, // 0x82,
// 3 bmPINBlockString BYTE // // 3 bmPINBlockString BYTE
// bits 7-4 bit size of PIN length in APDU // // bits 7-4 bit size of PIN length in APDU
// bits 3-0 PIN block size in bytes after justification and formatting // // bits 3-0 PIN block size in bytes after justification and formatting
fixedlen, // fixedlen,
// 4 bmPINLengthFormat BYTE // // 4 bmPINLengthFormat BYTE
// bits 7-5 RFU, bit 4 set if system units are bytes clear if // // bits 7-5 RFU, bit 4 set if system units are bytes clear if
// system units are bits, // // system units are bits,
// bits 3-0 PIN length position in system units // // bits 3-0 PIN length position in system units
0x00, // 0x00,
// 5 wPINMaxExtraDigit USHORT XXYY, where XX is minimum PIN size // // 5 wPINMaxExtraDigit USHORT XXYY, where XX is minimum PIN size
// in digits, YY is maximum // // in digits, YY is maximum
pin_max_size, // pin_max_size,
pin_min_size, // pin_min_size,
// 7 bEntryValidationCondition BYTE Conditions under which PIN // // 7 bEntryValidationCondition BYTE Conditions under which PIN
// entry should be considered complete. // // entry should be considered complete.
// // //
// table for bEntryValidationCondition: // // table for bEntryValidationCondition:
// 0x01: Max size reached // // 0x01: Max size reached
// 0x02: Validation key pressed // // 0x02: Validation key pressed
// 0x04: Timeout occurred // // 0x04: Timeout occurred
0x07, // 0x07,
// 8 bNumberMessage BYTE Number of messages to display for PIN // // 8 bNumberMessage BYTE Number of messages to display for PIN
// verification // // verification
0x01, // 0x01,
// 9 wLangIdU SHORT Language for messages // // 9 wLangIdU SHORT Language for messages
0x04, // 0x04,
0x09, // US english // 0x09, // US english
// 11 bMsgIndex BYTE Message index (should be 00) // // 11 bMsgIndex BYTE Message index (should be 00)
0x00, // 0x00,
// 12 bTeoPrologue BYTE[3] T=1 I-block prologue field to use (fill with 00) // // 12 bTeoPrologue BYTE[3] T=1 I-block prologue field to use (fill with 00)
0x00, // 0x00,
0x00, // 0x00,
0x00, // 0x00,
]; // ];
//
// 15 ulDataLength ULONG length of Data to be sent to the ICC // // 15 ulDataLength ULONG length of Data to be sent to the ICC
send.extend(&(ab_data.len() as u32).to_le_bytes()); // send.extend(&(ab_data.len() as u32).to_le_bytes());
//
// 19 abData BYTE[] Data to send to the ICC // // 19 abData BYTE[] Data to send to the ICC
send.extend(ab_data); // send.extend(ab_data);
//
log::debug!("pcsc pinpad_verify send: {:x?}", send); // log::debug!("pcsc pinpad_verify send: {:x?}", send);
//
let mut recv = vec![0xAA; 256]; // let mut recv = vec![0xAA; 256];
//
let verify_ioctl: [u8; 4] = self // let verify_ioctl: [u8; 4] = self
.reader_caps // .reader_caps
.get(&FEATURE_VERIFY_PIN_DIRECT) // .get(&FEATURE_VERIFY_PIN_DIRECT)
.ok_or_else(|| anyhow!("no reader_capability"))? // .ok_or_else(|| anyhow!("no reader_capability"))?
.value() // .value()
.try_into()?; // .try_into()?;
//
let res = self.card.control( // let res = self.card.control(
u32::from_be_bytes(verify_ioctl).into(), // u32::from_be_bytes(verify_ioctl).into(),
&send, // &send,
&mut recv, // &mut recv,
)?; // )?;
//
log::debug!(" <- pcsc pinpad_verify result: {:x?}", res); // log::debug!(" <- pcsc pinpad_verify result: {:x?}", res);
//
Ok(res.to_vec()) // Ok(res.to_vec())
} // }
//
fn pinpad_modify(&mut self, pin_id: u8) -> Result<Vec<u8>> { // fn pinpad_modify(&mut self, pin_id: u8) -> Result<Vec<u8>> {
let pin_min_size = self.min_pin_len(pin_id)?; // let pin_min_size = self.min_pin_len(pin_id)?;
let pin_max_size = self.max_pin_len(pin_id)?; // let pin_max_size = self.max_pin_len(pin_id)?;
//
// Default to varlen, for now. // // Default to varlen, for now.
// (NOTE: Some readers don't support varlen, and need explicit length // // (NOTE: Some readers don't support varlen, and need explicit length
// information. Also see https://wiki.gnupg.org/CardReader/PinpadInput) // // information. Also see https://wiki.gnupg.org/CardReader/PinpadInput)
let fixedlen: u8 = 0; // let fixedlen: u8 = 0;
//
// APDU: 00 24 00 pin_id <len> [(ff)* x2] // // APDU: 00 24 00 pin_id <len> [(ff)* x2]
let mut ab_data = vec![ // let mut ab_data = vec![
0x00, /* CLA */ // 0x00, /* CLA */
0x24, /* INS: CHANGE_REFERENCE_DATA */ // 0x24, /* INS: CHANGE_REFERENCE_DATA */
0x00, /* P1 */ // 0x00, /* P1 */
pin_id, /* P2 */ // pin_id, /* P2 */
fixedlen * 2, /* Lc: 'fixedlen' data bytes */ // fixedlen * 2, /* Lc: 'fixedlen' data bytes */
]; // ];
ab_data.extend([0xff].repeat(fixedlen as usize * 2)); // ab_data.extend([0xff].repeat(fixedlen as usize * 2));
//
// PC/SC v2.02.05 Part 10 PIN modification data structure // // PC/SC v2.02.05 Part 10 PIN modification data structure
let mut send: Vec<u8> = vec![ // let mut send: Vec<u8> = vec![
// 0 bTimeOut BYTE timeout in seconds (00 means use default // // 0 bTimeOut BYTE timeout in seconds (00 means use default
// timeout) // // timeout)
0x00, // 0x00,
// 1 bTimeOut2 BYTE timeout in seconds after first key stroke // // 1 bTimeOut2 BYTE timeout in seconds after first key stroke
0x00, // 0x00,
// 2 bmFormatString BYTE formatting options USB_CCID_PIN_FORMAT_xxx // // 2 bmFormatString BYTE formatting options USB_CCID_PIN_FORMAT_xxx
0x82, // 0x82,
// 3 bmPINBlockString BYTE // // 3 bmPINBlockString BYTE
// bits 7-4 bit size of PIN length in APDU // // bits 7-4 bit size of PIN length in APDU
// bits 3-0 PIN block size in bytes after justification and formatting // // bits 3-0 PIN block size in bytes after justification and formatting
fixedlen, // fixedlen,
// 4 bmPINLengthFormat BYTE // // 4 bmPINLengthFormat BYTE
// bits 7-5 RFU, bit 4 set if system units are bytes clear if // // bits 7-5 RFU, bit 4 set if system units are bytes clear if
// system units are bits, // // system units are bits,
// bits 3-0 PIN length position in system units // // bits 3-0 PIN length position in system units
0x00, // 0x00,
// 5 bInsertionOffsetOld BYTE Insertion position offset in bytes for // // 5 bInsertionOffsetOld BYTE Insertion position offset in bytes for
// the current PIN // // the current PIN
0x00, // 0x00,
// 6 bInsertionOffsetNew BYTE Insertion position offset in bytes for // // 6 bInsertionOffsetNew BYTE Insertion position offset in bytes for
// the new PIN // // the new PIN
fixedlen, // fixedlen,
// 7 wPINMaxExtraDigit USHORT XXYY, where XX is minimum PIN size // // 7 wPINMaxExtraDigit USHORT XXYY, where XX is minimum PIN size
// in digits, YY is maximum // // in digits, YY is maximum
pin_max_size, // pin_max_size,
pin_min_size, // pin_min_size,
// 9 bConfirmPIN // // 9 bConfirmPIN
0x03, // TODO check? // 0x03, // TODO check?
// 10 bEntryValidationCondition BYTE Conditions under which PIN // // 10 bEntryValidationCondition BYTE Conditions under which PIN
// entry should be considered complete. // // entry should be considered complete.
// // //
// table for bEntryValidationCondition: // // table for bEntryValidationCondition:
// 0x01: Max size reached // // 0x01: Max size reached
// 0x02: Validation key pressed // // 0x02: Validation key pressed
// 0x04: Timeout occurred // // 0x04: Timeout occurred
0x07, // 0x07,
// 11 bNumberMessage BYTE Number of messages to display for PIN // // 11 bNumberMessage BYTE Number of messages to display for PIN
// verification // // verification
0x03, // TODO check? (match with bConfirmPIN?) // 0x03, // TODO check? (match with bConfirmPIN?)
// 12 wLangId USHORT Language for messages // // 12 wLangId USHORT Language for messages
0x04, // 0x04,
0x09, // US english // 0x09, // US english
// 14 bMsgIndex1-3 // // 14 bMsgIndex1-3
0x00, // 0x00,
0x01, // 0x01,
0x02, // 0x02,
// 17 bTeoPrologue BYTE[3] T=1 I-block prologue field to use (fill with 00) // // 17 bTeoPrologue BYTE[3] T=1 I-block prologue field to use (fill with 00)
0x00, // 0x00,
0x00, // 0x00,
0x00, // 0x00,
]; // ];
//
// 15 ulDataLength ULONG length of Data to be sent to the ICC // // 15 ulDataLength ULONG length of Data to be sent to the ICC
send.extend(&(ab_data.len() as u32).to_le_bytes()); // send.extend(&(ab_data.len() as u32).to_le_bytes());
//
// 19 abData BYTE[] Data to send to the ICC // // 19 abData BYTE[] Data to send to the ICC
send.extend(ab_data); // send.extend(ab_data);
//
log::debug!("pcsc pinpad_modify send: {:x?}", send); // log::debug!("pcsc pinpad_modify send: {:x?}", send);
//
let mut recv = vec![0xAA; 256]; // let mut recv = vec![0xAA; 256];
//
let modify_ioctl: [u8; 4] = self // let modify_ioctl: [u8; 4] = self
.reader_caps // .reader_caps
.get(&FEATURE_MODIFY_PIN_DIRECT) // .get(&FEATURE_MODIFY_PIN_DIRECT)
.ok_or_else(|| anyhow!("no reader_capability"))? // .ok_or_else(|| anyhow!("no reader_capability"))?
.value() // .value()
.try_into()?; // .try_into()?;
//
let res = self.card.control( // let res = self.card.control(
u32::from_be_bytes(modify_ioctl).into(), // u32::from_be_bytes(modify_ioctl).into(),
&send, // &send,
&mut recv, // &mut recv,
)?; // )?;
//
log::debug!(" <- pcsc pinpad_modify result: {:x?}", res); // log::debug!(" <- pcsc pinpad_modify result: {:x?}", res);
//
Ok(res.to_vec()) // Ok(res.to_vec())
} // }
} // }