A CardClient instance can now contain a CardCaps (which specifies how commands should be sent to the card).
Add max_rsp_bytes field to CardCaps.
This commit is contained in:
parent
9d93570d9f
commit
dbf2e9e3fb
4 changed files with 103 additions and 167 deletions
|
@ -16,7 +16,7 @@ use crate::card_app::CardApp;
|
|||
use crate::errors::{OcErrorStatus, OpenpgpCardError, SmartcardError};
|
||||
use crate::{CardBase, CardCaps, CardClient, CardClientBox};
|
||||
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
#[derive(Clone, Copy, PartialEq, Debug)]
|
||||
pub(crate) enum Le {
|
||||
None,
|
||||
Short,
|
||||
|
@ -31,37 +31,34 @@ pub(crate) fn send_command(
|
|||
card_client: &mut CardClientBox,
|
||||
cmd: Command,
|
||||
expect_reply: bool,
|
||||
card_caps: Option<&CardCaps>,
|
||||
) -> Result<Response, OpenpgpCardError> {
|
||||
let mut resp = Response::try_from(send_command_low_level(
|
||||
card_client,
|
||||
cmd,
|
||||
expect_reply,
|
||||
card_caps,
|
||||
)?)?;
|
||||
|
||||
while resp.status()[0] == 0x61 {
|
||||
// More data is available for this command from the card
|
||||
|
||||
log::trace!(" response was truncated, getting more data");
|
||||
log::debug!(" response was truncated, getting more data");
|
||||
|
||||
// Get additional data
|
||||
let next = Response::try_from(send_command_low_level(
|
||||
card_client,
|
||||
commands::get_response(),
|
||||
expect_reply,
|
||||
card_caps,
|
||||
)?)?;
|
||||
|
||||
// FIXME: first check for 0x61xx or 0x9000?
|
||||
log::trace!(" appending {} bytes to response", next.raw_data().len());
|
||||
log::debug!(" appending {} bytes to response", next.raw_data().len());
|
||||
|
||||
// Append new data to resp.data and overwrite status.
|
||||
resp.raw_mut_data().extend_from_slice(next.raw_data());
|
||||
resp.set_status(next.status());
|
||||
}
|
||||
|
||||
log::trace!(" final response len: {}", resp.raw_data().len());
|
||||
log::debug!(" final response len: {}", resp.raw_data().len());
|
||||
|
||||
Ok(resp)
|
||||
}
|
||||
|
@ -75,29 +72,39 @@ fn send_command_low_level(
|
|||
card_client: &mut CardClientBox,
|
||||
cmd: Command,
|
||||
expect_reply: bool,
|
||||
card_caps: Option<&CardCaps>,
|
||||
) -> Result<Vec<u8>, OpenpgpCardError> {
|
||||
let (ext_support, chaining_support, max_cmd_bytes) =
|
||||
if let Some(caps) = card_caps {
|
||||
log::trace!("found card caps data!");
|
||||
let (ext_support, chaining_support, mut max_cmd_bytes, max_rsp_bytes) =
|
||||
if let Some(caps) = card_client.get_caps() {
|
||||
log::debug!("found card caps data!");
|
||||
|
||||
(
|
||||
caps.ext_support,
|
||||
caps.chaining_support,
|
||||
caps.max_cmd_bytes as usize,
|
||||
caps.max_rsp_bytes as usize,
|
||||
)
|
||||
} else {
|
||||
log::trace!("found NO card caps data!");
|
||||
log::debug!("found NO card caps data!");
|
||||
|
||||
// default settings
|
||||
(false, false, 255)
|
||||
(false, false, 255, 255)
|
||||
};
|
||||
|
||||
log::trace!(
|
||||
"ext le/lc {}, chaining {}, command chunk size {}",
|
||||
// If the CardClient implementation has an inherent limit for the cmd
|
||||
// size, take that limit into account.
|
||||
// (E.g. when using scdaemon as a CardClient backend, there is a
|
||||
// limitation to 1000 bytes length for Assuan commands, which
|
||||
// translates to maximum command length of a bit under 500 bytes)
|
||||
if let Some(max_cardclient_cmd_bytes) = card_client.max_cmd_len() {
|
||||
max_cmd_bytes = usize::min(max_cmd_bytes, max_cardclient_cmd_bytes);
|
||||
}
|
||||
|
||||
log::debug!(
|
||||
"ext le/lc {}, chaining {}, max cmd {}, max rsp {}",
|
||||
ext_support,
|
||||
chaining_support,
|
||||
max_cmd_bytes
|
||||
max_cmd_bytes,
|
||||
max_rsp_bytes
|
||||
);
|
||||
|
||||
// Set Le to 'long', if we're using an extended chunk size.
|
||||
|
@ -111,18 +118,20 @@ fn send_command_low_level(
|
|||
_ => Le::Short,
|
||||
};
|
||||
|
||||
log::trace!(" -> full APDU command: {:x?}", cmd);
|
||||
log::debug!(" -> full APDU command: {:x?}", cmd);
|
||||
|
||||
let buf_size = if !ext_support {
|
||||
let buf_size = if !ext_support || ext == Le::Short {
|
||||
pcsc::MAX_BUFFER_SIZE
|
||||
} else {
|
||||
pcsc::MAX_BUFFER_SIZE_EXTENDED
|
||||
max_rsp_bytes
|
||||
};
|
||||
|
||||
log::trace!("buf_size {}", buf_size);
|
||||
|
||||
if chaining_support && !cmd.data.is_empty() {
|
||||
// Send command in chained mode
|
||||
|
||||
log::trace!("chained command mode");
|
||||
log::debug!("chained command mode");
|
||||
|
||||
// Break up payload into chunks that fit into one command, each
|
||||
let chunks: Vec<_> = cmd.data.chunks(max_cmd_bytes).collect();
|
||||
|
@ -138,11 +147,12 @@ fn send_command_low_level(
|
|||
let serialized = partial
|
||||
.serialize(ext)
|
||||
.map_err(OpenpgpCardError::InternalError)?;
|
||||
log::trace!(" -> chunked APDU command: {:x?}", &serialized);
|
||||
|
||||
log::debug!(" -> chunked APDU command: {:x?}", &serialized);
|
||||
|
||||
let resp = card_client.transmit(&serialized, buf_size)?;
|
||||
|
||||
log::trace!(" <- APDU chunk response: {:x?}", &resp);
|
||||
log::debug!(" <- APDU chunk response: {:x?}", &resp);
|
||||
|
||||
if resp.len() < 2 {
|
||||
return Err(OcErrorStatus::ResponseLength(resp.len()).into());
|
||||
|
@ -175,7 +185,7 @@ fn send_command_low_level(
|
|||
|
||||
let resp = card_client.transmit(&serialized, buf_size)?;
|
||||
|
||||
log::trace!(" <- APDU response: {:x?}", resp);
|
||||
log::debug!(" <- APDU response: {:x?}", resp);
|
||||
|
||||
Ok(resp)
|
||||
}
|
||||
|
@ -183,11 +193,15 @@ fn send_command_low_level(
|
|||
|
||||
pub struct PcscClient {
|
||||
card: Card,
|
||||
card_caps: Option<CardCaps>,
|
||||
}
|
||||
|
||||
impl PcscClient {
|
||||
fn new(card: Card) -> Self {
|
||||
Self { card }
|
||||
Self {
|
||||
card,
|
||||
card_caps: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn list_cards() -> Result<Vec<PcscClient>> {
|
||||
|
@ -204,7 +218,7 @@ impl PcscClient {
|
|||
/// data").
|
||||
pub fn open(card: Card) -> Result<CardBase, OpenpgpCardError> {
|
||||
let card_client = PcscClient::new(card);
|
||||
let mut ccb = Box::new(card_client) as CardClientBox;
|
||||
let ccb = Box::new(card_client) as CardClientBox;
|
||||
|
||||
let mut ca = CardApp::new(ccb);
|
||||
let resp = ca.select()?;
|
||||
|
@ -228,8 +242,16 @@ impl CardClient for PcscClient {
|
|||
)))
|
||||
})?;
|
||||
|
||||
log::trace!(" <- APDU response: {:x?}", resp);
|
||||
log::debug!(" <- APDU response: {:x?}", resp);
|
||||
|
||||
Ok(resp.to_vec())
|
||||
}
|
||||
|
||||
fn init_caps(&mut self, caps: CardCaps) {
|
||||
self.card_caps = Some(caps);
|
||||
}
|
||||
|
||||
fn get_caps(&self) -> Option<&CardCaps> {
|
||||
self.card_caps.as_ref()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,23 +31,22 @@ use crate::{
|
|||
|
||||
pub struct CardApp {
|
||||
card_client: CardClientBox,
|
||||
card_caps: Option<CardCaps>,
|
||||
}
|
||||
|
||||
impl CardApp {
|
||||
pub fn new(card_client: CardClientBox) -> Self {
|
||||
Self {
|
||||
card_client,
|
||||
card_caps: None,
|
||||
}
|
||||
Self { card_client }
|
||||
}
|
||||
|
||||
pub(crate) fn take_card(self) -> CardClientBox {
|
||||
self.card_client
|
||||
}
|
||||
|
||||
/// Read capabilities from the card, and set them in the CardApp
|
||||
pub fn init_caps(self, ard: &Tlv) -> Result<Self> {
|
||||
/// Read capabilities from the card, and set them in the CardApp.
|
||||
///
|
||||
/// Also initializes the underlying CardClient with the caps - some
|
||||
/// implementations may need this information.
|
||||
pub fn init_caps(&mut self, ard: &Tlv) -> Result<()> {
|
||||
// Determine chaining/extended length support from card
|
||||
// metadata and cache this information in CardApp (as a
|
||||
// CardCaps)
|
||||
|
@ -62,44 +61,36 @@ impl CardApp {
|
|||
}
|
||||
}
|
||||
|
||||
let max_cmd_bytes = if let Ok(Some(eli)) =
|
||||
let (max_cmd_bytes, max_rsp_bytes) = if let Ok(Some(eli)) =
|
||||
CardApp::get_extended_length_information(&ard)
|
||||
{
|
||||
eli.max_command_bytes
|
||||
(eli.max_command_bytes, eli.max_response_bytes)
|
||||
} else {
|
||||
255
|
||||
(255, 255)
|
||||
};
|
||||
|
||||
let caps = CardCaps {
|
||||
ext_support,
|
||||
chaining_support,
|
||||
max_cmd_bytes,
|
||||
max_rsp_bytes,
|
||||
};
|
||||
|
||||
Ok(self.set_caps(caps))
|
||||
}
|
||||
self.card_client.init_caps(caps);
|
||||
|
||||
pub fn set_caps(self, card_caps: CardCaps) -> Self {
|
||||
Self {
|
||||
card_client: self.card_client,
|
||||
card_caps: Some(card_caps),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn card(&mut self) -> &mut CardClientBox {
|
||||
&mut self.card_client
|
||||
}
|
||||
|
||||
pub fn card_caps(&self) -> Option<&CardCaps> {
|
||||
self.card_caps.as_ref()
|
||||
}
|
||||
|
||||
// --- select ---
|
||||
|
||||
/// "Select" the OpenPGP card application
|
||||
pub fn select(&mut self) -> Result<Response, OpenpgpCardError> {
|
||||
let select_openpgp = commands::select_openpgp();
|
||||
apdu::send_command(&mut self.card_client, select_openpgp, false, None)
|
||||
apdu::send_command(&mut self.card_client, select_openpgp, false)
|
||||
}
|
||||
|
||||
// --- application data ---
|
||||
|
@ -110,10 +101,10 @@ impl CardApp {
|
|||
/// (the data is stored in the OpenPGPCard object).
|
||||
pub fn get_app_data(&mut self) -> Result<Tlv> {
|
||||
let ad = commands::get_application_data();
|
||||
let resp = apdu::send_command(&mut self.card_client, ad, true, None)?;
|
||||
let resp = apdu::send_command(&mut self.card_client, ad, true)?;
|
||||
let entry = TlvEntry::from(resp.data()?, true)?;
|
||||
|
||||
log::trace!(" App data TlvEntry: {:x?}", entry);
|
||||
log::debug!(" App data TlvEntry: {:x?}", entry);
|
||||
|
||||
Ok(Tlv(Tag::from([0x6E]), entry))
|
||||
}
|
||||
|
@ -283,7 +274,6 @@ impl CardApp {
|
|||
&mut self.card_client,
|
||||
commands::get_url(),
|
||||
true,
|
||||
self.card_caps.as_ref(),
|
||||
)?;
|
||||
|
||||
Ok(String::from_utf8_lossy(resp.data()?).to_string())
|
||||
|
@ -292,12 +282,7 @@ impl CardApp {
|
|||
// --- cardholder related data (65) ---
|
||||
pub fn get_cardholder_related_data(&mut self) -> Result<CardHolder> {
|
||||
let crd = commands::cardholder_related_data();
|
||||
let resp = apdu::send_command(
|
||||
&mut self.card_client,
|
||||
crd,
|
||||
true,
|
||||
self.card_caps.as_ref(),
|
||||
)?;
|
||||
let resp = apdu::send_command(&mut self.card_client, crd, true)?;
|
||||
resp.check_ok()?;
|
||||
|
||||
CardHolder::try_from(resp.data()?)
|
||||
|
@ -306,12 +291,7 @@ impl CardApp {
|
|||
// --- security support template (7a) ---
|
||||
pub fn get_security_support_template(&mut self) -> Result<Tlv> {
|
||||
let sst = commands::get_security_support_template();
|
||||
let resp = apdu::send_command(
|
||||
&mut self.card_client,
|
||||
sst,
|
||||
true,
|
||||
self.card_caps.as_ref(),
|
||||
)?;
|
||||
let resp = apdu::send_command(&mut self.card_client, sst, true)?;
|
||||
resp.check_ok()?;
|
||||
|
||||
Tlv::try_from(resp.data()?)
|
||||
|
@ -323,7 +303,6 @@ impl CardApp {
|
|||
&mut self.card_client,
|
||||
commands::get_algo_list(),
|
||||
true,
|
||||
self.card_caps.as_ref(),
|
||||
)?;
|
||||
resp.check_ok()?;
|
||||
|
||||
|
@ -339,12 +318,8 @@ impl CardApp {
|
|||
// [apdu 00 20 00 81 08 40 40 40 40 40 40 40 40]
|
||||
for _ in 0..4 {
|
||||
let verify = commands::verify_pw1_81([0x40; 8].to_vec());
|
||||
let resp = apdu::send_command(
|
||||
&mut self.card_client,
|
||||
verify,
|
||||
false,
|
||||
self.card_caps.as_ref(),
|
||||
)?;
|
||||
let resp =
|
||||
apdu::send_command(&mut self.card_client, verify, false)?;
|
||||
if !(resp.status() == [0x69, 0x82]
|
||||
|| resp.status() == [0x69, 0x83])
|
||||
{
|
||||
|
@ -356,12 +331,8 @@ impl CardApp {
|
|||
// [apdu 00 20 00 83 08 40 40 40 40 40 40 40 40]
|
||||
for _ in 0..4 {
|
||||
let verify = commands::verify_pw3([0x40; 8].to_vec());
|
||||
let resp = apdu::send_command(
|
||||
&mut self.card_client,
|
||||
verify,
|
||||
false,
|
||||
self.card_caps.as_ref(),
|
||||
)?;
|
||||
let resp =
|
||||
apdu::send_command(&mut self.card_client, verify, false)?;
|
||||
|
||||
if !(resp.status() == [0x69, 0x82]
|
||||
|| resp.status() == [0x69, 0x83])
|
||||
|
@ -372,22 +343,12 @@ impl CardApp {
|
|||
|
||||
// terminate_df [apdu 00 e6 00 00]
|
||||
let term = commands::terminate_df();
|
||||
let resp = apdu::send_command(
|
||||
&mut self.card_client,
|
||||
term,
|
||||
false,
|
||||
self.card_caps.as_ref(),
|
||||
)?;
|
||||
let resp = apdu::send_command(&mut self.card_client, term, false)?;
|
||||
resp.check_ok()?;
|
||||
|
||||
// activate_file [apdu 00 44 00 00]
|
||||
let act = commands::activate_file();
|
||||
let resp = apdu::send_command(
|
||||
&mut self.card_client,
|
||||
act,
|
||||
false,
|
||||
self.card_caps.as_ref(),
|
||||
)?;
|
||||
let resp = apdu::send_command(&mut self.card_client, act, false)?;
|
||||
resp.check_ok()?;
|
||||
|
||||
// FIXME: does the connection need to be re-opened on some cards,
|
||||
|
@ -403,22 +364,12 @@ impl CardApp {
|
|||
assert!(pin.len() >= 6); // FIXME: Err
|
||||
|
||||
let verify = commands::verify_pw1_81(pin.as_bytes().to_vec());
|
||||
apdu::send_command(
|
||||
&mut self.card_client,
|
||||
verify,
|
||||
false,
|
||||
self.card_caps.as_ref(),
|
||||
)
|
||||
apdu::send_command(&mut self.card_client, verify, false)
|
||||
}
|
||||
|
||||
pub fn check_pw1(&mut self) -> Result<Response, OpenpgpCardError> {
|
||||
let verify = commands::verify_pw1_82(vec![]);
|
||||
apdu::send_command(
|
||||
&mut self.card_client,
|
||||
verify,
|
||||
false,
|
||||
self.card_caps.as_ref(),
|
||||
)
|
||||
apdu::send_command(&mut self.card_client, verify, false)
|
||||
}
|
||||
|
||||
pub fn verify_pw1(
|
||||
|
@ -428,22 +379,12 @@ impl CardApp {
|
|||
assert!(pin.len() >= 6); // FIXME: Err
|
||||
|
||||
let verify = commands::verify_pw1_82(pin.as_bytes().to_vec());
|
||||
apdu::send_command(
|
||||
&mut self.card_client,
|
||||
verify,
|
||||
false,
|
||||
self.card_caps.as_ref(),
|
||||
)
|
||||
apdu::send_command(&mut self.card_client, verify, false)
|
||||
}
|
||||
|
||||
pub fn check_pw3(&mut self) -> Result<Response, OpenpgpCardError> {
|
||||
let verify = commands::verify_pw3(vec![]);
|
||||
apdu::send_command(
|
||||
&mut self.card_client,
|
||||
verify,
|
||||
false,
|
||||
self.card_caps.as_ref(),
|
||||
)
|
||||
apdu::send_command(&mut self.card_client, verify, false)
|
||||
}
|
||||
|
||||
pub fn verify_pw3(
|
||||
|
@ -453,12 +394,7 @@ impl CardApp {
|
|||
assert!(pin.len() >= 8); // FIXME: Err
|
||||
|
||||
let verify = commands::verify_pw3(pin.as_bytes().to_vec());
|
||||
apdu::send_command(
|
||||
&mut self.card_client,
|
||||
verify,
|
||||
false,
|
||||
self.card_caps.as_ref(),
|
||||
)
|
||||
apdu::send_command(&mut self.card_client, verify, false)
|
||||
}
|
||||
|
||||
// --- decrypt ---
|
||||
|
@ -499,12 +435,7 @@ impl CardApp {
|
|||
) -> Result<Vec<u8>, OpenpgpCardError> {
|
||||
// The OpenPGP card is already connected and PW1 82 has been verified
|
||||
let dec_cmd = commands::decryption(data);
|
||||
let resp = apdu::send_command(
|
||||
&mut self.card_client,
|
||||
dec_cmd,
|
||||
true,
|
||||
self.card_caps.as_ref(),
|
||||
)?;
|
||||
let resp = apdu::send_command(&mut self.card_client, dec_cmd, true)?;
|
||||
resp.check_ok()?;
|
||||
|
||||
Ok(resp.data().map(|d| d.to_vec())?)
|
||||
|
@ -557,12 +488,7 @@ impl CardApp {
|
|||
) -> Result<Vec<u8>, OpenpgpCardError> {
|
||||
let dec_cmd = commands::signature(data);
|
||||
|
||||
let resp = apdu::send_command(
|
||||
&mut self.card_client,
|
||||
dec_cmd,
|
||||
true,
|
||||
self.card_caps.as_ref(),
|
||||
)?;
|
||||
let resp = apdu::send_command(&mut self.card_client, dec_cmd, true)?;
|
||||
|
||||
Ok(resp.data().map(|d| d.to_vec())?)
|
||||
}
|
||||
|
@ -574,12 +500,7 @@ impl CardApp {
|
|||
name: &str,
|
||||
) -> Result<Response, OpenpgpCardError> {
|
||||
let put_name = commands::put_name(name.as_bytes().to_vec());
|
||||
apdu::send_command(
|
||||
&mut self.card_client,
|
||||
put_name,
|
||||
false,
|
||||
self.card_caps.as_ref(),
|
||||
)
|
||||
apdu::send_command(&mut self.card_client, put_name, false)
|
||||
}
|
||||
|
||||
pub fn set_lang(
|
||||
|
@ -587,22 +508,12 @@ impl CardApp {
|
|||
lang: &str,
|
||||
) -> Result<Response, OpenpgpCardError> {
|
||||
let put_lang = commands::put_lang(lang.as_bytes().to_vec());
|
||||
apdu::send_command(
|
||||
self.card_client.borrow_mut(),
|
||||
put_lang,
|
||||
false,
|
||||
self.card_caps.as_ref(),
|
||||
)
|
||||
apdu::send_command(self.card_client.borrow_mut(), put_lang, false)
|
||||
}
|
||||
|
||||
pub fn set_sex(&mut self, sex: Sex) -> Result<Response, OpenpgpCardError> {
|
||||
let put_sex = commands::put_sex(sex.as_u8());
|
||||
apdu::send_command(
|
||||
self.card_client.borrow_mut(),
|
||||
put_sex,
|
||||
false,
|
||||
self.card_caps.as_ref(),
|
||||
)
|
||||
apdu::send_command(self.card_client.borrow_mut(), put_sex, false)
|
||||
}
|
||||
|
||||
pub fn set_url(
|
||||
|
@ -610,12 +521,7 @@ impl CardApp {
|
|||
url: &str,
|
||||
) -> Result<Response, OpenpgpCardError> {
|
||||
let put_url = commands::put_url(url.as_bytes().to_vec());
|
||||
apdu::send_command(
|
||||
&mut self.card_client,
|
||||
put_url,
|
||||
false,
|
||||
self.card_caps.as_ref(),
|
||||
)
|
||||
apdu::send_command(&mut self.card_client, put_url, false)
|
||||
}
|
||||
|
||||
pub fn upload_key(
|
||||
|
|
|
@ -67,8 +67,6 @@ pub(crate) fn upload_key(
|
|||
}
|
||||
};
|
||||
|
||||
let caps: Option<CardCaps> = card_app.card_caps().copied();
|
||||
|
||||
copy_key_to_card(
|
||||
card_app.card(),
|
||||
key_type,
|
||||
|
@ -76,7 +74,6 @@ pub(crate) fn upload_key(
|
|||
key.get_fp(),
|
||||
algo_cmd,
|
||||
key_cmd,
|
||||
caps.as_ref(),
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
|
@ -375,7 +372,6 @@ fn copy_key_to_card(
|
|||
fp: Vec<u8>,
|
||||
algo_cmd: Command,
|
||||
key_cmd: Command,
|
||||
card_caps: Option<&CardCaps>,
|
||||
) -> Result<(), OpenpgpCardError> {
|
||||
let fp_cmd = commands::put_data(&[key_type.get_fingerprint_put_tag()], fp);
|
||||
|
||||
|
@ -395,11 +391,11 @@ fn copy_key_to_card(
|
|||
|
||||
// FIXME: Only write algo attributes to the card if "extended
|
||||
// capabilities" show that they are changeable!
|
||||
apdu::send_command(card_client, algo_cmd, false, card_caps)?.check_ok()?;
|
||||
apdu::send_command(card_client, algo_cmd, false)?.check_ok()?;
|
||||
|
||||
apdu::send_command(card_client, key_cmd, false, card_caps)?.check_ok()?;
|
||||
apdu::send_command(card_client, fp_cmd, false, card_caps)?.check_ok()?;
|
||||
apdu::send_command(card_client, time_cmd, false, card_caps)?.check_ok()?;
|
||||
apdu::send_command(card_client, key_cmd, false)?.check_ok()?;
|
||||
apdu::send_command(card_client, fp_cmd, false)?.check_ok()?;
|
||||
apdu::send_command(card_client, time_cmd, false)?.check_ok()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -27,17 +27,27 @@ mod tlv;
|
|||
|
||||
pub trait CardClient {
|
||||
fn transmit(&mut self, cmd: &[u8], buf_size: usize) -> Result<Vec<u8>>;
|
||||
fn init_caps(&mut self, caps: CardCaps);
|
||||
fn get_caps(&self) -> Option<&CardCaps>;
|
||||
|
||||
/// If a CardClient implementation introduces an inherent limit for
|
||||
/// maximum number of bytes per command, this fn can indicate that
|
||||
/// limit by returning `Some(max_cmd_len)`.
|
||||
fn max_cmd_len(&self) -> Option<usize> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub type CardClientBox = Box<dyn CardClient + Send + Sync>;
|
||||
|
||||
/// Information about the capabilities of the card.
|
||||
/// (feature configuration from card metadata)
|
||||
#[derive(Clone, Copy)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct CardCaps {
|
||||
pub(crate) ext_support: bool,
|
||||
pub(crate) chaining_support: bool,
|
||||
pub(crate) max_cmd_bytes: u16,
|
||||
pub ext_support: bool,
|
||||
pub chaining_support: bool,
|
||||
pub max_cmd_bytes: u16,
|
||||
pub max_rsp_bytes: u16,
|
||||
}
|
||||
|
||||
impl CardCaps {
|
||||
|
@ -45,11 +55,13 @@ impl CardCaps {
|
|||
ext_support: bool,
|
||||
chaining_support: bool,
|
||||
max_cmd_bytes: u16,
|
||||
max_rsp_bytes: u16,
|
||||
) -> CardCaps {
|
||||
Self {
|
||||
ext_support,
|
||||
chaining_support,
|
||||
max_cmd_bytes,
|
||||
max_rsp_bytes,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -324,7 +336,7 @@ impl CardBase {
|
|||
|
||||
let ard = card_app.get_app_data()?;
|
||||
|
||||
card_app = card_app.init_caps(&ard)?;
|
||||
card_app.init_caps(&ard)?;
|
||||
|
||||
Ok(Self { card_app, ard })
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue