This commit is contained in:
Kai Jan Kriegel 2022-01-10 21:30:36 +01:00
parent d76554741b
commit 26277f067b
6 changed files with 80 additions and 77 deletions

View File

@ -2,7 +2,7 @@
name = "desfire" name = "desfire"
version = "0.1.0" version = "0.1.0"
authors = ["Kai Jan Kriegel <kai@kjkriegel.de>"] authors = ["Kai Jan Kriegel <kai@kjkriegel.de>"]
edition = "2018" edition = "2021"
license = "MIT OR Apache-2.0" license = "MIT OR Apache-2.0"
description = "Library for interfacing with Mifare Desfire cards." description = "Library for interfacing with Mifare Desfire cards."
repository = "https://gitlab.com/fabinfra/fabaccess/nfc_rs" repository = "https://gitlab.com/fabinfra/fabaccess/nfc_rs"

View File

@ -12,9 +12,11 @@ pub struct CipherKey {
pub key: Box<[u8]>, //TODO: Decide if this should be pub pub key: Box<[u8]>, //TODO: Decide if this should be pub
/// CipherType of Key /// CipherType of Key
#[allow(dead_code)]
cipher: CipherType, cipher: CipherType,
/// KeyVersion of Key /// KeyVersion of Key
#[allow(dead_code)]
key_version: u8, key_version: u8,
} }

View File

@ -1,4 +1,4 @@
use crate::error::{Error, Result}; use crate::error::Result;
use simple_error::simple_error; use simple_error::simple_error;
/// Extracts the the last `n` bytes of a slice. n being the blocksize. /// Extracts the the last `n` bytes of a slice. n being the blocksize.

View File

@ -1,41 +1,42 @@
#[allow(dead_code)]
#[repr(u8)] #[repr(u8)]
pub enum APDUInstructions { pub enum APDUInstructions {
AUTHENTICATE_ISO = 0x1A, AuthenticateIso = 0x1A,
AUTHENTICATE_AES = 0xAA, AuthenticateAes = 0xAA,
CHANGE_KEY_SETTINGS = 0x54, ChangeKeySettings = 0x54,
SET_CONFIGURATION = 0x5C, SetConfiguration = 0x5C,
CHANGE_KEY = 0xC4, ChangeKey = 0xC4,
GET_KEY_VERSION = 0x64, GetKeyVersion = 0x64,
CREATE_APPLICATION = 0xCA, CreateApplication = 0xCA,
DELETE_APPLICATION = 0xDA, DeleteApplication = 0xDA,
GET_APPLICATION_IDS = 0x6A, GetApplicationIds = 0x6A,
FREE_MEMORY = 0x6E, FreeMemory = 0x6E,
GET_DF_NAMES = 0x6D, GetDfNames = 0x6D,
GET_KEY_SETTINGS = 0x45, GetKeySettings = 0x45,
SELECT_APPLICATION = 0x5A, SelectApplication = 0x5A,
FORMAT_PICC = 0xFC, FormatPicc = 0xFC,
GET_VERSION = 0x60, GetVersion = 0x60,
GET_CARD_UID = 0x51, GetCardUid = 0x51,
GET_FILE_IDS = 0x6F, GetFileIds = 0x6F,
GET_FILE_SETTINGS = 0xF5, GetFileSettings = 0xF5,
CHANGE_FILE_SETTINGS = 0x5F, ChangeFileSettings = 0x5F,
CREATE_STDDATAFILE = 0xCD, CreateStddatafile = 0xCD,
CREATE_BACKUPDATAFILE = 0xCB, CreateBackupdatafile = 0xCB,
CREATE_VALUE_FILE = 0xCC, CreateValueFile = 0xCC,
CREATE_LINEAR_RECORD_FILE = 0xC1, CreateLinearRecordFile = 0xC1,
CREATE_CYCLIC_RECORD_FILE = 0xC0, CreateCyclicRecordFile = 0xC0,
DELETE_FILE = 0xDF, DeleteFile = 0xDF,
GET_ISO_FILE_IDS = 0x61, GetIsoFileIds = 0x61,
READ_DATA = 0xBD, ReadData = 0xBD,
WRITE_DATA = 0x3D, WriteData = 0x3D,
GET_VALUE = 0x6C, GetValue = 0x6C,
CREDIT = 0x0C, Credit = 0x0C,
DEBIT = 0xDC, Debit = 0xDC,
LIMITED_CREDIT = 0x1C, LimitedCredit = 0x1C,
WRITE_RECORD = 0x3B, WriteRecord = 0x3B,
READ_RECORDS = 0xBB, ReadRecords = 0xBB,
CLEAR_RECORD_FILE = 0xEB, ClearRecordFile = 0xEB,
COMMIT_TRANSACTION = 0xC7, CommitTransaction = 0xC7,
ABORT_TRANSACTION = 0xA7, AbortTransaction = 0xA7,
CONTINUE = 0xAF, Continue = 0xAF,
} }

View File

@ -1,68 +1,69 @@
#[allow(dead_code)]
#[repr(u16)] #[repr(u16)]
pub enum APDUStatusCodes { pub enum APDUStatusCodes {
/// Successful operation /// Successful operation
OPERATION_OK = 0x9000, OperationOk = 0x9000,
/// No changes done to backup files, CommitTransaction / AbortTransaction not necessary /// No changes done to backup files, CommitTransaction / AbortTransaction not necessary
NO_CHANGES = 0x900C, NoChanges = 0x900C,
/// Insufficient NV-Memory to complete command /// Insufficient NV-Memory to complete command
OUT_OF_EEPROM_ERROR = 0x900E, OutOfEepromError = 0x900E,
/// Command code not supported /// Command code not supported
ILLEGAL_COMMAND_CODE = 0x901C, IllegalCommandCode = 0x901C,
/// CRC or MAC does not match data Padding bytes not valid /// CRC or MAC does not match data Padding bytes not valid
INTEGRITY_ERROR = 0x901E, IntegrityError = 0x901E,
/// Invalid key number specified /// Invalid key number specified
NO_SUCH_KEY = 0x9040, NoSuchKey = 0x9040,
/// Invalid key number specified /// Invalid key number specified
LENGTH_ERROR = 0x907E, LengthError = 0x907E,
/// Current configuration / status does not allow the requested command /// Current configuration / status does not allow the requested command
PERMISSION_DENIED = 0x909D, PermissionDenied = 0x909D,
/// Value of the parameter(s) invalid /// Value of the parameter(s) invalid
PARAMETER_ERROR = 0x909E, ParameterError = 0x909E,
/// Requested AID not present on PICC /// Requested AID not present on PICC
APPLICATION_NOT_FOUND = 0x90A0, ApplicationNotFound = 0x90A0,
/// Unrecoverable error within application, application will be disabled /// Unrecoverable error within application, application will be disabled
APPL_INTEGRITY_ERROR = 0x90A1, ApplIntegrityError = 0x90A1,
/// Current authentication status does not allow the requested command /// Current authentication status does not allow the requested command
AUTHENTICATION_ERROR = 0x90AE, AuthenticationError = 0x90AE,
/// Additional data frame is expected to be sent /// Additional data frame is expected to be sent
ADDITIONAL_FRAME = 0x90AF, AdditionalFrame = 0x90AF,
/// Attempt to read/write data from/to beyond the file\'s/record\'s limits. Attempt to exceed the limits of a value file. /// Attempt to read/write data from/to beyond the file\'s/record\'s limits. Attempt to exceed the limits of a value file.
BOUNDARY_ERROR = 0x90BE, BoundaryError = 0x90BE,
/// Unrecoverable error within PICC, PICC will be disabled /// Unrecoverable error within PICC, PICC will be disabled
PICC_INTEGRITY_ERROR = 0x90C1, PiccIntegrityError = 0x90C1,
/// Previous Command was not fully completed Not all Frames were requested or provided by the PCD /// Previous Command was not fully completed Not all Frames were requested or provided by the PCD
COMMAND_ABORTED = 0x90CA, CommandAborted = 0x90CA,
/// PICC was disabled by an unrecoverable error /// PICC was disabled by an unrecoverable error
PICC_DISABLED_ERROR = 0x90CD, PiccDisabledError = 0x90CD,
/// Number of Applications limited to 28, no additional CreateApplication possible /// Number of Applications limited to 28, no additional CreateApplication possible
COUNT_ERROR = 0x90CE, CountError = 0x90CE,
/// Creation of file/application failed because file/application with same number already exists /// Creation of file/application failed because file/application with same number already exists
DUPLICATE_ERROR = 0x90DE, DuplicateError = 0x90DE,
/// Could not complete NV-write operation due to loss of power, internal backup/rollback mechanism activated /// Could not complete NV-write operation due to loss of power, internal backup/rollback mechanism activated
EEPROM_ERROR = 0x90EE, EepromError = 0x90EE,
/// Specified file number does not exist /// Specified file number does not exist
FILE_NOT_FOUND = 0x90F0, FileNotFound = 0x90F0,
/// Unrecoverable error within file, file will be disabled /// Unrecoverable error within file, file will be disabled
FILE_INTEGRITY_ERROR = 0x90F1, FileIntegrityError = 0x90F1,
} }

View File

@ -9,7 +9,6 @@ use crate::error::{Result, Error};
use crate::error::Error::{InvalidApplicationID, InvalidKeyID, InvalidFileID, NumKeys, InvalidKeyVersion}; use crate::error::Error::{InvalidApplicationID, InvalidKeyID, InvalidFileID, NumKeys, InvalidKeyVersion};
use crate::crypto::util::expand_to_blocksize; use crate::crypto::util::expand_to_blocksize;
use crate::desfire::{FileCommunication, ChangeMasterKeySettings, CreateDeleteFile, FileDirectoryAccess, ChangeMasterKey, ChangeApplicationKey, CryptoOperationsType, FileIdentifiers}; use crate::desfire::{FileCommunication, ChangeMasterKeySettings, CreateDeleteFile, FileDirectoryAccess, ChangeMasterKey, ChangeApplicationKey, CryptoOperationsType, FileIdentifiers};
use crate::iso7816_4::apduresponse::APDUResponse;
use num_traits::FromPrimitive; use num_traits::FromPrimitive;
pub struct Desfire { pub struct Desfire {
@ -33,7 +32,7 @@ impl Desfire {
let cmd_select_app = APDUCommand { let cmd_select_app = APDUCommand {
case: IsoCase::Case4Short, case: IsoCase::Case4Short,
cla: 0x90, cla: 0x90,
ins: APDUInstructions::SELECT_APPLICATION as u8, ins: APDUInstructions::SelectApplication as u8,
data: Option::from(aid.to_le_bytes()[..3].to_vec()), //FIXME: Which byteorder? data: Option::from(aid.to_le_bytes()[..3].to_vec()), //FIXME: Which byteorder?
..Default::default() ..Default::default()
}; };
@ -57,7 +56,7 @@ impl Desfire {
let cmd_challenge_request = APDUCommand { let cmd_challenge_request = APDUCommand {
case: IsoCase::Case4Short, case: IsoCase::Case4Short,
cla: 0x90, cla: 0x90,
ins: APDUInstructions::AUTHENTICATE_ISO as u8, ins: APDUInstructions::AuthenticateIso as u8,
data: Option::from(vec!(key_id)), data: Option::from(vec!(key_id)),
..Default::default() ..Default::default()
}; };
@ -97,7 +96,7 @@ impl Desfire {
let cmd_challenge_response = APDUCommand { let cmd_challenge_response = APDUCommand {
case: IsoCase::Case4Short, case: IsoCase::Case4Short,
cla: 0x90, cla: 0x90,
ins: APDUInstructions::CONTINUE as u8, ins: APDUInstructions::Continue as u8,
data: Some(rnd_ab_enc.clone()), data: Some(rnd_ab_enc.clone()),
..Default::default() ..Default::default()
}; };
@ -144,7 +143,7 @@ impl Desfire {
let cmd_challenge_request = APDUCommand { let cmd_challenge_request = APDUCommand {
case: IsoCase::Case4Short, case: IsoCase::Case4Short,
cla: 0x90, cla: 0x90,
ins: APDUInstructions::AUTHENTICATE_AES as u8, ins: APDUInstructions::AuthenticateAes as u8,
data: Option::from(vec!(key_id)), data: Option::from(vec!(key_id)),
..Default::default() ..Default::default()
}; };
@ -162,7 +161,7 @@ impl Desfire {
let rnd_b_enc = rnd_b_response_body.as_slice(); let rnd_b_enc = rnd_b_response_body.as_slice();
println!("RND_B_ENC: {:x?}", rnd_b_enc); println!("RND_B_ENC: {:x?}", rnd_b_enc);
let mut rnd_b = AES::decrypt(rnd_b_enc, key, vec![0 as u8; 16].as_slice()).unwrap(); let rnd_b = AES::decrypt(rnd_b_enc, key, vec![0 as u8; 16].as_slice()).unwrap();
println!("RND_B: {:x?}", rnd_b); println!("RND_B: {:x?}", rnd_b);
// auth_iv = rnd_b.clone(); // auth_iv = rnd_b.clone();
@ -187,7 +186,7 @@ impl Desfire {
let cmd_challenge_response = APDUCommand { let cmd_challenge_response = APDUCommand {
case: IsoCase::Case4Short, case: IsoCase::Case4Short,
cla: 0x90, cla: 0x90,
ins: APDUInstructions::CONTINUE as u8, ins: APDUInstructions::Continue as u8,
data: Some(rnd_ab_enc.clone()), data: Some(rnd_ab_enc.clone()),
..Default::default() ..Default::default()
}; };
@ -231,7 +230,7 @@ impl Desfire {
let cmd_format = APDUCommand { let cmd_format = APDUCommand {
case: IsoCase::Case2Short, case: IsoCase::Case2Short,
cla: 0x90, cla: 0x90,
ins: APDUInstructions::FORMAT_PICC as u8, ins: APDUInstructions::FormatPicc as u8,
..Default::default() ..Default::default()
}; };
@ -255,7 +254,7 @@ impl Desfire {
let cmd_create_application = APDUCommand { let cmd_create_application = APDUCommand {
case: IsoCase::Case4Short, case: IsoCase::Case4Short,
cla: 0x90, cla: 0x90,
ins: APDUInstructions::CREATE_APPLICATION as u8, ins: APDUInstructions::CreateApplication as u8,
data: Option::from(data), //FIXME: Which byteorder? data: Option::from(data), //FIXME: Which byteorder?
..Default::default() ..Default::default()
}; };
@ -312,7 +311,7 @@ impl Desfire {
let cmd_change_key = APDUCommand { let cmd_change_key = APDUCommand {
case: IsoCase::Case4Short, case: IsoCase::Case4Short,
cla: 0x90, cla: 0x90,
ins: APDUInstructions::CHANGE_KEY as u8, ins: APDUInstructions::ChangeKey as u8,
data: Option::from(data), //FIXME: Which byteorder? data: Option::from(data), //FIXME: Which byteorder?
..Default::default() ..Default::default()
}; };
@ -381,7 +380,7 @@ impl Desfire {
let cmd_change_key = APDUCommand { let cmd_change_key = APDUCommand {
case: IsoCase::Case4Short, case: IsoCase::Case4Short,
cla: 0x90, cla: 0x90,
ins: APDUInstructions::CHANGE_KEY as u8, ins: APDUInstructions::ChangeKey as u8,
data: Option::from(data), //FIXME: Which byteorder? data: Option::from(data), //FIXME: Which byteorder?
..Default::default() ..Default::default()
}; };
@ -407,7 +406,7 @@ impl Desfire {
let cmd_create_file_standard = APDUCommand { let cmd_create_file_standard = APDUCommand {
case: IsoCase::Case4Short, case: IsoCase::Case4Short,
cla: 0x90, cla: 0x90,
ins: APDUInstructions::CREATE_STDDATAFILE as u8, ins: APDUInstructions::CreateStddatafile as u8,
data: Option::from(data), data: Option::from(data),
..Default::default() ..Default::default()
}; };
@ -455,7 +454,7 @@ impl Desfire {
let cmd_write_data = APDUCommand { let cmd_write_data = APDUCommand {
case: IsoCase::Case4Short, case: IsoCase::Case4Short,
cla: 0x90, cla: 0x90,
ins: APDUInstructions::WRITE_DATA as u8, ins: APDUInstructions::WriteData as u8,
data: Option::from(write_buffer), data: Option::from(write_buffer),
..Default::default() ..Default::default()
}; };
@ -500,7 +499,7 @@ impl Desfire {
let cmd_read_data = APDUCommand { let cmd_read_data = APDUCommand {
case: IsoCase::Case4Short, case: IsoCase::Case4Short,
cla: 0x90, cla: 0x90,
ins: APDUInstructions::READ_DATA as u8, ins: APDUInstructions::ReadData as u8,
data: Option::from(send_buffer), data: Option::from(send_buffer),
..Default::default() ..Default::default()
}; };
@ -509,7 +508,7 @@ impl Desfire {
let response = self.card.transmit(cmd_read_data).unwrap(); let response = self.card.transmit(cmd_read_data).unwrap();
println!("RESPONSE: {}", response); println!("RESPONSE: {}", response);
response.check().or_else(|e| return Err(e)); response.check().or_else(|e| return Err(e))?;
// println!("RESPONSE_DATA: {:x?}, WITHOUT_CMAC: {:x?}", response.body.as_ref().unwrap(), response.body.as_ref().unwrap()[..bytes_toread].to_vec()); // println!("RESPONSE_DATA: {:x?}, WITHOUT_CMAC: {:x?}", response.body.as_ref().unwrap(), response.body.as_ref().unwrap()[..bytes_toread].to_vec());
read_buffer.append(&mut response.body.unwrap()[..bytes_toread].to_vec()); read_buffer.append(&mut response.body.unwrap()[..bytes_toread].to_vec());