use std::fmt;
use std::fmt::Formatter;
use std::convert::TryFrom;
use crate::error::{Result, Error};
#[repr(u8)]
#[derive(Eq, PartialEq, Hash, Debug)]
pub enum IsoCase {
/// No command data. No response data.
///
///
/// - lc is valued to 0.
/// - le is valued to 0.
/// - No data byte is present.
///
///
Case1 = 0,
/// No command data. Expected response data.
///
///
/// - lc is valued to 0.
/// - le is valued from 1 to 256.
/// - No data byte is present.
///
///
Case2Short = 1,
/// Command data. No response data.
///
///
/// - lc is valued from 1 to 255.
/// - le is valued to 0.
/// - lc data bytes are present.
///
///
Case3Short = 2,
/// Command data. Expected response data.
///
///
/// - lc is valued from 1 to 255.
/// - le is valued from 1 to 256.
/// - lc data bytes are present.
///
///
Case4Short = 3,
/// No command data. Expected response data.
///
///
/// - lc is valued to 0.
/// - le is valued from 1 to 65536.
/// - No data byte is present.
///
///
Case2Extended = 4,
/// Command data. No response data.
///
///
/// - lc is valued from 1 to 65536.
/// - le is valued to 0.
/// - lc data bytes are present.
///
///
Case3Extended = 5,
/// Command data. Expected response data.
///
///
/// - lc is valued from 1 to 65535.
/// - le is valued from 1 to 65536.
/// - lc data bytes are present.
///
///
Case4Extended = 6,
}
impl Default for IsoCase {
fn default() -> Self { IsoCase::Case1 }
}
#[repr(u8)]
#[derive(Eq, PartialEq, Hash, Debug)]
pub enum SCardProtocol {
///
/// protocol not defined.
///
Unset = 0x0000,
/// T=0 active protocol.
T0 = 0x0001,
/// T=1 active protocol.
T1 = 0x0002,
/// Raw active protocol. Use with memory type cards.
Raw = 0x0004,
/// T=15 protocol.
T15 = 0x0008,
/// ( | ). IFD (Interface device) determines protocol.
Any = (0x0001 | 0x0002),
}
impl Default for SCardProtocol {
fn default() -> Self { SCardProtocol::Unset }
}
#[derive(Eq, PartialEq, Hash, Debug, Default)]
pub struct APDUCommand {
pub case: IsoCase,
pub protocol: SCardProtocol,
pub cla: u8,
pub ins: u8,
pub p1: u8,
pub p2: u8,
pub data: Option>,
pub lc: i32,
pub le: i32,
}
impl TryFrom for Vec {
type Error = Error;
fn try_from(cmd: APDUCommand) -> Result {
let mut v: Vec = vec![];
//build header
v.push(cmd.cla);
v.push(cmd.ins);
v.push(cmd.p1);
v.push(cmd.p2);
//build body according to case
match cmd.case {
IsoCase::Case1 => {
if cmd.protocol == SCardProtocol::T0 {
v.push(0x00);
}
}
IsoCase::Case2Short => {
v.push(cmd.le as u8);
}
IsoCase::Case3Short => {
v.push(cmd.lc as u8);
v.extend(cmd.data.unwrap());
}
IsoCase::Case4Short => {
v.push(cmd.lc as u8);
v.extend(cmd.data.unwrap());
if cmd.protocol == SCardProtocol::T0 {
v.push(0x00);
} else {
v.push(cmd.le as u8);
}
}
_ => {
return Err(Error::InvalidIsoCase)
}
}
Ok(v)
}
}
//FIXME: Do we want pub fields or constructor?
// We want pub fields! + constructor?
// impl APDUCommand {
// pub fn new(case: IsoCase) -> Self {
// APDUCommand { case, protocol: (), cla: (), ins: (), p1: (), p2: (), data: (), lc: (), le: () }
// }
// }
impl fmt::Display for APDUCommand {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self.case {
IsoCase::Case1 => {
write!(f, "(CASE: 1) cla: {:#X} | ins: {:#X} | p1: {:#X} | p2: {:#X}", self.cla, self.ins, self.p1, self.p2)
}
IsoCase::Case2Short | IsoCase::Case2Extended => {
write!(f, "(CASE: 2) cla: {:#X} | ins: {:#X} | p1: {:#X} | p2: {:#X} | LE: {:#X}", self.cla, self.ins, self.p1, self.p2, self.le)
}
IsoCase::Case3Short | IsoCase::Case3Extended => {
write!(f, "(CASE: 3) cla: {:#X} | ins: {:#X} | p1: {:#X} | p2: {:#X} | LC: {:#X} | data: [ ", self.cla, self.ins, self.p1, self.p2, self.lc)?;
for b in self.data.as_ref().unwrap() {
write!(f, "{:#X} ", b)?;
}
write!(f, "]")
}
IsoCase::Case4Short | IsoCase::Case4Extended => {
write!(f, "(CASE: 4) cla: {:#X} | ins: {:#X} | p1: {:#X} | p2: {:#X} | LC: {:#X} | data: [ ", self.cla, self.ins, self.p1, self.p2, self.lc)?;
for b in self.data.as_ref().unwrap() {
write!(f, "{:#X} ", b)?;
}
write!(f, "] | LE: {:#X}", self.le)
}
}
}
}
#[cfg(test)]
mod tests {
use crate::iso7816_4::apducommand::{APDUCommand, SCardProtocol, IsoCase};
#[test]
fn compare() {
let command1 = APDUCommand{
case: IsoCase::Case4Short,
protocol: SCardProtocol::Unset,
cla: 0x90,
ins: 0xAA,
data: Some(vec![0x01, 0x02, 0x03]),
..Default::default()
};
let command2 = APDUCommand{
case: IsoCase::Case4Short,
protocol: SCardProtocol::Unset,
cla: 0x90,
ins: 0xAA,
data: Some(vec![0x01, 0x02, 0x03]),
..Default::default()
};
assert_eq!(command1, command2);
}
#[test]
fn compare_diff() {
let command1 = APDUCommand{
case: IsoCase::Case4Short,
protocol: SCardProtocol::Unset,
cla: 0x90,
ins: 0xAA,
data: Some(vec![0x01, 0x02, 0x03]),
..Default::default()
};
let command2 = APDUCommand{
case: IsoCase::Case4Short,
protocol: SCardProtocol::Unset,
cla: 0x90,
ins: 0x1A,
data: Some(vec![0x01, 0x02, 0x03]),
..Default::default()
};
assert_ne!(command1, command2);
}
#[test]
fn to_string_case1() {
let command1 = APDUCommand{
case: IsoCase::Case1,
cla: 0x90,
ins: 0x1A,
..Default::default()
};
println!("{}", command1.to_string());
assert_eq!("(CASE: 1) cla: 0x90 | ins: 0x1A | p1: 0x0 | p2: 0x0", command1.to_string());
}
#[test]
fn to_string_case2() {
let command1 = APDUCommand{
case: IsoCase::Case2Short,
cla: 0x90,
ins: 0x1A,
..Default::default()
};
println!("{}", command1.to_string());
assert_eq!("(CASE: 2) cla: 0x90 | ins: 0x1A | p1: 0x0 | p2: 0x0 | LE: 0x0", command1.to_string());
}
#[test]
fn to_string_case3() {
let command1 = APDUCommand{
case: IsoCase::Case3Short,
cla: 0x90,
ins: 0x1A,
data: Some(vec![0x01, 0x02, 0x03]),
..Default::default()
};
println!("{}", command1.to_string());
assert_eq!("(CASE: 3) cla: 0x90 | ins: 0x1A | p1: 0x0 | p2: 0x0 | LC: 0x0 | data: [ 0x1 0x2 0x3 ]", command1.to_string());
}
#[test]
fn to_string_case4() {
let command1 = APDUCommand{
case: IsoCase::Case4Short,
cla: 0x90,
ins: 0x1A,
data: Some(vec![0x01, 0x02, 0x03]),
..Default::default()
};
println!("{}", command1.to_string());
assert_eq!("(CASE: 4) cla: 0x90 | ins: 0x1A | p1: 0x0 | p2: 0x0 | LC: 0x0 | data: [ 0x1 0x2 0x3 ] | LE: 0x0", command1.to_string());
}
}