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()); } }