From 0c86fcb84abbf51c0e66123d2bfec47eefcc6a18 Mon Sep 17 00:00:00 2001 From: Heiko Schaefer Date: Fri, 18 Feb 2022 16:16:55 +0100 Subject: [PATCH] Implement CardBackend/CardTransaction for scdc backend --- card-functionality/src/cards.rs | 8 ++--- scdc/src/lib.rs | 52 ++++++++++++++++++++++----------- 2 files changed, 38 insertions(+), 22 deletions(-) diff --git a/card-functionality/src/cards.rs b/card-functionality/src/cards.rs index c3cc730..b35c19b 100644 --- a/card-functionality/src/cards.rs +++ b/card-functionality/src/cards.rs @@ -11,7 +11,7 @@ use std::collections::BTreeMap; use openpgp_card::{CardBackend, Error}; use openpgp_card_pcsc::PcscBackend; -use openpgp_card_scdc::ScdClient; +use openpgp_card_scdc::ScdBackend; const SHARE_MODE: Option = Some(ShareMode::Shared); @@ -97,7 +97,7 @@ impl TestCard { Self::Pcsc(ident) => { // Attempt to shutdown SCD, if it is running. // Ignore any errors that occur during that shutdown attempt. - let res = ScdClient::shutdown_scd(None); + let res = ScdBackend::shutdown_scd(None); log::trace!(" Attempt to shutdown scd: {:?}", res); // Make three attempts to open the card before failing @@ -119,9 +119,7 @@ impl TestCard { Ok(card?) } Self::Scdc(serial) => { - unimplemented!(); - println!("open scdc card {}", serial); - // Ok(Box::new(ScdClient::open_by_serial(None, serial)?)) + Ok(Box::new(ScdBackend::open_by_serial(None, serial)?)) } } } diff --git a/scdc/src/lib.rs b/scdc/src/lib.rs index 8a883bd..947e9b6 100644 --- a/scdc/src/lib.rs +++ b/scdc/src/lib.rs @@ -13,7 +13,7 @@ use sequoia_ipc::gnupg::{Agent, Context}; use std::sync::Mutex; use tokio::runtime::Runtime; -use openpgp_card::{CardCaps, CardTransaction, Error}; +use openpgp_card::{CardBackend, CardCaps, CardTransaction, Error}; lazy_static! { static ref RT: Mutex = @@ -48,24 +48,30 @@ const ASSUAN_LINELENGTH: usize = 1000; /// In particular, uploading rsa4096 keys fails via scdaemon, with such cards. const APDU_CMD_BYTES_MAX: usize = (ASSUAN_LINELENGTH - 25) / 2; -/// An implementation of the CardTransaction trait that uses GnuPG's scdaemon +/// An implementation of the CardBackend trait that uses GnuPG's scdaemon /// (via GnuPG Agent) to access OpenPGP card devices. -pub struct ScdClient { +pub struct ScdBackend { agent: Agent, card_caps: Option, } -impl ScdClient { +impl From for Box { + fn from(card: ScdBackend) -> Box { + Box::new(card) as Box + } +} + +impl ScdBackend { /// Open a CardApp that uses an scdaemon instance as its backend. /// The specific card with AID `serial` is requested from scdaemon. pub fn open_by_serial( agent: Option, serial: &str, ) -> Result { - let mut card = ScdClient::new(agent, true)?; + let mut card = ScdBackend::new(agent, true)?; card.select_card(serial)?; - ::initialize(&mut card)?; + card.transaction()?.initialize()?; Ok(card) } @@ -77,9 +83,9 @@ impl ScdClient { /// (NOTE: implicitly picking an unspecified card might be a bad idea. /// You might want to avoid using this function.) pub fn open_yolo(agent: Option) -> Result { - let mut card = ScdClient::new(agent, true)?; + let mut card = ScdBackend::new(agent, true)?; - ::initialize(&mut card)?; + card.transaction()?.initialize()?; Ok(card) } @@ -199,19 +205,31 @@ impl ScdClient { } } -impl CardTransaction for ScdClient { +impl CardBackend for ScdBackend { + fn transaction( + &mut self, + ) -> Result, Error> { + Ok(Box::new(ScdTransaction { scd: self })) + } +} + +pub struct ScdTransaction<'a> { + scd: &'a mut ScdBackend, +} + +impl CardTransaction for ScdTransaction<'_> { fn transmit(&mut self, cmd: &[u8], _: usize) -> Result, Error> { log::trace!("SCDC cmd len {}", cmd.len()); let hex = hex::encode(cmd); // (Unwrap is ok here, not having a card_caps is fine) - let ext = if self.card_caps.is_some() - && self.card_caps.unwrap().ext_support() + let ext = if self.card_caps().is_some() + && self.card_caps().unwrap().ext_support() { // If we know about card_caps, and can do extended length we // set "exlen" accordingly ... - format!("--exlen={} ", self.card_caps.unwrap().max_rsp_bytes()) + format!("--exlen={} ", self.card_caps().unwrap().max_rsp_bytes()) } else { // ... otherwise don't send "exlen" to scdaemon "".to_string() @@ -227,11 +245,11 @@ impl CardTransaction for ScdClient { ))); } - self.agent.send(send)?; + self.scd.agent.send(send)?; let mut rt = RT.lock().unwrap(); - while let Some(response) = rt.block_on(self.agent.next()) { + while let Some(response) = rt.block_on(self.scd.agent.next()) { log::debug!("res: {:x?}", response); if response.is_err() { return Err(Error::InternalError(anyhow!( @@ -244,7 +262,7 @@ impl CardTransaction for ScdClient { let res = partial; // drop remaining lines - while let Some(drop) = rt.block_on(self.agent.next()) { + while let Some(drop) = rt.block_on(self.scd.agent.next()) { log::trace!("drop: {:x?}", drop); } @@ -256,11 +274,11 @@ impl CardTransaction for ScdClient { } fn init_card_caps(&mut self, caps: CardCaps) { - self.card_caps = Some(caps); + self.scd.card_caps = Some(caps); } fn card_caps(&self) -> Option<&CardCaps> { - self.card_caps.as_ref() + self.scd.card_caps.as_ref() } /// Return limit for APDU command size via scdaemon (based on Assuan