Implement CardBackend/CardTransaction for scdc backend

This commit is contained in:
Heiko Schaefer 2022-02-18 16:16:55 +01:00
parent 1496da6dd5
commit 0c86fcb84a
No known key found for this signature in database
GPG key ID: 4A849A1904CCBD7D
2 changed files with 38 additions and 22 deletions

View file

@ -11,7 +11,7 @@ use std::collections::BTreeMap;
use openpgp_card::{CardBackend, Error}; use openpgp_card::{CardBackend, Error};
use openpgp_card_pcsc::PcscBackend; use openpgp_card_pcsc::PcscBackend;
use openpgp_card_scdc::ScdClient; use openpgp_card_scdc::ScdBackend;
const SHARE_MODE: Option<ShareMode> = Some(ShareMode::Shared); const SHARE_MODE: Option<ShareMode> = Some(ShareMode::Shared);
@ -97,7 +97,7 @@ impl TestCard {
Self::Pcsc(ident) => { Self::Pcsc(ident) => {
// Attempt to shutdown SCD, if it is running. // Attempt to shutdown SCD, if it is running.
// Ignore any errors that occur during that shutdown attempt. // 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); log::trace!(" Attempt to shutdown scd: {:?}", res);
// Make three attempts to open the card before failing // Make three attempts to open the card before failing
@ -119,9 +119,7 @@ impl TestCard {
Ok(card?) Ok(card?)
} }
Self::Scdc(serial) => { Self::Scdc(serial) => {
unimplemented!(); Ok(Box::new(ScdBackend::open_by_serial(None, serial)?))
println!("open scdc card {}", serial);
// Ok(Box::new(ScdClient::open_by_serial(None, serial)?))
} }
} }
} }

View file

@ -13,7 +13,7 @@ use sequoia_ipc::gnupg::{Agent, Context};
use std::sync::Mutex; use std::sync::Mutex;
use tokio::runtime::Runtime; use tokio::runtime::Runtime;
use openpgp_card::{CardCaps, CardTransaction, Error}; use openpgp_card::{CardBackend, CardCaps, CardTransaction, Error};
lazy_static! { lazy_static! {
static ref RT: Mutex<Runtime> = static ref RT: Mutex<Runtime> =
@ -48,24 +48,30 @@ const ASSUAN_LINELENGTH: usize = 1000;
/// In particular, uploading rsa4096 keys fails via scdaemon, with such cards. /// In particular, uploading rsa4096 keys fails via scdaemon, with such cards.
const APDU_CMD_BYTES_MAX: usize = (ASSUAN_LINELENGTH - 25) / 2; 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. /// (via GnuPG Agent) to access OpenPGP card devices.
pub struct ScdClient { pub struct ScdBackend {
agent: Agent, agent: Agent,
card_caps: Option<CardCaps>, card_caps: Option<CardCaps>,
} }
impl ScdClient { impl From<ScdBackend> for Box<dyn CardBackend> {
fn from(card: ScdBackend) -> Box<dyn CardBackend> {
Box::new(card) as Box<dyn CardBackend>
}
}
impl ScdBackend {
/// Open a CardApp that uses an scdaemon instance as its backend. /// Open a CardApp that uses an scdaemon instance as its backend.
/// The specific card with AID `serial` is requested from scdaemon. /// The specific card with AID `serial` is requested from scdaemon.
pub fn open_by_serial( pub fn open_by_serial(
agent: Option<Agent>, agent: Option<Agent>,
serial: &str, serial: &str,
) -> Result<Self, Error> { ) -> Result<Self, Error> {
let mut card = ScdClient::new(agent, true)?; let mut card = ScdBackend::new(agent, true)?;
card.select_card(serial)?; card.select_card(serial)?;
<dyn CardTransaction>::initialize(&mut card)?; card.transaction()?.initialize()?;
Ok(card) Ok(card)
} }
@ -77,9 +83,9 @@ impl ScdClient {
/// (NOTE: implicitly picking an unspecified card might be a bad idea. /// (NOTE: implicitly picking an unspecified card might be a bad idea.
/// You might want to avoid using this function.) /// You might want to avoid using this function.)
pub fn open_yolo(agent: Option<Agent>) -> Result<Self, Error> { pub fn open_yolo(agent: Option<Agent>) -> Result<Self, Error> {
let mut card = ScdClient::new(agent, true)?; let mut card = ScdBackend::new(agent, true)?;
<dyn CardTransaction>::initialize(&mut card)?; card.transaction()?.initialize()?;
Ok(card) Ok(card)
} }
@ -199,19 +205,31 @@ impl ScdClient {
} }
} }
impl CardTransaction for ScdClient { impl CardBackend for ScdBackend {
fn transaction(
&mut self,
) -> Result<Box<dyn CardTransaction + Send + Sync + '_>, 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<Vec<u8>, Error> { fn transmit(&mut self, cmd: &[u8], _: usize) -> Result<Vec<u8>, Error> {
log::trace!("SCDC cmd len {}", cmd.len()); log::trace!("SCDC cmd len {}", cmd.len());
let hex = hex::encode(cmd); let hex = hex::encode(cmd);
// (Unwrap is ok here, not having a card_caps is fine) // (Unwrap is ok here, not having a card_caps is fine)
let ext = if self.card_caps.is_some() let ext = if self.card_caps().is_some()
&& self.card_caps.unwrap().ext_support() && self.card_caps().unwrap().ext_support()
{ {
// If we know about card_caps, and can do extended length we // If we know about card_caps, and can do extended length we
// set "exlen" accordingly ... // set "exlen" accordingly ...
format!("--exlen={} ", self.card_caps.unwrap().max_rsp_bytes()) format!("--exlen={} ", self.card_caps().unwrap().max_rsp_bytes())
} else { } else {
// ... otherwise don't send "exlen" to scdaemon // ... otherwise don't send "exlen" to scdaemon
"".to_string() "".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(); 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); log::debug!("res: {:x?}", response);
if response.is_err() { if response.is_err() {
return Err(Error::InternalError(anyhow!( return Err(Error::InternalError(anyhow!(
@ -244,7 +262,7 @@ impl CardTransaction for ScdClient {
let res = partial; let res = partial;
// drop remaining lines // 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); log::trace!("drop: {:x?}", drop);
} }
@ -256,11 +274,11 @@ impl CardTransaction for ScdClient {
} }
fn init_card_caps(&mut self, caps: CardCaps) { 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> { 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 /// Return limit for APDU command size via scdaemon (based on Assuan