From de0645ef0e30deb35226a03eb874b47faf4298bb Mon Sep 17 00:00:00 2001 From: Heiko Schaefer Date: Mon, 12 Jul 2021 12:15:31 +0200 Subject: [PATCH] refactor opening of cards --- openpgp-card-sequoia/src/main.rs | 4 +- openpgp-card/src/apdu/mod.rs | 28 ++++++++-- openpgp-card/src/card_app.rs | 24 ++++----- openpgp-card/src/key_upload.rs | 5 +- openpgp-card/src/lib.rs | 91 ++++++++++++++------------------ scdc/src/lib.rs | 42 +++------------ 6 files changed, 84 insertions(+), 110 deletions(-) diff --git a/openpgp-card-sequoia/src/main.rs b/openpgp-card-sequoia/src/main.rs index 899ee27..027fded 100644 --- a/openpgp-card-sequoia/src/main.rs +++ b/openpgp-card-sequoia/src/main.rs @@ -186,7 +186,7 @@ fn main() -> Result<(), Box> { // Open fresh Card for signing // ----------------------------- // let oc = CardBase::open_by_ident(&test_card_ident)?; - let mut oc = ScdClient::open_scdc(SOCKET)?; + let oc = ScdClient::open_scdc(SOCKET)?; // Sign match oc.verify_pw1_for_signing("123456") { @@ -220,7 +220,7 @@ fn main() -> Result<(), Box> { println!("The following OpenPGP cards are connected to your system:"); - let cards = openpgp_card::CardBase::list_cards()?; + let cards = openpgp_card::CardBase::list_cards_pcsc()?; for c in cards { println!(" '{}'", c.get_aid()?.ident()); } diff --git a/openpgp-card/src/apdu/mod.rs b/openpgp-card/src/apdu/mod.rs index c91923b..ef633dd 100644 --- a/openpgp-card/src/apdu/mod.rs +++ b/openpgp-card/src/apdu/mod.rs @@ -5,14 +5,14 @@ pub mod command; pub mod commands; pub mod response; -use anyhow::Result; +use anyhow::{anyhow, Result}; use pcsc::Card; use std::convert::TryFrom; use crate::apdu::command::Command; use crate::apdu::response::Response; use crate::errors::{OcErrorStatus, OpenpgpCardError, SmartcardError}; -use crate::{CardCaps, CardClient}; +use crate::{CardBase, CardCaps, CardClient, CardClientBox}; #[derive(Clone, Copy, PartialEq)] pub(crate) enum Le { @@ -26,7 +26,7 @@ pub(crate) enum Le { /// 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 Box, + card_client: &mut CardClientBox, cmd: Command, expect_reply: bool, card_caps: Option<&CardCaps>, @@ -70,7 +70,7 @@ pub(crate) fn send_command( /// If the response is chained, this fn only returns one chunk, the caller /// needs take care of chained responses fn send_command_low_level( - card_client: &mut Box, + card_client: &mut CardClientBox, cmd: Command, expect_reply: bool, card_caps: Option<&CardCaps>, @@ -184,9 +184,27 @@ pub struct PcscClient { } impl PcscClient { - pub fn new(card: Card) -> Self { + fn new(card: Card) -> Self { Self { card } } + + /// Take a PCSC Card object and try to open the OpenPGP card applet. + /// If successful, wrap and return the resulting CardClient as a + /// CardBase object (which involves caching the "application related + /// data"). + pub fn open(card: Card) -> Result { + let card_client = PcscClient::new(card); + let mut ccb = Box::new(card_client) as CardClientBox; + + let select_openpgp = commands::select_openpgp(); + let resp = send_command(&mut ccb, select_openpgp, false, None)?; + + if resp.is_ok() { + CardBase::open_card(ccb) + } else { + Err(anyhow!("Couldn't open OpenPGP application").into()) + } + } } impl CardClient for PcscClient { diff --git a/openpgp-card/src/card_app.rs b/openpgp-card/src/card_app.rs index 7bca175..1608cf6 100644 --- a/openpgp-card/src/card_app.rs +++ b/openpgp-card/src/card_app.rs @@ -13,34 +13,28 @@ use std::borrow::BorrowMut; use std::convert::TryFrom; use anyhow::{anyhow, Result}; -use pcsc::*; -use apdu::{commands, response::Response}; -use parse::{ +use crate::apdu::{commands, response::Response}; +use crate::errors::OpenpgpCardError; +use crate::parse::{ algo_attrs::Algo, algo_info::AlgoInfo, application_id::ApplicationId, cardholder::CardHolder, extended_cap::ExtendedCap, extended_length_info::ExtendedLengthInfo, fingerprint, historical::Historical, pw_status::PWStatus, KeySet, }; -use tlv::Tlv; - -use crate::errors::OpenpgpCardError; -use crate::tlv::tag::Tag; -use crate::tlv::TlvEntry; - -use crate::Hash; +use crate::tlv::{tag::Tag, Tlv, TlvEntry}; use crate::{ - apdu, key_upload, parse, tlv, CardCaps, CardClient, CardUploadableKey, - DecryptMe, KeyType, Sex, + apdu, key_upload, parse, tlv, CardCaps, CardClientBox, CardUploadableKey, + DecryptMe, Hash, KeyType, Sex, }; pub struct CardApp { - card_client: Box, + card_client: CardClientBox, card_caps: Option, } impl CardApp { - pub fn new(card_client: Box) -> Self { + pub fn new(card_client: CardClientBox) -> Self { Self { card_client, card_caps: None, @@ -54,7 +48,7 @@ impl CardApp { } } - pub fn card(&mut self) -> &mut Box { + pub fn card(&mut self) -> &mut CardClientBox { &mut self.card_client } diff --git a/openpgp-card/src/key_upload.rs b/openpgp-card/src/key_upload.rs index 0bd733f..27aea12 100644 --- a/openpgp-card/src/key_upload.rs +++ b/openpgp-card/src/key_upload.rs @@ -10,12 +10,11 @@ use crate::errors::OpenpgpCardError; use crate::parse::algo_attrs::{Algo, RsaAttrs}; use crate::parse::algo_info::AlgoInfo; use crate::tlv::{tag::Tag, Tlv, TlvEntry}; -use crate::{apdu, CardCaps, CardClient}; +use crate::{apdu, CardCaps, CardClientBox}; use crate::{ tlv, CardUploadableKey, EccKey, EccType, KeyType, PrivateKeyMaterial, RSAKey, }; -use pcsc::Card; /// Upload an explicitly selected Key to the card as a specific KeyType. /// @@ -370,7 +369,7 @@ fn ecc_algo_attrs_cmd( } fn copy_key_to_card( - card_client: &mut Box, + card_client: &mut CardClientBox, key_type: KeyType, ts: u64, fp: Vec, diff --git a/openpgp-card/src/lib.rs b/openpgp-card/src/lib.rs index 1caa72a..a93bbbd 100644 --- a/openpgp-card/src/lib.rs +++ b/openpgp-card/src/lib.rs @@ -30,6 +30,8 @@ pub trait CardClient { fn transmit(&mut self, cmd: &[u8], buf_size: usize) -> Result>; } +pub type CardClientBox = Box; + /// Information about the capabilities of the card. /// (feature configuration from card metadata) #[derive(Clone, Copy)] @@ -250,12 +252,12 @@ impl CardBase { Self { card_app, ard } } - /// Get all cards that can be opened as an OpenPGP card applet - pub fn list_cards() -> Result> { + /// Get all cards that can be opened as an OpenPGP card applet via pcsc + pub fn list_cards_pcsc() -> Result> { let cards = card::get_cards().map_err(|err| anyhow!(err))?; let ocs: Vec<_> = cards .into_iter() - .map(Self::open_card) + .map(PcscClient::open) .map(|oc| oc.ok()) .flatten() .collect(); @@ -267,7 +269,7 @@ impl CardBase { /// /// The ident is constructed as a concatenation of manufacturer /// id, a colon, and the card serial. Example: "1234:5678ABCD". - pub fn open_by_ident(ident: &str) -> Result { + pub fn open_by_ident_pcsc(ident: &str) -> Result { let cards = card::get_cards().map_err(|e| { OpenpgpCardError::Smartcard(SmartcardError::Error(format!( "{:?}", @@ -276,7 +278,7 @@ impl CardBase { })?; for card in cards { - let res = Self::open_card(card); + let res = PcscClient::open(card); if let Ok(opened_card) = res { let res = opened_card.get_aid(); if let Ok(aid) = res { @@ -293,7 +295,7 @@ impl CardBase { } /// Open connection to some card and select the openpgp applet - pub fn open_yolo() -> Result { + pub fn open_yolo_pcsc() -> Result { let mut cards = card::get_cards().map_err(|e| { OpenpgpCardError::Smartcard(SmartcardError::Error(format!( "{:?}", @@ -304,57 +306,46 @@ impl CardBase { // randomly use the first card in the list let card = cards.swap_remove(0); - Self::open_card(card) + PcscClient::open(card) } - /// Open connection to a specific card and select the openpgp applet - fn open_card(card: Card) -> Result { - let select_openpgp = commands::select_openpgp(); + /// Set up connection (cache "application related data") to a + /// CardClient, on which the openpgp applet has already been opened. + pub fn open_card(ccb: CardClientBox) -> Result { + // read and cache "application related data" + let mut card_app = CardApp::new(ccb); + let ard = card_app.get_app_data()?; - let card_client = PcscClient::new(card); - let mut ccb = - Box::new(card_client) as Box; + // Determine chaining/extended length support from card + // metadata and cache this information in CardApp (as a + // CardCaps) - let resp = apdu::send_command(&mut ccb, select_openpgp, false, None)?; + let mut ext_support = false; + let mut chaining_support = false; - if resp.is_ok() { - // read and cache "application related data" - let mut card_app = CardApp::new(ccb); - let ard = card_app.get_app_data()?; - - // Determine chaining/extended length support from card - // metadata and cache this information in CardApp (as a - // CardCaps) - - let mut ext_support = false; - let mut chaining_support = false; - - if let Ok(hist) = CardApp::get_historical(&ard) { - if let Some(cc) = hist.get_card_capabilities() { - chaining_support = cc.get_command_chaining(); - ext_support = cc.get_extended_lc_le(); - } + if let Ok(hist) = CardApp::get_historical(&ard) { + if let Some(cc) = hist.get_card_capabilities() { + chaining_support = cc.get_command_chaining(); + ext_support = cc.get_extended_lc_le(); } - - let max_cmd_bytes = if let Ok(Some(eli)) = - CardApp::get_extended_length_information(&ard) - { - eli.max_command_bytes - } else { - 255 - }; - - let caps = CardCaps { - ext_support, - chaining_support, - max_cmd_bytes, - }; - let card_app = card_app.set_caps(caps); - - Ok(Self { card_app, ard }) - } else { - Err(anyhow!("Couldn't open OpenPGP application").into()) } + + let max_cmd_bytes = if let Ok(Some(eli)) = + CardApp::get_extended_length_information(&ard) + { + eli.max_command_bytes + } else { + 255 + }; + + let caps = CardCaps { + ext_support, + chaining_support, + max_cmd_bytes, + }; + let card_app = card_app.set_caps(caps); + + Ok(Self { card_app, ard }) } // --- application data --- diff --git a/scdc/src/lib.rs b/scdc/src/lib.rs index fb15cc6..89347ce 100644 --- a/scdc/src/lib.rs +++ b/scdc/src/lib.rs @@ -5,9 +5,8 @@ use sequoia_ipc::assuan::{Client, Response}; use std::sync::{Arc, Mutex}; use tokio::runtime::Runtime; -use openpgp_card::card_app::CardApp; use openpgp_card::errors::OpenpgpCardError; -use openpgp_card::{CardBase, CardCaps, CardClient}; +use openpgp_card::{CardBase, CardClient, CardClientBox}; lazy_static! { pub(crate) static ref RT: Mutex = @@ -23,40 +22,9 @@ impl ScdClient { /// backend. pub fn open_scdc(socket: &str) -> Result { let card_client = ScdClient::new(socket)?; - let card_client_box = - Box::new(card_client) as Box; + let card_client_box = Box::new(card_client) as CardClientBox; - // read and cache "application related data" - let mut card_app = CardApp::new(card_client_box); - let ard = card_app.get_app_data()?; - - // Determine chaining/extended length support from card - // metadata and cache this information in CardApp (as a - // CardCaps) - - let mut ext_support = false; - let mut chaining_support = false; - - if let Ok(hist) = CardApp::get_historical(&ard) { - if let Some(cc) = hist.get_card_capabilities() { - chaining_support = cc.get_command_chaining(); - ext_support = cc.get_extended_lc_le(); - } - } - - let max_cmd_bytes = if let Ok(Some(eli)) = - CardApp::get_extended_length_information(&ard) - { - eli.max_command_bytes - } else { - 255 - }; - - let caps = CardCaps::new(ext_support, chaining_support, max_cmd_bytes); - - let card_app = card_app.set_caps(caps); - - Ok(CardBase::new(card_app, ard)) + CardBase::open_card(card_client_box) } pub fn new(socket: &str) -> Result { @@ -80,6 +48,10 @@ impl CardClient for ScdClient { while let Some(response) = rt.block_on(client.next()) { println!("res: {:x?}", response); + if let Err(_) = response { + unimplemented!(); + } + if let Ok(Response::Data { partial }) = response { let res = partial;