libs.nfc_rs/src/iso7816_4/apducommand.rs
2021-12-19 17:15:18 +01:00

305 lines
9.6 KiB
Rust

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 {
/// <summary>No command data. No response data.</summary>
/// <remarks>
/// <list type="bullet">
/// <item><description>lc is valued to 0.</description></item>
/// <item><description>le is valued to 0.</description></item>
/// <item><description>No data byte is present.</description></item>
/// </list>
/// </remarks>
Case1 = 0,
/// <summary>No command data. Expected response data.</summary>
/// <remarks>
/// <list type="bullet">
/// <item><description>lc is valued to 0.</description></item>
/// <item><description>le is valued from 1 to 256.</description></item>
/// <item><description>No data byte is present.</description></item>
/// </list>
/// </remarks>
Case2Short = 1,
/// <summary>Command data. No response data.</summary>
/// <remarks>
/// <list type="bullet">
/// <item><description>lc is valued from 1 to 255.</description></item>
/// <item><description>le is valued to 0.</description></item>
/// <item><description>lc data bytes are present.</description></item>
/// </list>
/// </remarks>
Case3Short = 2,
/// <summary>Command data. Expected response data.</summary>
/// <remarks>
/// <list type="bullet">
/// <item><description>lc is valued from 1 to 255.</description></item>
/// <item><description>le is valued from 1 to 256.</description></item>
/// <item><description>lc data bytes are present.</description></item>
/// </list>
/// </remarks>
Case4Short = 3,
/// <summary>No command data. Expected response data.</summary>
/// <remarks>
/// <list type="bullet">
/// <item><description>lc is valued to 0.</description></item>
/// <item><description>le is valued from 1 to 65536.</description></item>
/// <item><description>No data byte is present.</description></item>
/// </list>
/// </remarks>
Case2Extended = 4,
/// <summary>Command data. No response data.</summary>
/// <remarks>
/// <list type="bullet">
/// <item><description>lc is valued from 1 to 65536.</description></item>
/// <item><description>le is valued to 0.</description></item>
/// <item><description>lc data bytes are present.</description></item>
/// </list>
/// </remarks>
Case3Extended = 5,
/// <summary>Command data. Expected response data.</summary>
/// <remarks>
/// <list type="bullet">
/// <item><description>lc is valued from 1 to 65535.</description></item>
/// <item><description>le is valued from 1 to 65536.</description></item>
/// <item><description>lc data bytes are present.</description></item>
/// </list>
/// </remarks>
Case4Extended = 6,
}
impl Default for IsoCase {
fn default() -> Self { IsoCase::Case1 }
}
#[repr(u8)]
#[derive(Eq, PartialEq, Hash, Debug)]
pub enum SCardProtocol {
/// <summary>
/// protocol not defined.
/// </summary>
Unset = 0x0000,
/// <summary>T=0 active protocol.</summary>
T0 = 0x0001,
/// <summary>T=1 active protocol.</summary>
T1 = 0x0002,
/// <summary>Raw active protocol. Use with memory type cards.</summary>
Raw = 0x0004,
/// <summary>T=15 protocol.</summary>
T15 = 0x0008,
/// <summary>(<see cref="SCardProtocol.T0" /> | <see cref="SCardProtocol.T1" />). IFD (Interface device) determines protocol.</summary>
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<Vec<u8>>,
pub lc: i32,
pub le: i32,
}
impl TryFrom<APDUCommand> for Vec<u8> {
type Error = Error;
fn try_from(cmd: APDUCommand) -> Result<Self> {
let mut v: Vec<u8> = 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());
}
}