Implement CardBackend/CardTransaction for scdc backend
This commit is contained in:
parent
1496da6dd5
commit
0c86fcb84a
2 changed files with 38 additions and 22 deletions
|
@ -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)?))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue