From 431da53b28f31286ef37f7b4ff8a487789d4699c Mon Sep 17 00:00:00 2001 From: Heiko Schaefer Date: Tue, 18 Jan 2022 13:18:30 +0100 Subject: [PATCH] Don't implement CardClient for PcscClient (users should always use transactions) --- card-functionality/src/list-cards.rs | 13 +- pcsc/src/lib.rs | 521 ++++++++++++++------------- 2 files changed, 275 insertions(+), 259 deletions(-) diff --git a/card-functionality/src/list-cards.rs b/card-functionality/src/list-cards.rs index 1d68ce2..22b1bf0 100644 --- a/card-functionality/src/list-cards.rs +++ b/card-functionality/src/list-cards.rs @@ -2,15 +2,22 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 use anyhow::Result; +use pcsc::Transaction; -use openpgp_card_pcsc::PcscClient; +use openpgp_card_pcsc::{PcscClient, PcscTxClient}; use openpgp_card_sequoia::card::Open; fn main() -> Result<()> { println!("The following OpenPGP cards are connected to your system:"); - for mut ca in PcscClient::cards()? { - let open = Open::new(&mut ca)?; + for mut card in PcscClient::cards()? { + 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()); } diff --git a/pcsc/src/lib.rs b/pcsc/src/lib.rs index 9e5bedf..268efc1 100644 --- a/pcsc/src/lib.rs +++ b/pcsc/src/lib.rs @@ -20,7 +20,9 @@ const FEATURE_MODIFY_PIN_DIRECT: u8 = 0x07; #[macro_export] macro_rules! start_tx { ($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; @@ -101,9 +103,7 @@ impl<'a, 'b> PcscTxClient<'a, 'b> { ) -> Self { PcscTxClient { tx, card_caps } } -} -impl<'a, 'b> PcscTxClient<'a, 'b> { /// Try to select the OpenPGP application on a card pub fn select(card_client: &'a mut PcscTxClient) -> Result<(), Error> { if ::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, 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<'_, '_> { @@ -380,13 +399,17 @@ impl PcscClient { } } - /// Make an initialized CardApp from a PcscClient. - /// Obtain and store feature lists from reader (pinpad functionality). + /// Make an initialized CardApp from a PcscClient: + /// - Obtain and store feature lists from reader (pinpad functionality). + /// - Get ARD from card, set CardCaps based on ARD. fn initialize_card(mut self) -> Result { 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) - if let Ok(feat) = self.features() { + if let Ok(feat) = txc.features() { for tlv in feat { log::debug!("Found reader feature {:?}", tlv); self.reader_caps.insert(tlv.tag().into(), tlv); @@ -394,7 +417,12 @@ impl PcscClient { } // Initalize CardClient (set CardCaps from ARD) - ::initialize(&mut self)?; + ::initialize(&mut txc)?; + + self.card_caps = txc.card_caps().cloned(); + + drop(txc); + drop(tx); 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, 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 { self.card_caps } } -impl CardClient for PcscClient { - fn transmit( - &mut self, - cmd: &[u8], - buf_size: usize, - ) -> Result, Error> { - let stat = self.card.status2_owned(); - log::debug!("PcscClient transmit - status2: {:x?}", stat); - - let mut tx: Transaction = start_tx!(self.card, true)?; - - log::debug!("PcscClient transmit 2"); - let mut txc = PcscTxClient::new(&mut tx, self.card_caps); - 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) { - self.card_caps = Some(caps); - } - - fn card_caps(&self) -> Option<&CardCaps> { - self.card_caps.as_ref() - } - - fn feature_pinpad_verify(&self) -> bool { - self.reader_caps.contains_key(&FEATURE_VERIFY_PIN_DIRECT) - } - - fn feature_pinpad_modify(&self) -> bool { - self.reader_caps.contains_key(&FEATURE_MODIFY_PIN_DIRECT) - } - - fn pinpad_verify(&mut self, pin_id: u8) -> Result> { - let pin_min_size = self.min_pin_len(pin_id)?; - let pin_max_size = self.max_pin_len(pin_id)?; - - // Default to varlen, for now. - // (NOTE: Some readers don't support varlen, and need explicit length - // information. Also see https://wiki.gnupg.org/CardReader/PinpadInput) - let fixedlen: u8 = 0; - - // APDU: 00 20 00 pin_id (ff)* - let mut ab_data = vec![ - 0x00, /* CLA */ - 0x20, /* INS: VERIFY */ - 0x00, /* P1 */ - pin_id, /* P2 */ - fixedlen, /* Lc: 'fixedlen' data bytes */ - ]; - ab_data.extend([0xff].repeat(fixedlen as usize)); - - // PC/SC v2.02.05 Part 10 PIN verification data structure - let mut send: Vec = vec![ - // 0 bTimeOut BYTE timeout in seconds (00 means use default - // timeout) - 0x00, - // 1 bTimeOut2 BYTE timeout in seconds after first key stroke - 0x00, - // 2 bmFormatString BYTE formatting options USB_CCID_PIN_FORMAT_xxx - 0x82, - // 3 bmPINBlockString BYTE - // bits 7-4 bit size of PIN length in APDU - // bits 3-0 PIN block size in bytes after justification and formatting - fixedlen, - // 4 bmPINLengthFormat BYTE - // bits 7-5 RFU, bit 4 set if system units are bytes clear if - // system units are bits, - // bits 3-0 PIN length position in system units - 0x00, - // 5 wPINMaxExtraDigit USHORT XXYY, where XX is minimum PIN size - // in digits, YY is maximum - pin_max_size, - pin_min_size, - // 7 bEntryValidationCondition BYTE Conditions under which PIN - // entry should be considered complete. - // - // table for bEntryValidationCondition: - // 0x01: Max size reached - // 0x02: Validation key pressed - // 0x04: Timeout occurred - 0x07, - // 8 bNumberMessage BYTE Number of messages to display for PIN - // verification - 0x01, - // 9 wLangIdU SHORT Language for messages - 0x04, - 0x09, // US english - // 11 bMsgIndex BYTE Message index (should be 00) - 0x00, - // 12 bTeoPrologue BYTE[3] T=1 I-block prologue field to use (fill with 00) - 0x00, - 0x00, - 0x00, - ]; - - // 15 ulDataLength ULONG length of Data to be sent to the ICC - send.extend(&(ab_data.len() as u32).to_le_bytes()); - - // 19 abData BYTE[] Data to send to the ICC - send.extend(ab_data); - - log::debug!("pcsc pinpad_verify send: {:x?}", send); - - let mut recv = vec![0xAA; 256]; - - let verify_ioctl: [u8; 4] = self - .reader_caps - .get(&FEATURE_VERIFY_PIN_DIRECT) - .ok_or_else(|| anyhow!("no reader_capability"))? - .value() - .try_into()?; - - let res = self.card.control( - u32::from_be_bytes(verify_ioctl).into(), - &send, - &mut recv, - )?; - - log::debug!(" <- pcsc pinpad_verify result: {:x?}", res); - - Ok(res.to_vec()) - } - - fn pinpad_modify(&mut self, pin_id: u8) -> Result> { - let pin_min_size = self.min_pin_len(pin_id)?; - let pin_max_size = self.max_pin_len(pin_id)?; - - // Default to varlen, for now. - // (NOTE: Some readers don't support varlen, and need explicit length - // information. Also see https://wiki.gnupg.org/CardReader/PinpadInput) - let fixedlen: u8 = 0; - - // APDU: 00 24 00 pin_id [(ff)* x2] - let mut ab_data = vec![ - 0x00, /* CLA */ - 0x24, /* INS: CHANGE_REFERENCE_DATA */ - 0x00, /* P1 */ - pin_id, /* P2 */ - fixedlen * 2, /* Lc: 'fixedlen' data bytes */ - ]; - ab_data.extend([0xff].repeat(fixedlen as usize * 2)); - - // PC/SC v2.02.05 Part 10 PIN modification data structure - let mut send: Vec = vec![ - // 0 bTimeOut BYTE timeout in seconds (00 means use default - // timeout) - 0x00, - // 1 bTimeOut2 BYTE timeout in seconds after first key stroke - 0x00, - // 2 bmFormatString BYTE formatting options USB_CCID_PIN_FORMAT_xxx - 0x82, - // 3 bmPINBlockString BYTE - // bits 7-4 bit size of PIN length in APDU - // bits 3-0 PIN block size in bytes after justification and formatting - fixedlen, - // 4 bmPINLengthFormat BYTE - // bits 7-5 RFU, bit 4 set if system units are bytes clear if - // system units are bits, - // bits 3-0 PIN length position in system units - 0x00, - // 5 bInsertionOffsetOld BYTE Insertion position offset in bytes for - // the current PIN - 0x00, - // 6 bInsertionOffsetNew BYTE Insertion position offset in bytes for - // the new PIN - fixedlen, - // 7 wPINMaxExtraDigit USHORT XXYY, where XX is minimum PIN size - // in digits, YY is maximum - pin_max_size, - pin_min_size, - // 9 bConfirmPIN - 0x03, // TODO check? - // 10 bEntryValidationCondition BYTE Conditions under which PIN - // entry should be considered complete. - // - // table for bEntryValidationCondition: - // 0x01: Max size reached - // 0x02: Validation key pressed - // 0x04: Timeout occurred - 0x07, - // 11 bNumberMessage BYTE Number of messages to display for PIN - // verification - 0x03, // TODO check? (match with bConfirmPIN?) - // 12 wLangId USHORT Language for messages - 0x04, - 0x09, // US english - // 14 bMsgIndex1-3 - 0x00, - 0x01, - 0x02, - // 17 bTeoPrologue BYTE[3] T=1 I-block prologue field to use (fill with 00) - 0x00, - 0x00, - 0x00, - ]; - - // 15 ulDataLength ULONG length of Data to be sent to the ICC - send.extend(&(ab_data.len() as u32).to_le_bytes()); - - // 19 abData BYTE[] Data to send to the ICC - send.extend(ab_data); - - log::debug!("pcsc pinpad_modify send: {:x?}", send); - - let mut recv = vec![0xAA; 256]; - - let modify_ioctl: [u8; 4] = self - .reader_caps - .get(&FEATURE_MODIFY_PIN_DIRECT) - .ok_or_else(|| anyhow!("no reader_capability"))? - .value() - .try_into()?; - - let res = self.card.control( - u32::from_be_bytes(modify_ioctl).into(), - &send, - &mut recv, - )?; - - log::debug!(" <- pcsc pinpad_modify result: {:x?}", res); - - Ok(res.to_vec()) - } -} +// impl CardClient for PcscClient { +// fn transmit( +// &mut self, +// cmd: &[u8], +// buf_size: usize, +// ) -> Result, Error> { +// let stat = self.card.status2_owned(); +// log::debug!("PcscClient transmit - status2: {:x?}", stat); +// +// let mut tx: Transaction = start_tx!(self.card, true)?; +// +// log::debug!("PcscClient transmit 2"); +// let mut txc = PcscTxClient::new(&mut tx, self.card_caps); +// 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) { +// self.card_caps = Some(caps); +// } +// +// fn card_caps(&self) -> Option<&CardCaps> { +// self.card_caps.as_ref() +// } +// +// fn feature_pinpad_verify(&self) -> bool { +// self.reader_caps.contains_key(&FEATURE_VERIFY_PIN_DIRECT) +// } +// +// fn feature_pinpad_modify(&self) -> bool { +// self.reader_caps.contains_key(&FEATURE_MODIFY_PIN_DIRECT) +// } +// +// fn pinpad_verify(&mut self, pin_id: u8) -> Result> { +// let pin_min_size = self.min_pin_len(pin_id)?; +// let pin_max_size = self.max_pin_len(pin_id)?; +// +// // Default to varlen, for now. +// // (NOTE: Some readers don't support varlen, and need explicit length +// // information. Also see https://wiki.gnupg.org/CardReader/PinpadInput) +// let fixedlen: u8 = 0; +// +// // APDU: 00 20 00 pin_id (ff)* +// let mut ab_data = vec![ +// 0x00, /* CLA */ +// 0x20, /* INS: VERIFY */ +// 0x00, /* P1 */ +// pin_id, /* P2 */ +// fixedlen, /* Lc: 'fixedlen' data bytes */ +// ]; +// ab_data.extend([0xff].repeat(fixedlen as usize)); +// +// // PC/SC v2.02.05 Part 10 PIN verification data structure +// let mut send: Vec = vec![ +// // 0 bTimeOut BYTE timeout in seconds (00 means use default +// // timeout) +// 0x00, +// // 1 bTimeOut2 BYTE timeout in seconds after first key stroke +// 0x00, +// // 2 bmFormatString BYTE formatting options USB_CCID_PIN_FORMAT_xxx +// 0x82, +// // 3 bmPINBlockString BYTE +// // bits 7-4 bit size of PIN length in APDU +// // bits 3-0 PIN block size in bytes after justification and formatting +// fixedlen, +// // 4 bmPINLengthFormat BYTE +// // bits 7-5 RFU, bit 4 set if system units are bytes clear if +// // system units are bits, +// // bits 3-0 PIN length position in system units +// 0x00, +// // 5 wPINMaxExtraDigit USHORT XXYY, where XX is minimum PIN size +// // in digits, YY is maximum +// pin_max_size, +// pin_min_size, +// // 7 bEntryValidationCondition BYTE Conditions under which PIN +// // entry should be considered complete. +// // +// // table for bEntryValidationCondition: +// // 0x01: Max size reached +// // 0x02: Validation key pressed +// // 0x04: Timeout occurred +// 0x07, +// // 8 bNumberMessage BYTE Number of messages to display for PIN +// // verification +// 0x01, +// // 9 wLangIdU SHORT Language for messages +// 0x04, +// 0x09, // US english +// // 11 bMsgIndex BYTE Message index (should be 00) +// 0x00, +// // 12 bTeoPrologue BYTE[3] T=1 I-block prologue field to use (fill with 00) +// 0x00, +// 0x00, +// 0x00, +// ]; +// +// // 15 ulDataLength ULONG length of Data to be sent to the ICC +// send.extend(&(ab_data.len() as u32).to_le_bytes()); +// +// // 19 abData BYTE[] Data to send to the ICC +// send.extend(ab_data); +// +// log::debug!("pcsc pinpad_verify send: {:x?}", send); +// +// let mut recv = vec![0xAA; 256]; +// +// let verify_ioctl: [u8; 4] = self +// .reader_caps +// .get(&FEATURE_VERIFY_PIN_DIRECT) +// .ok_or_else(|| anyhow!("no reader_capability"))? +// .value() +// .try_into()?; +// +// let res = self.card.control( +// u32::from_be_bytes(verify_ioctl).into(), +// &send, +// &mut recv, +// )?; +// +// log::debug!(" <- pcsc pinpad_verify result: {:x?}", res); +// +// Ok(res.to_vec()) +// } +// +// fn pinpad_modify(&mut self, pin_id: u8) -> Result> { +// let pin_min_size = self.min_pin_len(pin_id)?; +// let pin_max_size = self.max_pin_len(pin_id)?; +// +// // Default to varlen, for now. +// // (NOTE: Some readers don't support varlen, and need explicit length +// // information. Also see https://wiki.gnupg.org/CardReader/PinpadInput) +// let fixedlen: u8 = 0; +// +// // APDU: 00 24 00 pin_id [(ff)* x2] +// let mut ab_data = vec![ +// 0x00, /* CLA */ +// 0x24, /* INS: CHANGE_REFERENCE_DATA */ +// 0x00, /* P1 */ +// pin_id, /* P2 */ +// fixedlen * 2, /* Lc: 'fixedlen' data bytes */ +// ]; +// ab_data.extend([0xff].repeat(fixedlen as usize * 2)); +// +// // PC/SC v2.02.05 Part 10 PIN modification data structure +// let mut send: Vec = vec![ +// // 0 bTimeOut BYTE timeout in seconds (00 means use default +// // timeout) +// 0x00, +// // 1 bTimeOut2 BYTE timeout in seconds after first key stroke +// 0x00, +// // 2 bmFormatString BYTE formatting options USB_CCID_PIN_FORMAT_xxx +// 0x82, +// // 3 bmPINBlockString BYTE +// // bits 7-4 bit size of PIN length in APDU +// // bits 3-0 PIN block size in bytes after justification and formatting +// fixedlen, +// // 4 bmPINLengthFormat BYTE +// // bits 7-5 RFU, bit 4 set if system units are bytes clear if +// // system units are bits, +// // bits 3-0 PIN length position in system units +// 0x00, +// // 5 bInsertionOffsetOld BYTE Insertion position offset in bytes for +// // the current PIN +// 0x00, +// // 6 bInsertionOffsetNew BYTE Insertion position offset in bytes for +// // the new PIN +// fixedlen, +// // 7 wPINMaxExtraDigit USHORT XXYY, where XX is minimum PIN size +// // in digits, YY is maximum +// pin_max_size, +// pin_min_size, +// // 9 bConfirmPIN +// 0x03, // TODO check? +// // 10 bEntryValidationCondition BYTE Conditions under which PIN +// // entry should be considered complete. +// // +// // table for bEntryValidationCondition: +// // 0x01: Max size reached +// // 0x02: Validation key pressed +// // 0x04: Timeout occurred +// 0x07, +// // 11 bNumberMessage BYTE Number of messages to display for PIN +// // verification +// 0x03, // TODO check? (match with bConfirmPIN?) +// // 12 wLangId USHORT Language for messages +// 0x04, +// 0x09, // US english +// // 14 bMsgIndex1-3 +// 0x00, +// 0x01, +// 0x02, +// // 17 bTeoPrologue BYTE[3] T=1 I-block prologue field to use (fill with 00) +// 0x00, +// 0x00, +// 0x00, +// ]; +// +// // 15 ulDataLength ULONG length of Data to be sent to the ICC +// send.extend(&(ab_data.len() as u32).to_le_bytes()); +// +// // 19 abData BYTE[] Data to send to the ICC +// send.extend(ab_data); +// +// log::debug!("pcsc pinpad_modify send: {:x?}", send); +// +// let mut recv = vec![0xAA; 256]; +// +// let modify_ioctl: [u8; 4] = self +// .reader_caps +// .get(&FEATURE_MODIFY_PIN_DIRECT) +// .ok_or_else(|| anyhow!("no reader_capability"))? +// .value() +// .try_into()?; +// +// let res = self.card.control( +// u32::from_be_bytes(modify_ioctl).into(), +// &send, +// &mut recv, +// )?; +// +// log::debug!(" <- pcsc pinpad_modify result: {:x?}", res); +// +// Ok(res.to_vec()) +// } +// }