//! ISO/IEC 7816-4 APDUs, used by CTAP v2 NFC tokens, and all CTAP v1/U2F
//! tokens.
#[derive(Debug, PartialEq, Eq)]
pub enum Error {
/// [`ISO7816RequestAPDU.to_bytes()`]: `data` was too long for the given
/// length form.
DataTooLong,
/// [`ISO7816RequestAPDU.to_bytes()`]: `ne` was too long for the given
/// length form.
NeTooLong,
/// [`push_length_value()`]: The given value cannot be represented in short
/// form.
IntegerOverflow,
/// [`ISO7816ResponseAPDU.from_bytes()`]: response was less than 2 bytes.
ResponseTooShort,
}
/// The L<sub>c</sub> and L<sub>e</sub> form to use for [ISO7816RequestAPDU],
/// per ISO/IEC 7816-4:2005 §5.1.
///
/// All smartcards **must** support short form, and communication in extended
/// form may only be used if
/// [declared in the ATR][crate::nfc::Atr::extended_lc]. Smartcards which do not
/// support extended form must use [ISO7816LengthForm::ShortOnly].
///
/// FIDO tokens on non-NFC transports are **not required** to support short
/// form, and **must** support extended form on **all** transports – so must use
/// [ISO7816LengthForm::ExtendedOnly] for CTAP v1 / U2F commands.
///
/// FIDO tokens on NFC transports are required to support **both** short and
/// extended form.
///
/// Truth table:
///
/// Spec | Short Form | Extended Form
/// -------------- | ------------- | ------------------
/// ISO 7816 | required | optional
/// FIDO / BTLE | not supported | required (CTAP v1)
/// FIDO / NFC | required | required
/// FIDO / USB-HID | not supported | required (CTAP v1)
pub enum ISO7816LengthForm {
/// Only use short form (1 byte length field). This limits
/// [`ISO7816RequestAPDU::data`] to 255 bytes, and
/// [`ISO7816RequestAPDU::ne`] to 256 bytes.
///
/// This mode is always supported by smart cards, but may not be supported
/// by non-NFC FIDO tokens (in CTAPv1 / U2F mode).
ShortOnly,
/// Automatically use extended form (3 bytes length field), if the request
/// requires it, otherwise use short form (1 byte length field). This
/// reduces the size of short messages.
///
/// This mode is recommended only for NFC FIDO tokens. This may not be
/// supported by non-NFC FIDO tokens (in CTAPv1 / U2F mode).
Extended,
/// Always use extended form (3 bytes length field), even if the request is
/// short enough to not require it. This increases the size of short
/// messages.
///
/// This is needed to communicate with non-NFC FIDO tokens in CTAPv1 mode.
/// This may not be supported by non-FIDO smartcards.
ExtendedOnly,
}
/// ISO/IEC 7816-4 command APDU.
#[derive(Debug, Clone)]
pub struct ISO7816RequestAPDU {
/// Class byte (`CLA`, ISO/IEC 7816-4:2005 §5.1.1).
pub cla: u8,
/// Instruction byte (`INS`, ISO/IEC 7816-4:2005 §5.1.2).
pub ins: u8,
/// Parameter byte 1 (`P1`).
pub p1: u8,
/// Parameter byte 2 (`P2`).
pub p2: u8,
/// Optional command data, up to 255 bytes in short form, or up to 65535
/// bytes in extended form.
///
/// Extended form can only be used with smartcards which
/// [declare support for it in the ATR][crate::nfc::Atr::extended_lc].
pub data: Vec<u8>,
/// The maximum expected response length from the card (N<sub>e</sub>), in
/// bytes, up to 256 bytes in short form, or 65535 bytes in extended form.
///
/// Extended form can only be used with smartcards which
/// [declare support for it in the ATR][crate::nfc::Atr::extended_lc].
pub ne: usize,
}
/// Pushes a length `value` of `form` bytes to a mutable buffer, optionally
/// using ISO/IEC 7816-4 extended form:
///
/// * `form = 0`: only `value == 0`
/// * `form = 1`: for short form; `value > 0 && value <= 256`
/// * `form = 2`: for extended form with no 0-prefix (N<sub>e</sub> when
/// N<sub>c</sub> > 0)
/// * `form = 3`: for extended form with 0 prefix
///
/// # Errors
///
/// Returns [`Error::IntegerOverflow`] when `form` is inappropriate for the
/// `value`, or unknown `form`.
fn push_length_value(buf: &mut Vec<u8>, value: usize, form: u8) -> Result<(), Error> {
if form >= 4 {
return Err(Error::IntegerOverflow);
} else if form == 0 {
if value > 0 {
return Err(Error::IntegerOverflow);
}
// do nothing
} else if value == 0 || value > 65536 {
return Err(Error::IntegerOverflow);
} else if form >= 2 {
if form == 3 {
// uint16be prefixed with 0
buf.push(0);
}
buf.extend_from_slice(&(value as u16).to_be_bytes());
} else if value > 256 {
return Err(Error::IntegerOverflow);
} else {
// 256 = 0x00, 1 = 0x01, 255 = 0xFF
buf.push((value & 0xff) as u8);
}
Ok(())
}
impl ISO7816RequestAPDU {
/// Serializes a request APDU into bytes to send to the card.
pub fn to_bytes(&self, form: &ISO7816LengthForm) -> Result<Vec<u8>, Error> {
let extended_form = match form {
ISO7816LengthForm::Extended => self.ne > 256 || self.data.len() > 255,
ISO7816LengthForm::ShortOnly => false,
ISO7816LengthForm::ExtendedOnly => true,
};
if extended_form && self.data.len() > 65535 {
return Err(Error::DataTooLong);
} else if !extended_form {
if self.data.len() > 255 {
return Err(Error::DataTooLong);
}
if self.ne > 256 {
return Err(Error::NeTooLong);
}
}
// §5.1: "short and extended length fields shall not be combined: either
// both of them are short, or both of them are extended".
let lc_len: u8 = if self.data.is_empty() {
0
} else if extended_form {
3
} else {
1
};
let le_len: u8 = if self.ne == 0 {
0
} else if extended_form {
if lc_len == 0 {
// §5.1: extended Le prefixed with 0 if Lc field is absent
3
} else {
2
}
} else {
1
};
let mut buf = Vec::with_capacity(4 + self.data.len() + lc_len as usize + le_len as usize);
buf.push(self.cla);
buf.push(self.ins);
buf.push(self.p1);
buf.push(self.p2);
push_length_value(&mut buf, self.data.len(), lc_len)?;
if !self.data.is_empty() {
buf.extend_from_slice(&self.data);
}
push_length_value(&mut buf, self.ne, le_len)?;
Ok(buf)
}
}
/// ISO/IEC 7816-4 response APDU.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ISO7816ResponseAPDU {
/// Response data field.
pub data: Vec<u8>,
/// Status byte 1 (`SW1`, ISO/IEC 7816-4:2005 §5.1.3).
pub sw1: u8,
/// Status byte 2 (`SW2`, ISO/IEC 7816-4:2005 §5.1.3).
pub sw2: u8,
}
impl TryFrom<&[u8]> for ISO7816ResponseAPDU {
type Error = self::Error;
/// Attempts to deserialize a ISO/IEC 7816-4 response APDU.
fn try_from(raw: &[u8]) -> Result<Self, Error> {
if raw.len() < 2 {
Err(Error::ResponseTooShort)
} else {
Ok(Self {
data: raw[..raw.len() - 2].to_vec(),
sw1: raw[raw.len() - 2],
sw2: raw[raw.len() - 1],
})
}
}
}
impl ISO7816ResponseAPDU {
/// True if the response from the card was a simple "OK" (`90 00`).
pub fn is_ok(&self) -> bool {
self.sw1 == 0x90 && self.sw2 == 0x00
}
/// Non-zero if the card responded that further data bytes are available
/// (`61 XX`), which can be retrieved with [`get_response()`].
pub fn bytes_available(&self) -> usize {
if self.sw1 == 0x61 {
if self.sw2 == 0x00 {
256
} else {
self.sw2.into()
}
} else {
0
}
}
/// **CTAP proprietary**: `true` if the card expects a `NFCCTAP_GETRESPONSE`
/// command to get the actual response (`91 00`).
pub fn ctap_needs_get_response(&self) -> bool {
self.sw1 == 0x91 && self.sw2 == 0x00
}
/// `true` if the card returned a success-like response ([`Self::is_ok()`],
/// [`Self::bytes_available()`] > 0, or
/// [`Self::ctap_needs_get_response()`]).
pub fn is_success(&self) -> bool {
self.is_ok() || self.bytes_available() > 0 || self.ctap_needs_get_response()
}
}
/// Selects an application by DF (directory file) name, of up to 16 bytes.
///
/// Reference: ISO/IEC 7816-4:2005 §5.3.1, §7.1.1.
pub fn select_by_df_name(df: &[u8]) -> ISO7816RequestAPDU {
ISO7816RequestAPDU {
cla: 0x00,
ins: 0xA4, // SELECT
p1: 0x04, // By DF name
p2: 0x00, // First or only occurrence
data: df.to_vec(),
ne: 256,
}
}
/// Requests a chunked response from the previous command that was too long for
/// the previous [`ISO7816RequestAPDU::ne`].
///
/// Reference: ISO/IEC 7816-4:2005 §7.6.1
pub fn get_response(cla: u8, ne: usize) -> ISO7816RequestAPDU {
ISO7816RequestAPDU {
cla,
ins: 0xC0, // GET RESPONSE
p1: 0x00,
p2: 0x00,
data: vec![],
ne,
}
}
pub const EMPTY_RESPONSE: ISO7816ResponseAPDU = ISO7816ResponseAPDU {
data: vec![],
sw1: 0,
sw2: 0,
};
#[cfg(test)]
mod tests {
use super::*;
// Copy of crate::nfc::APPLET_DF, so that the tests don't require NFC.
const APPLET_DF: [u8; 8] = [0xA0, 0x00, 0x00, 0x06, 0x47, 0x2F, 0x00, 0x01];
macro_rules! length_tests {
($($name:ident: $value:expr,)*) => {
$(
#[test]
fn $name() {
let (input, len, expected): (usize, u8, &[u8]) = $value;
let mut b = Vec::with_capacity(expected.len());
assert!(push_length_value(&mut b, input, len).is_ok());
assert_eq!(expected, b);
}
)*
}
}
macro_rules! length_errors {
($($name:ident: $value:expr,)*) => {
$(
#[test]
fn $name() {
let (input, len): (usize, u8) = $value;
let mut b = Vec::with_capacity(0);
let r = push_length_value(&mut b, input, len);
assert_eq!(Error::IntegerOverflow, r.unwrap_err());
assert_eq!(0, b.len());
}
)*
}
}
macro_rules! command_tests {
($($name:ident: $value:expr,)*) => {
$(
#[test]
fn $name() {
let (input, form, expected): (ISO7816RequestAPDU, ISO7816LengthForm, &[u8]) = $value;
let b = input.to_bytes(&form).expect("serialisation error");
assert_eq!(expected, b);
}
)*
}
}
macro_rules! command_errors {
($($name:ident: $value:expr,)*) => {
$(
#[test]
fn $name() {
let (input, form, expected): (ISO7816RequestAPDU, ISO7816LengthForm, Error) = $value;
let err = input.to_bytes(&form).expect_err("expected error");
assert_eq!(expected, err);
}
)*
}
}
macro_rules! response_tests {
($($name:ident: $value:expr,)*) => {
$(
#[test]
fn $name() {
let (input, expected): (&[u8], ISO7816ResponseAPDU) = $value;
let r = ISO7816ResponseAPDU::try_from(input).expect("deserialisation error");
assert_eq!(expected, r);
}
)*
}
}
macro_rules! response_errors {
($($name:ident: $value:expr,)*) => {
$(
#[test]
fn $name() {
let input: &[u8] = $value;
let err = ISO7816ResponseAPDU::try_from(input).expect_err("expected error");
assert_eq!(Error::ResponseTooShort, err);
}
)*
}
}
length_tests! {
length_0: (0, 0, &[]),
length_1_short: (1, 1, &[0x01]),
length_1_long: (1, 2, &[0x00, 0x01]),
length_1_longer: (1, 3, &[0x00, 0x00, 0x01]),
length_255_short: (255, 1, &[0xff]),
length_255_long: (255, 2, &[0x00, 0xff]),
length_255_longer: (255, 3, &[0x00, 0x00, 0xff]),
length_256_short: (256, 1, &[0x00]),
length_256_long: (256, 2, &[0x01, 0x00]),
length_256_longer: (256, 3, &[0x00, 0x01, 0x00]),
length_65535_long: (65535, 2, &[0xff, 0xff]),
length_65535_longer: (65535, 3, &[0x00, 0xff, 0xff]),
length_65536_long: (65536, 2, &[0x00, 0x00]),
length_65536_longer: (65536, 3, &[0x00, 0x00, 0x00]),
}
length_errors! {
length_0_short: (0, 1),
length_0_long: (0, 2),
length_0_longer: (0, 3),
length_1_zero: (1, 0),
length_1_bad_form: (1, 4),
length_255_zero: (255, 0),
length_257_zero: (257, 0),
length_257_short: (257, 1),
length_65535_short: (65535, 1),
length_65536_short: (65536, 1),
length_65537_short: (65537, 1),
length_65537_long: (65537, 2),
length_65537_longer: (65537, 3),
}
command_tests! {
select_none_auto: (
select_by_df_name(&[]),
ISO7816LengthForm::Extended,
&[0x00, 0xa4, 0x04, 0x00, 0x00]),
select_none_short: (
select_by_df_name(&[]),
ISO7816LengthForm::ShortOnly,
&[0x00, 0xa4, 0x04, 0x00, 0x00]),
select_none_extended: (
select_by_df_name(&[]),
ISO7816LengthForm::ExtendedOnly,
&[0x00, 0xa4, 0x04, 0x00, 0x00, 0x01, 0x00]),
select_u2f_applet_auto: (
select_by_df_name(&APPLET_DF),
ISO7816LengthForm::Extended,
&[0x00, 0xa4, 0x04, 0x00, 0x08, 0xA0, 0x00, 0x00, 0x06, 0x47, 0x2F, 0x00, 0x01, 0x00]),
select_u2f_applet_short: (
select_by_df_name(&APPLET_DF),
ISO7816LengthForm::ShortOnly,
&[0x00, 0xa4, 0x04, 0x00, 0x08, 0xA0, 0x00, 0x00, 0x06, 0x47, 0x2F, 0x00, 0x01, 0x00]),
select_u2f_applet_extended: (
select_by_df_name(&APPLET_DF),
ISO7816LengthForm::ExtendedOnly,
&[0x00, 0xa4, 0x04, 0x00, 0x00, 0x00, 0x08, 0xA0, 0x00, 0x00, 0x06, 0x47, 0x2F, 0x00, 0x01, 0x01, 0x00]),
select_255_auto: (
select_by_df_name(&[0xFF; 255]),
ISO7816LengthForm::Extended,
&[0x00, 0xa4, 0x04, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00],
),
select_255_short: (
select_by_df_name(&[0xFF; 255]),
ISO7816LengthForm::ShortOnly,
&[0x00, 0xa4, 0x04, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00],
),
select_255_extended: (
select_by_df_name(&[0xFF; 255]),
ISO7816LengthForm::ExtendedOnly,
&[0x00, 0xa4, 0x04, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00],
),
select_256_auto: (
select_by_df_name(&[0xFF; 256]),
ISO7816LengthForm::Extended,
&[0x00, 0xa4, 0x04, 0x00, 0x00, 0x01, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00],
),
select_256_extended: (
select_by_df_name(&[0xFF; 256]),
ISO7816LengthForm::ExtendedOnly,
&[0x00, 0xa4, 0x04, 0x00, 0x00, 0x01, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00],
),
get_response_auto: (
get_response(0x80, 256),
ISO7816LengthForm::Extended,
&[0x80, 0xc0, 0x00, 0x00, 0x00]),
get_response_short: (
get_response(0x80, 256),
ISO7816LengthForm::ShortOnly,
&[0x80, 0xc0, 0x00, 0x00, 0x00]),
get_response_extended: (
get_response(0x80, 65535),
ISO7816LengthForm::ExtendedOnly,
&[0x80, 0xc0, 0x00, 0x00, 0x00, 0xff, 0xff]),
get_response_extended_auto: (
get_response(0x80, 65535),
ISO7816LengthForm::Extended,
&[0x80, 0xc0, 0x00, 0x00, 0x00, 0xff, 0xff]),
}
command_errors! {
get_response_long_short: (
get_response(0x80, 65535),
ISO7816LengthForm::ShortOnly,
Error::NeTooLong,
),
select_64k: (
select_by_df_name(&[0xFF; 65536]),
ISO7816LengthForm::Extended,
Error::DataTooLong,
),
select_64k_short: (
select_by_df_name(&[0xFF; 65536]),
ISO7816LengthForm::ShortOnly,
Error::DataTooLong,
),
select_64k_extended: (
select_by_df_name(&[0xFF; 65536]),
ISO7816LengthForm::ExtendedOnly,
Error::DataTooLong,
),
select_256_short: (
select_by_df_name(&[0xFF; 256]),
ISO7816LengthForm::ShortOnly,
Error::DataTooLong,
),
}
response_tests! {
response_ok: (
&[0x90, 0x00],
ISO7816ResponseAPDU { sw1: 0x90, sw2: 0x00, data: vec![] },
),
response_data: (
&[0x01, 0x02, 0x03, 0x90, 0x00],
ISO7816ResponseAPDU { sw1: 0x90, sw2: 0x00, data: vec![0x01, 0x02, 0x03] },
),
}
#[test]
fn response_attrs() {
// OK
let mut r = ISO7816ResponseAPDU {
sw1: 0x90,
sw2: 0x00,
data: vec![],
};
assert!(r.is_ok());
assert!(r.is_success());
assert!(!r.ctap_needs_get_response());
assert_eq!(0, r.bytes_available());
// More bytes available
r = ISO7816ResponseAPDU {
sw1: 0x61,
sw2: 0x01,
data: vec![],
};
assert!(!r.is_ok());
assert!(r.is_success());
assert!(!r.ctap_needs_get_response());
assert_eq!(1, r.bytes_available());
r = ISO7816ResponseAPDU {
sw1: 0x61,
sw2: 0x00,
data: vec![],
};
assert!(!r.is_ok());
assert!(r.is_success());
assert!(!r.ctap_needs_get_response());
assert_eq!(256, r.bytes_available());
// Needs NFCCTAP_GETRESPONSE
r = ISO7816ResponseAPDU {
sw1: 0x91,
sw2: 0x00,
data: vec![],
};
assert!(!r.is_ok());
assert!(r.is_success());
assert!(r.ctap_needs_get_response());
assert_eq!(0, r.bytes_available());
// Error
r = ISO7816ResponseAPDU {
sw1: 0x6A,
sw2: 0x82,
data: vec![],
};
assert!(!r.is_ok());
assert!(!r.is_success());
assert!(!r.ctap_needs_get_response());
assert_eq!(0, r.bytes_available());
}
response_errors! {
response_empty: &[],
response_1_byte: &[0x90],
}
}