mirror of
https://gitlab.com/fabinfra/fabaccess/nfc_rs.git
synced 2025-05-10 03:23:26 +02:00
added basic iso7816_4 card support
This commit is contained in:
parent
32cb8e6639
commit
8c112d519d
99
src/error.rs
99
src/error.rs
@ -1,14 +1,33 @@
|
||||
use std::io;
|
||||
use std::fmt;
|
||||
use hex::FromHexError;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
IO(io::Error),
|
||||
Boxed(Box<dyn std::error::Error>),
|
||||
Simple(simple_error::SimpleError),
|
||||
BlockModeError(block_modes::BlockModeError),
|
||||
InvalidKeyIvLength(block_modes::InvalidKeyIvLength),
|
||||
FromHexError(hex::FromHexError)
|
||||
FromHexError(hex::FromHexError),
|
||||
PadError(block_modes::block_padding::PadError),
|
||||
InvalidStatusWord,
|
||||
IllegalCommandCode,
|
||||
IntegrityError,
|
||||
NoSuchKey,
|
||||
LengthError,
|
||||
PermissionDenied,
|
||||
ParameterError,
|
||||
AuthenticationDelay,
|
||||
AuthenticationError,
|
||||
BoundaryError,
|
||||
CommandAborted,
|
||||
DuplicateError,
|
||||
FileNotFound,
|
||||
InvalidApplicationID,
|
||||
InvalidAPDUResponse,
|
||||
InvalidKeyID,
|
||||
InvalidPICCChallenge,
|
||||
InvalidFileID,
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
@ -16,19 +35,79 @@ impl fmt::Display for Error {
|
||||
match self {
|
||||
Error::IO(e) => {
|
||||
write!(f, "IO Error: {}", e)
|
||||
},
|
||||
}
|
||||
Error::Boxed(e) => {
|
||||
write!(f, "{}", e)
|
||||
},
|
||||
}
|
||||
Error::Simple(e) => {
|
||||
write!(f, "Generic Error: {}", e)
|
||||
}
|
||||
Error::BlockModeError(e) => {
|
||||
write!(f, "CBC Error: {}", e)
|
||||
},
|
||||
}
|
||||
Error::InvalidKeyIvLength(e) => {
|
||||
write!(f, "CBC InvalidKeyIvLength Error: {}", e)
|
||||
}
|
||||
Error::FromHexError(e) => {
|
||||
write!(f, "Hex conversion Error: {}", e)
|
||||
}
|
||||
Error::PadError(e) => {
|
||||
write!(f, "Padding to blocksize failed: {:#?}", e)
|
||||
}
|
||||
Error::InvalidStatusWord => {
|
||||
write!(f, "Invalid APDU Statusword")
|
||||
}
|
||||
Error::IllegalCommandCode => {
|
||||
write!(f, "SW2: Command code not supported.")
|
||||
}
|
||||
Error::IntegrityError => {
|
||||
write!(f, "SW2: CRC or MAC does not match data. Paddingbytes not valid.")
|
||||
}
|
||||
Error::NoSuchKey => {
|
||||
write!(f, "SW2: Invalid key number specified.")
|
||||
}
|
||||
Error::LengthError => {
|
||||
write!(f, "SW2: Length of command string invalid.")
|
||||
}
|
||||
Error::PermissionDenied => {
|
||||
write!(f, "SW2: Current configuration / status does not allow the requested command.")
|
||||
}
|
||||
Error::ParameterError => {
|
||||
write!(f, "SW2: Value of the parameter(s) invalid.")
|
||||
}
|
||||
Error::AuthenticationDelay => {
|
||||
write!(f, "SW2: Currently not allowed to authenticate. Keeptrying until full delay is spent.")
|
||||
}
|
||||
Error::AuthenticationError => {
|
||||
write!(f, "SW2: Current authentication status does not allow the requested command.")
|
||||
}
|
||||
Error::BoundaryError => {
|
||||
write!(f, "SW2: Attempt to read/write data from/to beyond the file’s/record’s limits. Attempt to exceed the limits of a value file.")
|
||||
}
|
||||
Error::CommandAborted => {
|
||||
write!(f, "SW2: Previous Command was not fully completed.Not all Frames were requested or provided by the PCD.")
|
||||
}
|
||||
Error::DuplicateError => {
|
||||
write!(f, "SW2: Creation of file/application failed because file/application with same number already exists")
|
||||
}
|
||||
Error::FileNotFound => {
|
||||
write!(f, "SW2: Specified file number does not exist.")
|
||||
}
|
||||
Error::InvalidApplicationID => {
|
||||
write!(f, "Application ID was larger then 24 bit")
|
||||
}
|
||||
Error::InvalidAPDUResponse => {
|
||||
write!(f, "Got malformed APDUResponse")
|
||||
}
|
||||
Error::InvalidKeyID => {
|
||||
write!(f, "Invalid KeyID: Was larger then 0x0E")
|
||||
}
|
||||
Error::InvalidPICCChallenge => {
|
||||
write!(f, "Authentication failed, PICC Challenge is invalid.")
|
||||
}
|
||||
Error::InvalidFileID => {
|
||||
write!(f, "Invalid FileID: Was larger then 0x20")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -39,6 +118,10 @@ impl From<io::Error> for Error {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<simple_error::SimpleError> for Error {
|
||||
fn from(e: simple_error::SimpleError) -> Error { Error::Simple(e) }
|
||||
}
|
||||
|
||||
impl From<block_modes::BlockModeError> for Error {
|
||||
fn from(e: block_modes::BlockModeError) -> Error {
|
||||
Error::BlockModeError(e)
|
||||
@ -52,7 +135,11 @@ impl From<block_modes::InvalidKeyIvLength> for Error {
|
||||
}
|
||||
|
||||
impl From<hex::FromHexError> for Error {
|
||||
fn from(e: hex::FromHexError) -> Error { Error::FromHexError(e)}
|
||||
fn from(e: hex::FromHexError) -> Error { Error::FromHexError(e) }
|
||||
}
|
||||
|
||||
impl From<block_modes::block_padding::PadError> for Error {
|
||||
fn from(e: block_modes::block_padding::PadError) -> Error { Error::PadError(e) }
|
||||
}
|
||||
|
||||
pub(crate) type Result<T> = std::result::Result<T, Error>;
|
||||
|
261
src/iso7816_4/apducommand.rs
Normal file
261
src/iso7816_4/apducommand.rs
Normal file
@ -0,0 +1,261 @@
|
||||
use std::fmt;
|
||||
use std::fmt::Formatter;
|
||||
|
||||
#[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,
|
||||
}
|
||||
//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());
|
||||
}
|
||||
}
|
100
src/iso7816_4/apduresponse.rs
Normal file
100
src/iso7816_4/apduresponse.rs
Normal file
@ -0,0 +1,100 @@
|
||||
use crate::iso7816_4::apdustatuswords::{APDUStatusWord, APDUStatusWord2};
|
||||
use crate::error::{Result};
|
||||
use num_traits::FromPrimitive;
|
||||
use crate::error::Error::{InvalidStatusWord, IllegalCommandCode, IntegrityError, NoSuchKey, LengthError, PermissionDenied, ParameterError, AuthenticationDelay, AuthenticationError, BoundaryError, CommandAborted, DuplicateError, FileNotFound};
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Eq, PartialEq, Hash, Debug)]
|
||||
pub struct APDUResponse {
|
||||
pub body: Vec<u8>,
|
||||
pub sw1: u8,
|
||||
pub sw2: u8,
|
||||
}
|
||||
|
||||
impl APDUResponse {
|
||||
pub fn body(&self) -> &Vec<u8> {
|
||||
&self.body
|
||||
}
|
||||
}
|
||||
|
||||
impl From<APDUResponse> for Vec<u8> {
|
||||
fn from(resp: APDUResponse) -> Self {
|
||||
let mut v: Vec<u8> = vec![];
|
||||
v.extend(resp.body);
|
||||
v.push(resp.sw1);
|
||||
v.push(resp.sw2);
|
||||
v
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for APDUResponse {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self.body.is_empty() {
|
||||
true => {
|
||||
write!(f, "SW1: {:#X} | SW2: 0x{:#X}", self.sw1, self.sw2)
|
||||
},
|
||||
false => {
|
||||
write!(f, "SW1: {:#X} | SW2: {:#X} | Body: {:#X?}", self.sw1, self.sw2, self.body)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl APDUResponse {
|
||||
pub fn new(raw: &[u8]) -> Self {
|
||||
APDUResponse {
|
||||
body: raw[..raw.len() - 1].to_vec(),
|
||||
sw1: raw[raw.len() - 2],
|
||||
sw2: raw[raw.len() - 3]
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_statusword(&self) -> Result<APDUStatusWord> {
|
||||
match self.sw1 {
|
||||
0x61 => Ok(APDUStatusWord::DataReady),
|
||||
0x62 => Ok(APDUStatusWord::StorageNotChanged),
|
||||
0x63 => {
|
||||
if (self.sw2 & 0xF0) == 0xC0 { Ok(APDUStatusWord::CounterReached) } else { Ok(APDUStatusWord::StorageChanged) }
|
||||
},
|
||||
0x64 => Ok(APDUStatusWord::ExecutionErrorWithoutChange),
|
||||
0x65 => Ok(APDUStatusWord::ExecutionErrorWithChange),
|
||||
0x6C => Ok(APDUStatusWord::InvalidLe),
|
||||
_ => FromPrimitive::from_u16(((self.sw1 as u16) << 8) | self.sw2 as u16).ok_or(InvalidStatusWord)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_data_length(&self) -> u8 {
|
||||
self.sw2
|
||||
}
|
||||
|
||||
pub fn get_counter(&self) -> u8 {
|
||||
self.sw2 & 0x0F
|
||||
}
|
||||
|
||||
pub fn get_correct_le(&self) -> u8 {
|
||||
self.sw2
|
||||
}
|
||||
|
||||
pub fn check(&self) -> Result<()> {
|
||||
if self.sw1 == 0x91 {
|
||||
return match FromPrimitive::from_u8(self.sw2) {
|
||||
Some(APDUStatusWord2::OperationOk) => { Ok(()) }
|
||||
Some(APDUStatusWord2::NoChanges) => { Ok(()) }
|
||||
Some(APDUStatusWord2::IllegalCommandCode) => {Err(IllegalCommandCode)}
|
||||
Some(APDUStatusWord2::IntegrityError) => {Err(IntegrityError)}
|
||||
Some(APDUStatusWord2::NoSuchKey) => {Err(NoSuchKey)}
|
||||
Some(APDUStatusWord2::LengthError) => {Err(LengthError)}
|
||||
Some(APDUStatusWord2::PermissionDenied) => {Err(PermissionDenied)}
|
||||
Some(APDUStatusWord2::ParameterError) => {Err(ParameterError)}
|
||||
Some(APDUStatusWord2::AuthenticationDelay) => {Err(AuthenticationDelay)}
|
||||
Some(APDUStatusWord2::AuthenticationError) => {Err(AuthenticationError)}
|
||||
Some(APDUStatusWord2::AdditionalFrame) => {Ok(())}
|
||||
Some(APDUStatusWord2::BoundaryError) => {Err(BoundaryError)}
|
||||
Some(APDUStatusWord2::CommandAborted) => {Err(CommandAborted)}
|
||||
Some(APDUStatusWord2::DuplicateError) => {Err(DuplicateError)}
|
||||
Some(APDUStatusWord2::FileNotFound) => {Err(FileNotFound)}
|
||||
None => { Err(InvalidStatusWord)}
|
||||
}
|
||||
} else { Err(InvalidStatusWord) }
|
||||
}
|
||||
}
|
153
src/iso7816_4/apdustatuswords.rs
Normal file
153
src/iso7816_4/apdustatuswords.rs
Normal file
@ -0,0 +1,153 @@
|
||||
use num_derive::FromPrimitive;
|
||||
|
||||
#[derive(FromPrimitive)]
|
||||
#[repr(u16)]
|
||||
pub enum APDUStatusWord {
|
||||
/// Kommando erfolgreich ausgefhrt. xx Datenbytes knnen mit dem GET RESPONSE-Kommando abgeholt werden. Statuswort zur Steuerung des T=0-Protokolls
|
||||
DataReady = 0x6100,
|
||||
|
||||
/// Die zurckgegebenen Daten knnen fehlerhaft sein.
|
||||
FaultyData = 0x6281,
|
||||
|
||||
/// Da das Dateiende vorher erreicht wurde, konnten nur weniger als Le Bytes gelesen werden.
|
||||
UnexpectedEndOfFile = 0x6282,
|
||||
|
||||
/// Die ausgewhlte Datei ist gesperrt (englisch invalidated, wrtlich ungltig).
|
||||
InvalidatedFile = 0x6283,
|
||||
|
||||
/// Die File Control Information (FCI) ist inkonform zu ISO 7816-4.
|
||||
FciNotConform = 0x6284,
|
||||
|
||||
/// Warnung; Zustand des nichtflchtigen Speichers nicht verndert
|
||||
StorageNotChanged = 0x6200,
|
||||
|
||||
/// Zhler hat den Wert x erreicht (die genaue Bedeutung ist vom Kommando abhngig)
|
||||
CounterReached = 0x63C0,
|
||||
|
||||
/// Warnung; Zustand des nichtflchtigen Speichers verndert
|
||||
StorageChanged = 0x6300,
|
||||
|
||||
/// Ausfhrungsfehler; Zustand des nichtflchtigen Speichers nicht verndert
|
||||
ExecutionErrorWithoutChange = 0x6400,
|
||||
|
||||
/// Speicherfehler
|
||||
MemoryError = 0x6581,
|
||||
|
||||
/// Ausfhrungsfehler; Zustand des nichtflchtigen Speichers verndert
|
||||
ExecutionErrorWithChange = 0x6500,
|
||||
|
||||
/// Befehlslnge (Lc) oder erwartete Antwortlnge (Le) falsch
|
||||
InvalidLcLe = 0x6700,
|
||||
|
||||
/// Funktionen im Class-Byte werden nicht untersttzt
|
||||
ClassFeatureNotSupported = 0x6800,
|
||||
|
||||
/// Logische Kanle werden nicht untersttzt
|
||||
LogicChannelNotSupported = 0x6881,
|
||||
|
||||
/// Secure Messaging wird nicht untersttzt
|
||||
SecureMessagingNotSupported = 0x6882,
|
||||
|
||||
/// Kommando nicht erlaubt
|
||||
CommandNotAllowed = 0x6900,
|
||||
|
||||
/// Kommando inkompatibel zur Dateistruktur
|
||||
CommandIncompatible = 0x6981,
|
||||
|
||||
/// Sicherheitszustand nicht erfllt
|
||||
SafetyStatusNotFulfilled = 0x6982,
|
||||
|
||||
/// Authentisierungsmethode ist gesperrt
|
||||
AuthenticationMethodLocked = 0x6983,
|
||||
|
||||
/// Referenzierte Daten sind gesperrt
|
||||
ReferencedFileLocked = 0x6984,
|
||||
|
||||
/// Nutzungsbedingungen sind nicht erfllt
|
||||
TermsOfServiceNotFulfilled = 0x6985,
|
||||
|
||||
/// Kommando nicht erlaubt (kein EF selektiert)
|
||||
CommandNotAllowedNoEfSelected = 0x6986,
|
||||
|
||||
/// Erwartete Secure-Messaging-Objekte nicht gefunden
|
||||
ExpectedSecureMessagingObjectsNotFound = 0x6987,
|
||||
|
||||
/// Secure-Messaging-Datenobjekte sind inkorrekt
|
||||
InvalidSecureMessagingObjects = 0x6988,
|
||||
|
||||
/// Falsche Parameter P1/P2
|
||||
WrongParameters = 0x6A00,
|
||||
|
||||
/// Falsche Daten
|
||||
WrongData = 0x6A80,
|
||||
|
||||
/// Funktion wird nicht untersttzt
|
||||
FeatureNotSupported = 0x6A81,
|
||||
|
||||
/// Datei wurde nicht gefunden
|
||||
FileNotFound = 0x6A82,
|
||||
|
||||
/// Datensatz (engl. record) der Datei nicht gefunden
|
||||
RecordNotFound = 0x6A83,
|
||||
|
||||
/// Nicht gengend Speicherplatz in der Datei
|
||||
InsufficientSpace = 0x6A84,
|
||||
|
||||
/// Lc nicht konsistent mit der TLV-Struktur
|
||||
LcTlvInconsistent = 0x6A85,
|
||||
|
||||
/// Inkorrekte Parameter P1/P2
|
||||
IncorrectParameters = 0x6A86,
|
||||
|
||||
/// Lc inkonsistent mit P1/P2
|
||||
LcParametersInconsistent = 0x6A87,
|
||||
|
||||
/// Referenzierte Daten nicht gefunden
|
||||
ReferencedFileNotFound = 0x6A88,
|
||||
|
||||
/// Parameter P1/P2 falsch
|
||||
WrongParameters2 = 0x6B00,
|
||||
|
||||
|
||||
/// Falsche Lnge Le; xx gibt die korrekte Lnge an Statuswort zur Steuerung des T=0-Protokolls
|
||||
InvalidLe = 0x6C00,
|
||||
|
||||
|
||||
/// Das Kommando (INS) wird nicht untersttzt
|
||||
InstructionNotSupported = 0x6D00,
|
||||
|
||||
|
||||
/// Die Kommandoklasse (CLA) wird nicht untersttzt
|
||||
ClassNotSupported = 0x6E00,
|
||||
|
||||
|
||||
/// Kommando wurde mit unbekanntem Fehler abgebrochen
|
||||
UnknownError = 0x6F00,
|
||||
|
||||
/// Kommando erfolgreich ausgefhrt
|
||||
SUCCESS = 0x9000,
|
||||
|
||||
/// OK
|
||||
OK = 0x9100,
|
||||
|
||||
}
|
||||
|
||||
#[derive(FromPrimitive)]
|
||||
#[repr(u8)]
|
||||
pub enum APDUStatusWord2 {
|
||||
OperationOk = 0x00,
|
||||
NoChanges = 0x0C,
|
||||
IllegalCommandCode = 0x1C,
|
||||
IntegrityError = 0x1E,
|
||||
NoSuchKey = 0x40,
|
||||
LengthError = 0x7E,
|
||||
PermissionDenied = 0x9D,
|
||||
ParameterError = 0x9E,
|
||||
AuthenticationDelay = 0xAD,
|
||||
AuthenticationError = 0xAE,
|
||||
AdditionalFrame = 0xAF,
|
||||
BoundaryError = 0xBE,
|
||||
CommandAborted = 0xCA,
|
||||
DuplicateError = 0xDE,
|
||||
FileNotFound = 0xF0,
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
pub mod apducommand;
|
||||
pub mod apduresponse;
|
||||
pub mod apdustatuswords;
|
23
src/lib.rs
23
src/lib.rs
@ -1,4 +1,27 @@
|
||||
use crate::iso7816_4::apduresponse::APDUResponse;
|
||||
use crate::iso7816_4::apducommand::APDUCommand;
|
||||
use error::Result;
|
||||
|
||||
pub mod crypto;
|
||||
pub mod iso7816_4;
|
||||
pub mod desfire;
|
||||
pub mod error;
|
||||
|
||||
pub trait Card {
|
||||
/// <summary>
|
||||
/// Connect to Smartcard
|
||||
/// </summary>
|
||||
fn connect(&self);
|
||||
|
||||
/// <summary>
|
||||
/// Disconnect from Smartcard
|
||||
/// </summary>
|
||||
fn disconnect(&self);
|
||||
|
||||
/// <summary>
|
||||
/// Transmit APDU Command to Smartcard
|
||||
/// </summary>
|
||||
/// <param name="apdu_cmd">Application Protocol Data Unit Command - ISO 7816</param>
|
||||
/// <returns>Application Protocol Data Unit Response - ISO 7816</returns>
|
||||
fn transmit(&self, apdu_cmd: APDUCommand) -> Result<APDUResponse>;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user