use std::ffi::{CStr, CString};
use std::io::Write;
use std::marker::PhantomData;
use std::thread::sleep;
use std::time::Duration;
use apdu::core::HandleError;
use pcsc::{Card, Protocols, Scope, ShareMode, MAX_BUFFER_SIZE};
#[cfg(feature = "tracing")]
use tracing::{debug, info};
use crate::nfc::HandlerInCtx;
#[cfg(not(feature = "tracing"))]
macro_rules! debug {
($($t: tt)*) => {};
}
#[cfg(not(feature = "tracing"))]
macro_rules! info {
($($t: tt)*) => {};
}
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("Error occurred while communicating with PC/SC: {0}")]
PcscError(#[from] pcsc::Error),
#[error("Reader not found on PC/SC service")]
ReaderNotFound,
}
pub(crate) type Result<T> = std::result::Result<T, Error>;
pub struct Context<'a> {
ctx: pcsc::Context,
_lifetime: PhantomData<&'a ()>,
}
impl<'a> Context<'a> {
pub fn try_new() -> Result<Self> {
Ok(Self {
ctx: pcsc::Context::establish(Scope::User).map_err(Error::PcscError)?,
_lifetime: Default::default(),
})
}
pub fn open<'b>(&self) -> Result<Device<'b>> {
let mut buf = [0u8; 2048];
Ok(Device::new(
self.ctx
.list_readers(&mut buf)
.map_err(Error::PcscError)?
.next()
.ok_or(Error::ReaderNotFound)?,
))
}
}
pub struct Device<'a> {
reader: Box<CString>,
_lifetime: PhantomData<&'a ()>,
}
impl<'a> Device<'a> {
fn new(reader: &CStr) -> Self {
debug!("Using device: {}", reader.to_str().unwrap_or_default());
Self {
reader: Box::new(reader.to_owned()),
_lifetime: Default::default(),
}
}
pub fn connect(&self, ctx: Context) -> Result<PcscCard<'a>> {
debug!("Waiting for a card");
loop {
match ctx
.ctx
.connect(&self.reader, ShareMode::Shared, Protocols::ANY)
{
Ok(card) => {
debug!("Connected to your card");
return Ok(PcscCard::new(card));
}
Err(e) => match e {
pcsc::Error::NoSmartcard => {
info!("Still waiting for your card...");
sleep(Duration::from_secs(1));
continue;
}
_ => return Err(Error::PcscError(e)),
},
}
}
}
}
pub struct PcscCard<'a> {
card: Card,
_lifetime: PhantomData<&'a ()>,
}
impl<'a> PcscCard<'a> {
fn new(card: Card) -> Self {
Self {
card,
_lifetime: Default::default(),
}
}
pub fn transmit(&self, tx: &[u8]) -> Result<Vec<u8>> {
debug!("TX: {}", hex::encode(tx));
let mut rx = [0u8; MAX_BUFFER_SIZE];
let rx = self.card.transmit(tx, &mut rx).map_err(Error::PcscError)?;
debug!("RX: {}", hex::encode(rx));
Ok(Vec::from(rx))
}
}
type Ctx = ();
impl<'a> HandlerInCtx<Ctx> for PcscCard<'a> {
fn handle_in_ctx(
&self,
_: Ctx,
command: &[u8],
mut response: &mut [u8],
) -> std::result::Result<usize, HandleError> {
let tx = Vec::from(command);
let rx = self.transmit(&tx).unwrap();
let len = rx.len();
if response.len() < len {
return Err(HandleError::NotEnoughBuffer(len));
}
match response.write(&rx) {
Ok(size) => Ok(size),
Err(e) => Err(HandleError::Nfc(Box::new(e))),
}
}
}