mirror of
https://gitlab.com/fabinfra/fabaccess/nfc_rs.git
synced 2025-03-12 06:41:46 +01:00
broke out some parts of the command generation
This commit is contained in:
parent
26277f067b
commit
9edce5ac35
@ -13,11 +13,11 @@ pub struct CipherKey {
|
|||||||
|
|
||||||
/// CipherType of Key
|
/// CipherType of Key
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
cipher: CipherType,
|
pub cipher: CipherType,
|
||||||
|
|
||||||
/// KeyVersion of Key
|
/// KeyVersion of Key
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
key_version: u8,
|
pub key_version: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CipherKey {
|
impl CipherKey {
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
use std::fs::read;
|
||||||
|
use std::usize::MAX;
|
||||||
use crate::{Card, error};
|
use crate::{Card, error};
|
||||||
use crate::iso7816_4::apducommand::{APDUCommand, IsoCase};
|
use crate::iso7816_4::apducommand::{APDUCommand, IsoCase};
|
||||||
use crate::desfire::apduinstructions::APDUInstructions;
|
use crate::desfire::apduinstructions::APDUInstructions;
|
||||||
@ -6,13 +8,15 @@ use crate::crypto::cipher::aes::AES;
|
|||||||
use crate::crypto::cipher::Cipher;
|
use crate::crypto::cipher::Cipher;
|
||||||
use crate::crypto::util;
|
use crate::crypto::util;
|
||||||
use crate::error::{Result, Error};
|
use crate::error::{Result, Error};
|
||||||
use crate::error::Error::{InvalidApplicationID, InvalidKeyID, InvalidFileID, NumKeys, InvalidKeyVersion};
|
use crate::error::Error::{InvalidApplicationID, InvalidKeyID, InvalidFileID, NumKeys, InvalidKeyVersion, InvalidLength};
|
||||||
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 num_traits::FromPrimitive;
|
use num_traits::FromPrimitive;
|
||||||
|
|
||||||
|
pub const MAX_BYTES_PER_TRANSACTION: usize = 47;
|
||||||
|
|
||||||
pub struct Desfire {
|
pub struct Desfire {
|
||||||
pub card: Box<dyn Card>,
|
pub card: Option<Box<dyn Card>>,
|
||||||
pub session_key: Option<Vec<u8>>,
|
pub session_key: Option<Vec<u8>>,
|
||||||
pub cbc_iv: Option<Vec<u8>>,
|
pub cbc_iv: Option<Vec<u8>>,
|
||||||
}
|
}
|
||||||
@ -20,26 +24,31 @@ pub struct Desfire {
|
|||||||
impl Desfire {
|
impl Desfire {
|
||||||
// Desfire commands
|
// Desfire commands
|
||||||
|
|
||||||
|
|
||||||
|
pub fn select_application_cmd(&self, aid: u32) -> Result<APDUCommand> {
|
||||||
|
if aid > 0xFFFFFF {
|
||||||
|
return Err(InvalidApplicationID);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(APDUCommand {
|
||||||
|
case: IsoCase::Case4Short,
|
||||||
|
cla: 0x90,
|
||||||
|
ins: APDUInstructions::SelectApplication as u8,
|
||||||
|
data: Option::from(aid.to_le_bytes()[..3].to_vec()),
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Select Application by ApplicationID (AID)
|
/// Select Application by ApplicationID (AID)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="aid">3 Byte AID</param>
|
/// <param name="aid">3 Byte AID</param>
|
||||||
pub fn select_application(&self, aid: u32) -> Result<()> {
|
pub fn select_application(&self, aid: u32) -> Result<()> {
|
||||||
if aid > 0xFFFFFF {
|
let cmd_select_app = self.select_application_cmd(aid)?;
|
||||||
return Err(InvalidApplicationID);
|
|
||||||
}
|
|
||||||
|
|
||||||
let cmd_select_app = APDUCommand {
|
let response = self.card.as_ref().unwrap().transmit(cmd_select_app)?;
|
||||||
case: IsoCase::Case4Short,
|
|
||||||
cla: 0x90,
|
|
||||||
ins: APDUInstructions::SelectApplication as u8,
|
|
||||||
data: Option::from(aid.to_le_bytes()[..3].to_vec()), //FIXME: Which byteorder?
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
let response = self.card.transmit(cmd_select_app).unwrap();
|
response.check()
|
||||||
|
|
||||||
response.check().map_err(|e| e)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -61,7 +70,7 @@ impl Desfire {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
let response = self.card.transmit(cmd_challenge_request).unwrap();
|
let response = self.card.as_ref().unwrap().transmit(cmd_challenge_request).unwrap();
|
||||||
|
|
||||||
match response.check() {
|
match response.check() {
|
||||||
Ok(_) => {}
|
Ok(_) => {}
|
||||||
@ -101,7 +110,7 @@ impl Desfire {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
let response = self.card.transmit(cmd_challenge_response).unwrap();
|
let response = self.card.as_ref().unwrap().transmit(cmd_challenge_response).unwrap();
|
||||||
|
|
||||||
match response.check() {
|
match response.check() {
|
||||||
Ok(_) => {}
|
Ok(_) => {}
|
||||||
@ -129,13 +138,8 @@ impl Desfire {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// Request challenge for aes auth from card
|
||||||
/// Authenticate to PICC, with ISO Authenticate for AES Key
|
pub fn authenticate_iso_aes_challenge_cmd(&mut self, key_id: u8) -> Result<APDUCommand> {
|
||||||
/// </summary>
|
|
||||||
/// <param name="key_id">0x01 - 0x0D</param>
|
|
||||||
/// <param name="key">Array of 8/16 Bytes</param>
|
|
||||||
/// <param name="rndA">!!! WARNING For Testing only !!!</param>
|
|
||||||
pub fn authenticate_iso_aes(&mut self, key_id: u8, key: &[u8], rnd_a: Option<[u8; 16]>) -> Result<()> {
|
|
||||||
if key_id > 0x0E {
|
if key_id > 0x0E {
|
||||||
return Err(InvalidKeyID);
|
return Err(InvalidKeyID);
|
||||||
}
|
}
|
||||||
@ -147,9 +151,73 @@ impl Desfire {
|
|||||||
data: Option::from(vec!(key_id)),
|
data: Option::from(vec!(key_id)),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Ok(cmd_challenge_request)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generate response for aes auth challenge
|
||||||
|
pub fn authenticate_iso_aes_response_cmd(&mut self, challenge: &[u8], key: &[u8], rnd_a: &[u8]) -> Result<(APDUCommand, Vec<u8>, Vec<u8>)> {
|
||||||
|
println!("RND_B_ENC: {:x?}", challenge);
|
||||||
|
|
||||||
|
let rnd_b = AES::decrypt(challenge, key, vec![0 as u8; 16].as_slice()).unwrap();
|
||||||
|
println!("RND_B: {:x?}", rnd_b);
|
||||||
|
|
||||||
|
// auth_iv = rnd_b.clone();
|
||||||
|
let mut rnd_b_rl = rnd_b.clone();
|
||||||
|
rnd_b_rl.rotate_left(1);
|
||||||
|
println!("RND_B_RL: {:x?}", rnd_b_rl);
|
||||||
|
|
||||||
|
let rnd_ab = [&rnd_a, rnd_b_rl.as_slice()].concat();
|
||||||
|
println!("RND_AB: {:x?}", rnd_ab);
|
||||||
|
|
||||||
|
let rnd_ab_enc = AES::encrypt(rnd_ab.as_slice(), key, challenge).unwrap();
|
||||||
|
println!("RND_AB_ENC: {:x?}", rnd_ab_enc);
|
||||||
|
|
||||||
|
let iv: &[u8] = util::extract_last_block(rnd_ab_enc.as_slice(), 16)?;
|
||||||
|
|
||||||
|
let cmd_challenge_response = APDUCommand {
|
||||||
|
case: IsoCase::Case4Short,
|
||||||
|
cla: 0x90,
|
||||||
|
ins: APDUInstructions::Continue as u8,
|
||||||
|
data: Some(rnd_ab_enc.clone()),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok((cmd_challenge_response, rnd_b, iv.to_vec()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Verify response from card
|
||||||
|
pub fn authenticate_iso_aes_verify(&mut self, response: &[u8], expected_response: &[u8], challenge: &[u8], key: &[u8], iv: &[u8]) -> Result<()> {
|
||||||
|
let mut rnd_a_rot_from_card = AES::decrypt(response, key, iv)?;
|
||||||
|
rnd_a_rot_from_card.rotate_right(1);
|
||||||
|
println!("RND_A_ROT_FROM_CARD: {:x?}", rnd_a_rot_from_card);
|
||||||
|
let rnd_a_from_card = rnd_a_rot_from_card.as_slice();
|
||||||
|
println!("RND_A_FROM_CARD: {:x?}", rnd_a_from_card);
|
||||||
|
|
||||||
|
if expected_response != rnd_a_from_card {
|
||||||
|
return Err(Error::InvalidPICCChallenge);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.session_key = Some(generate_session_key_aes(expected_response, challenge).unwrap());
|
||||||
|
println!("SESSION_KEY: {:x?}", self.session_key.as_ref().unwrap());
|
||||||
|
|
||||||
|
self.cbc_iv = Some(vec![0 as u8; 16]); //FIXME: this should be a random value
|
||||||
|
println!("CBC_IV: {:x?}", self.cbc_iv.as_ref().unwrap());
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Authenticate to PICC, with ISO Authenticate for AES Key
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key_id">0x01 - 0x0D</param>
|
||||||
|
/// <param name="key">Array of 8/16 Bytes</param>
|
||||||
|
/// <param name="rndA">!!! WARNING For Testing only !!!</param>
|
||||||
|
pub fn authenticate_iso_aes(&mut self, key_id: u8, key: &[u8], rnd_a: Option<[u8; 16]>) -> Result<()> {
|
||||||
|
let cmd_challenge_request = self.authenticate_iso_aes_challenge_cmd(key_id)?;
|
||||||
println!("CMD_CHALLENGE_REQUEST: {}", cmd_challenge_request);
|
println!("CMD_CHALLENGE_REQUEST: {}", cmd_challenge_request);
|
||||||
|
|
||||||
let response = self.card.transmit(cmd_challenge_request).unwrap();
|
let response = self.card.as_ref().unwrap().transmit(cmd_challenge_request)?;
|
||||||
|
|
||||||
match response.check() {
|
match response.check() {
|
||||||
Ok(_) => {}
|
Ok(_) => {}
|
||||||
@ -159,15 +227,6 @@ impl Desfire {
|
|||||||
|
|
||||||
let rnd_b_response_body = response.body.unwrap();
|
let rnd_b_response_body = response.body.unwrap();
|
||||||
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);
|
|
||||||
|
|
||||||
let rnd_b = AES::decrypt(rnd_b_enc, key, vec![0 as u8; 16].as_slice()).unwrap();
|
|
||||||
println!("RND_B: {:x?}", rnd_b);
|
|
||||||
|
|
||||||
// auth_iv = rnd_b.clone();
|
|
||||||
let mut rnd_b_rl = rnd_b.clone();
|
|
||||||
rnd_b_rl.rotate_left(1);
|
|
||||||
println!("RND_B_RL: {:x?}", rnd_b_rl);
|
|
||||||
|
|
||||||
//FIXME: This is ugly, we should find a better way to make the function testable
|
//FIXME: This is ugly, we should find a better way to make the function testable
|
||||||
//TODO: Check if we need a CSPRNG here
|
//TODO: Check if we need a CSPRNG here
|
||||||
@ -177,22 +236,9 @@ impl Desfire {
|
|||||||
};
|
};
|
||||||
println!("RND_A: {:x?}", rnd_a);
|
println!("RND_A: {:x?}", rnd_a);
|
||||||
|
|
||||||
let rnd_ab = [&rnd_a, rnd_b_rl.as_slice()].concat();
|
let (cmd_challenge_response, rnd_b, iv) = self.authenticate_iso_aes_response_cmd(rnd_b_enc, key, &rnd_a)?;
|
||||||
println!("RND_AB: {:x?}", rnd_ab);
|
|
||||||
|
|
||||||
let rnd_ab_enc = AES::encrypt(rnd_ab.as_slice(), key, rnd_b_enc).unwrap();
|
let response = self.card.as_ref().unwrap().transmit(cmd_challenge_response)?;
|
||||||
println!("RND_AB_ENC: {:x?}", rnd_ab_enc);
|
|
||||||
|
|
||||||
let cmd_challenge_response = APDUCommand {
|
|
||||||
case: IsoCase::Case4Short,
|
|
||||||
cla: 0x90,
|
|
||||||
ins: APDUInstructions::Continue as u8,
|
|
||||||
data: Some(rnd_ab_enc.clone()),
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
println!("CMD_CHALLENGE_RESPONSE: {}", cmd_challenge_response);
|
|
||||||
|
|
||||||
let response = self.card.transmit(cmd_challenge_response).unwrap();
|
|
||||||
|
|
||||||
match response.check() {
|
match response.check() {
|
||||||
Ok(_) => {}
|
Ok(_) => {}
|
||||||
@ -200,26 +246,9 @@ impl Desfire {
|
|||||||
}
|
}
|
||||||
println!("RESPONSE: {}", response);
|
println!("RESPONSE: {}", response);
|
||||||
|
|
||||||
let iv: &[u8] = util::extract_last_block(rnd_ab_enc.as_slice(), 16).unwrap();
|
|
||||||
let rnd_a_enc_from_card = response.body.unwrap();
|
let rnd_a_enc_from_card = response.body.unwrap();
|
||||||
println!("RND_A_ENC_FROM_CARD: {:x?}", rnd_a_enc_from_card.as_slice());
|
println!("RND_A_ENC_FROM_CARD: {:x?}", rnd_a_enc_from_card.as_slice());
|
||||||
let mut rnd_a_rot_from_card = AES::decrypt(rnd_a_enc_from_card.as_slice(), key, iv).unwrap();
|
self.authenticate_iso_aes_verify(rnd_a_enc_from_card.as_slice(), rnd_a.as_slice(), rnd_b.as_slice(), key, iv.as_slice())
|
||||||
rnd_a_rot_from_card.rotate_right(1);
|
|
||||||
println!("RND_A_ROT_FROM_CARD: {:x?}", rnd_a_rot_from_card);
|
|
||||||
let rnd_a_from_card = rnd_a_rot_from_card.as_slice();
|
|
||||||
println!("RND_A_FROM_CARD: {:x?}", rnd_a_from_card);
|
|
||||||
|
|
||||||
if rnd_a != rnd_a_from_card {
|
|
||||||
return Err(Error::InvalidPICCChallenge);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.session_key = Some(generate_session_key_aes(&rnd_a, rnd_b.as_slice()).unwrap());
|
|
||||||
println!("SESSION_KEY: {:x?}", self.session_key.as_ref().unwrap());
|
|
||||||
|
|
||||||
self.cbc_iv = Some(vec![0 as u8; 16]);
|
|
||||||
println!("CBC_IV: {:x?}", self.cbc_iv.as_ref().unwrap());
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -234,7 +263,7 @@ impl Desfire {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
let response = self.card.transmit(cmd_format).unwrap();
|
let response = self.card.as_ref().unwrap().transmit(cmd_format).unwrap();
|
||||||
|
|
||||||
response.check()
|
response.check()
|
||||||
}
|
}
|
||||||
@ -259,7 +288,7 @@ impl Desfire {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
let response = self.card.transmit(cmd_create_application).unwrap();
|
let response = self.card.as_ref().unwrap().transmit(cmd_create_application).unwrap();
|
||||||
|
|
||||||
response.check()
|
response.check()
|
||||||
}
|
}
|
||||||
@ -317,7 +346,7 @@ impl Desfire {
|
|||||||
};
|
};
|
||||||
println!("CMD_CHANGE_KEY: {}", cmd_change_key);
|
println!("CMD_CHANGE_KEY: {}", cmd_change_key);
|
||||||
|
|
||||||
let response = self.card.transmit(cmd_change_key).unwrap();
|
let response = self.card.as_ref().unwrap().transmit(cmd_change_key).unwrap();
|
||||||
println!("RESPONSE: {}", response);
|
println!("RESPONSE: {}", response);
|
||||||
|
|
||||||
response.check()
|
response.check()
|
||||||
@ -386,7 +415,7 @@ impl Desfire {
|
|||||||
};
|
};
|
||||||
println!("CMD_CHANGE_KEY: {}", cmd_change_key);
|
println!("CMD_CHANGE_KEY: {}", cmd_change_key);
|
||||||
|
|
||||||
let response = self.card.transmit(cmd_change_key).unwrap();
|
let response = self.card.as_ref().unwrap().transmit(cmd_change_key).unwrap();
|
||||||
println!("RESPONSE: {}", response);
|
println!("RESPONSE: {}", response);
|
||||||
|
|
||||||
response.check()
|
response.check()
|
||||||
@ -410,8 +439,9 @@ impl Desfire {
|
|||||||
data: Option::from(data),
|
data: Option::from(data),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
println!("CMD_CREATE_FILE_STANDARD: {}", cmd_create_file_standard);
|
||||||
|
|
||||||
let response = self.card.transmit(cmd_create_file_standard).unwrap();
|
let response = self.card.as_ref().unwrap().transmit(cmd_create_file_standard).unwrap();
|
||||||
println!("RESPONSE: {}", response);
|
println!("RESPONSE: {}", response);
|
||||||
|
|
||||||
response.check()
|
response.check()
|
||||||
@ -430,7 +460,7 @@ impl Desfire {
|
|||||||
|
|
||||||
println!("Writing data to file {}", file_id);
|
println!("Writing data to file {}", file_id);
|
||||||
|
|
||||||
const MAX_BYTES_PER_TRANSACTION: usize = 47;
|
|
||||||
let mut bytes_writen: usize = 0;
|
let mut bytes_writen: usize = 0;
|
||||||
let length: usize = data.len();
|
let length: usize = data.len();
|
||||||
|
|
||||||
@ -460,7 +490,7 @@ impl Desfire {
|
|||||||
};
|
};
|
||||||
println!("CMD_WRITE_DATA: {}", cmd_write_data);
|
println!("CMD_WRITE_DATA: {}", cmd_write_data);
|
||||||
|
|
||||||
let response = self.card.transmit(cmd_write_data).unwrap();
|
let response = self.card.as_ref().unwrap().transmit(cmd_write_data).unwrap();
|
||||||
println!("RESPONSE: {}", response);
|
println!("RESPONSE: {}", response);
|
||||||
|
|
||||||
ret = response.check();
|
ret = response.check();
|
||||||
@ -469,6 +499,30 @@ impl Desfire {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn read_data_chunk_cmd(&self, file_id: u8, offset: u32, length: usize) -> Result<APDUCommand> {
|
||||||
|
if file_id >= 0x20 {
|
||||||
|
return Err(InvalidFileID);
|
||||||
|
}
|
||||||
|
|
||||||
|
if length > MAX_BYTES_PER_TRANSACTION {
|
||||||
|
return Err(InvalidLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut sendbuf = vec![file_id];
|
||||||
|
sendbuf.append(&mut offset.to_le_bytes()[..3].to_vec());
|
||||||
|
sendbuf.append(&mut length.to_le_bytes()[..3].to_vec());
|
||||||
|
|
||||||
|
let cmd_read_data_chunk = APDUCommand {
|
||||||
|
case: IsoCase::Case4Short,
|
||||||
|
cla: 0x90,
|
||||||
|
ins: APDUInstructions::ReadData as u8,
|
||||||
|
data: Option::from(sendbuf),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
return Ok(cmd_read_data_chunk);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Read Data from File
|
/// Read Data from File
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -480,7 +534,6 @@ impl Desfire {
|
|||||||
return Err(InvalidFileID);
|
return Err(InvalidFileID);
|
||||||
}
|
}
|
||||||
|
|
||||||
const MAX_BYTES_PER_TRANSACTION: usize = 47;
|
|
||||||
let mut bytes_read: usize = 0;
|
let mut bytes_read: usize = 0;
|
||||||
|
|
||||||
let mut read_buffer: Vec<u8> = vec![];
|
let mut read_buffer: Vec<u8> = vec![];
|
||||||
@ -491,21 +544,12 @@ impl Desfire {
|
|||||||
false => { MAX_BYTES_PER_TRANSACTION }
|
false => { MAX_BYTES_PER_TRANSACTION }
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut send_buffer = vec![file_id];
|
let cmd_read_data = self.read_data_chunk_cmd(file_id, (offset as usize + bytes_read) as u32, bytes_toread).unwrap();
|
||||||
send_buffer.append(&mut (offset as usize + bytes_read).to_le_bytes()[..3].to_vec());
|
|
||||||
send_buffer.append(&mut bytes_toread.to_le_bytes()[..3].to_vec());
|
|
||||||
bytes_read += bytes_toread;
|
|
||||||
|
|
||||||
let cmd_read_data = APDUCommand {
|
|
||||||
case: IsoCase::Case4Short,
|
|
||||||
cla: 0x90,
|
|
||||||
ins: APDUInstructions::ReadData as u8,
|
|
||||||
data: Option::from(send_buffer),
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
println!("CMD_READ_DATA: {}", cmd_read_data);
|
println!("CMD_READ_DATA: {}", cmd_read_data);
|
||||||
|
|
||||||
let response = self.card.transmit(cmd_read_data).unwrap();
|
bytes_read += bytes_toread;
|
||||||
|
|
||||||
|
let response = self.card.as_ref().unwrap().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))?;
|
||||||
@ -641,7 +685,7 @@ mod tests {
|
|||||||
println!("{}", apdu_cmd);
|
println!("{}", apdu_cmd);
|
||||||
let apdu = Vec::<u8>::try_from(apdu_cmd).unwrap();
|
let apdu = Vec::<u8>::try_from(apdu_cmd).unwrap();
|
||||||
let mut rapdu_buf = [0; MAX_BUFFER_SIZE];
|
let mut rapdu_buf = [0; MAX_BUFFER_SIZE];
|
||||||
let rapdu = match self.card.as_ref().unwrap().transmit(apdu.as_slice(), &mut rapdu_buf) {
|
let rapdu = match self.card.as_ref().as_ref().unwrap().transmit(apdu.as_slice(), &mut rapdu_buf) {
|
||||||
Ok(rapdu) => rapdu,
|
Ok(rapdu) => rapdu,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
eprintln!("Failed to transmit APDU command to card: {}", err);
|
eprintln!("Failed to transmit APDU command to card: {}", err);
|
||||||
@ -746,7 +790,7 @@ mod tests {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
let desfire = Desfire{
|
let desfire = Desfire{
|
||||||
card: Box::new(mock),
|
card: Some(Box::new(mock)),
|
||||||
cbc_iv: None,
|
cbc_iv: None,
|
||||||
session_key: None
|
session_key: None
|
||||||
};
|
};
|
||||||
@ -760,7 +804,7 @@ mod tests {
|
|||||||
let mock = MockVirtualCard::new();
|
let mock = MockVirtualCard::new();
|
||||||
|
|
||||||
let mut desfire = Desfire{
|
let mut desfire = Desfire{
|
||||||
card: Box::new(mock),
|
card: Some(Box::new(mock)),
|
||||||
cbc_iv: None,
|
cbc_iv: None,
|
||||||
session_key: None
|
session_key: None
|
||||||
};
|
};
|
||||||
@ -808,7 +852,7 @@ mod tests {
|
|||||||
card.connect();
|
card.connect();
|
||||||
|
|
||||||
let desfire = Desfire{
|
let desfire = Desfire{
|
||||||
card: Box::new(card),
|
card: Some(Box::new(card)),
|
||||||
cbc_iv: None,
|
cbc_iv: None,
|
||||||
session_key: None
|
session_key: None
|
||||||
};
|
};
|
||||||
@ -836,7 +880,7 @@ mod tests {
|
|||||||
println!("{:x?}", key.key.deref());
|
println!("{:x?}", key.key.deref());
|
||||||
|
|
||||||
let mut desfire = Desfire{
|
let mut desfire = Desfire{
|
||||||
card: Box::new(mock),
|
card: Some(Box::new(mock)),
|
||||||
cbc_iv: None,
|
cbc_iv: None,
|
||||||
session_key: None
|
session_key: None
|
||||||
};
|
};
|
||||||
@ -857,7 +901,7 @@ mod tests {
|
|||||||
let mock = MockVirtualCard::new();
|
let mock = MockVirtualCard::new();
|
||||||
|
|
||||||
let mut desfire = Desfire{
|
let mut desfire = Desfire{
|
||||||
card: Box::new(mock),
|
card: Some(Box::new(mock)),
|
||||||
cbc_iv: None,
|
cbc_iv: None,
|
||||||
session_key: None
|
session_key: None
|
||||||
};
|
};
|
||||||
@ -905,7 +949,7 @@ mod tests {
|
|||||||
card.connect();
|
card.connect();
|
||||||
|
|
||||||
let mut desfire = Desfire{
|
let mut desfire = Desfire{
|
||||||
card: Box::new(card),
|
card: Some(Box::new(card)),
|
||||||
cbc_iv: None,
|
cbc_iv: None,
|
||||||
session_key: None
|
session_key: None
|
||||||
};
|
};
|
||||||
@ -942,7 +986,7 @@ mod tests {
|
|||||||
println!("{:x?}", key.key.deref());
|
println!("{:x?}", key.key.deref());
|
||||||
|
|
||||||
let mut desfire = Desfire{
|
let mut desfire = Desfire{
|
||||||
card: Box::new(mock),
|
card: Some(Box::new(mock)),
|
||||||
cbc_iv: None,
|
cbc_iv: None,
|
||||||
session_key: None
|
session_key: None
|
||||||
};
|
};
|
||||||
@ -963,7 +1007,7 @@ mod tests {
|
|||||||
let mock = MockVirtualCard::new();
|
let mock = MockVirtualCard::new();
|
||||||
|
|
||||||
let mut desfire = Desfire{
|
let mut desfire = Desfire{
|
||||||
card: Box::new(mock),
|
card: Some(Box::new(mock)),
|
||||||
cbc_iv: None,
|
cbc_iv: None,
|
||||||
session_key: None
|
session_key: None
|
||||||
};
|
};
|
||||||
@ -981,7 +1025,7 @@ mod tests {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
let mut desfire = Desfire{
|
let mut desfire = Desfire{
|
||||||
card: Box::new(mock),
|
card: Some(Box::new(mock)),
|
||||||
cbc_iv: None,
|
cbc_iv: None,
|
||||||
session_key: None
|
session_key: None
|
||||||
};
|
};
|
||||||
@ -1002,7 +1046,7 @@ mod tests {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
let mut desfire = Desfire{
|
let mut desfire = Desfire{
|
||||||
card: Box::new(mock),
|
card: Some(Box::new(mock)),
|
||||||
cbc_iv: None,
|
cbc_iv: None,
|
||||||
session_key: None
|
session_key: None
|
||||||
};
|
};
|
||||||
@ -1017,7 +1061,7 @@ mod tests {
|
|||||||
let mut mock = MockVirtualCard::new();
|
let mut mock = MockVirtualCard::new();
|
||||||
|
|
||||||
let mut desfire = Desfire{
|
let mut desfire = Desfire{
|
||||||
card: Box::new(mock),
|
card: Some(Box::new(mock)),
|
||||||
cbc_iv: None,
|
cbc_iv: None,
|
||||||
session_key: None
|
session_key: None
|
||||||
};
|
};
|
||||||
@ -1037,7 +1081,7 @@ mod tests {
|
|||||||
let new_key = hex!("25432a462d4a614e645267556b587032");
|
let new_key = hex!("25432a462d4a614e645267556b587032");
|
||||||
|
|
||||||
let mut desfire = Desfire {
|
let mut desfire = Desfire {
|
||||||
card: Box::new(mock),
|
card: Some(Box::new(mock)),
|
||||||
cbc_iv: Some(hex!("00000000000000000000000000000000").to_vec()),
|
cbc_iv: Some(hex!("00000000000000000000000000000000").to_vec()),
|
||||||
session_key: Some(hex!("a8514dd0350f3dfbc86e80744bcc9b57").to_vec())
|
session_key: Some(hex!("a8514dd0350f3dfbc86e80744bcc9b57").to_vec())
|
||||||
};
|
};
|
||||||
@ -1051,7 +1095,7 @@ mod tests {
|
|||||||
let mut mock = MockVirtualCard::new();
|
let mut mock = MockVirtualCard::new();
|
||||||
|
|
||||||
let mut desfire = Desfire {
|
let mut desfire = Desfire {
|
||||||
card: Box::new(mock),
|
card: Some(Box::new(mock)),
|
||||||
cbc_iv: None,
|
cbc_iv: None,
|
||||||
session_key: None
|
session_key: None
|
||||||
};
|
};
|
||||||
@ -1072,7 +1116,7 @@ mod tests {
|
|||||||
let new_key = hex!("25432a462d4a614e645267556b587032");
|
let new_key = hex!("25432a462d4a614e645267556b587032");
|
||||||
|
|
||||||
let mut desfire = Desfire {
|
let mut desfire = Desfire {
|
||||||
card: Box::new(mock),
|
card: Some(Box::new(mock)),
|
||||||
cbc_iv: Some(hex!("00000000000000000000000000000000").to_vec()),
|
cbc_iv: Some(hex!("00000000000000000000000000000000").to_vec()),
|
||||||
session_key: Some(hex!("1677623e1e158a62dc3d128db55f947d").to_vec())
|
session_key: Some(hex!("1677623e1e158a62dc3d128db55f947d").to_vec())
|
||||||
};
|
};
|
||||||
@ -1086,7 +1130,7 @@ mod tests {
|
|||||||
let mut mock = MockVirtualCard::new();
|
let mut mock = MockVirtualCard::new();
|
||||||
|
|
||||||
let mut desfire = Desfire {
|
let mut desfire = Desfire {
|
||||||
card: Box::new(mock),
|
card: Some(Box::new(mock)),
|
||||||
cbc_iv: None,
|
cbc_iv: None,
|
||||||
session_key: None
|
session_key: None
|
||||||
};
|
};
|
||||||
@ -1104,7 +1148,7 @@ mod tests {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
let mut desfire = Desfire {
|
let mut desfire = Desfire {
|
||||||
card: Box::new(mock),
|
card: Some(Box::new(mock)),
|
||||||
cbc_iv: None,
|
cbc_iv: None,
|
||||||
session_key: None
|
session_key: None
|
||||||
};
|
};
|
||||||
@ -1119,7 +1163,7 @@ mod tests {
|
|||||||
let mut mock = MockVirtualCard::new();
|
let mut mock = MockVirtualCard::new();
|
||||||
|
|
||||||
let mut desfire = Desfire {
|
let mut desfire = Desfire {
|
||||||
card: Box::new(mock),
|
card: Some(Box::new(mock)),
|
||||||
cbc_iv: None,
|
cbc_iv: None,
|
||||||
session_key: None
|
session_key: None
|
||||||
};
|
};
|
||||||
@ -1138,7 +1182,7 @@ mod tests {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
let mut desfire = Desfire {
|
let mut desfire = Desfire {
|
||||||
card: Box::new(mock),
|
card: Some(Box::new(mock)),
|
||||||
cbc_iv: None,
|
cbc_iv: None,
|
||||||
session_key: None
|
session_key: None
|
||||||
};
|
};
|
||||||
@ -1169,7 +1213,7 @@ mod tests {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
let mut desfire = Desfire {
|
let mut desfire = Desfire {
|
||||||
card: Box::new(mock),
|
card: Some(Box::new(mock)),
|
||||||
cbc_iv: None,
|
cbc_iv: None,
|
||||||
session_key: None
|
session_key: None
|
||||||
};
|
};
|
||||||
@ -1184,7 +1228,7 @@ mod tests {
|
|||||||
let mut mock = MockVirtualCard::new();
|
let mut mock = MockVirtualCard::new();
|
||||||
|
|
||||||
let mut desfire = Desfire {
|
let mut desfire = Desfire {
|
||||||
card: Box::new(mock),
|
card: Some(Box::new(mock)),
|
||||||
cbc_iv: None,
|
cbc_iv: None,
|
||||||
session_key: None
|
session_key: None
|
||||||
};
|
};
|
||||||
@ -1202,7 +1246,7 @@ mod tests {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
let mut desfire = Desfire {
|
let mut desfire = Desfire {
|
||||||
card: Box::new(mock),
|
card: Some(Box::new(mock)),
|
||||||
cbc_iv: None,
|
cbc_iv: None,
|
||||||
session_key: None
|
session_key: None
|
||||||
};
|
};
|
||||||
@ -1221,7 +1265,7 @@ mod tests {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
let mut desfire = Desfire {
|
let mut desfire = Desfire {
|
||||||
card: Box::new(mock),
|
card: Some(Box::new(mock)),
|
||||||
cbc_iv: None,
|
cbc_iv: None,
|
||||||
session_key: None
|
session_key: None
|
||||||
};
|
};
|
||||||
@ -1252,7 +1296,7 @@ mod tests {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
let mut desfire = Desfire {
|
let mut desfire = Desfire {
|
||||||
card: Box::new(mock),
|
card: Some(Box::new(mock)),
|
||||||
cbc_iv: None,
|
cbc_iv: None,
|
||||||
session_key: None
|
session_key: None
|
||||||
};
|
};
|
||||||
@ -1267,7 +1311,7 @@ mod tests {
|
|||||||
let mut mock = MockVirtualCard::new();
|
let mut mock = MockVirtualCard::new();
|
||||||
|
|
||||||
let mut desfire = Desfire {
|
let mut desfire = Desfire {
|
||||||
card: Box::new(mock),
|
card: Some(Box::new(mock)),
|
||||||
cbc_iv: None,
|
cbc_iv: None,
|
||||||
session_key: None
|
session_key: None
|
||||||
};
|
};
|
||||||
|
@ -167,3 +167,4 @@ pub enum FileTypes {
|
|||||||
mod apduinstructions;
|
mod apduinstructions;
|
||||||
mod apdustatuscodes;
|
mod apdustatuscodes;
|
||||||
pub mod desfire;
|
pub mod desfire;
|
||||||
|
pub use desfire::Desfire;
|
42
src/error.rs
42
src/error.rs
@ -31,6 +31,7 @@ pub enum Error {
|
|||||||
InvalidPICCChallenge,
|
InvalidPICCChallenge,
|
||||||
InvalidFileID,
|
InvalidFileID,
|
||||||
InvalidKeyVersion,
|
InvalidKeyVersion,
|
||||||
|
InvalidLength,
|
||||||
NumKeys,
|
NumKeys,
|
||||||
CardError,
|
CardError,
|
||||||
}
|
}
|
||||||
@ -137,6 +138,47 @@ impl fmt::Display for Error {
|
|||||||
Error::ApplicationNotFound => {
|
Error::ApplicationNotFound => {
|
||||||
write!(f, "The application was not found on the card.")
|
write!(f, "The application was not found on the card.")
|
||||||
}
|
}
|
||||||
|
Error::InvalidLength => {
|
||||||
|
write!(f, "More data was requested than can be handled in a single transaction.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::error::Error for Error {
|
||||||
|
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||||
|
match self {
|
||||||
|
Error::IO(e) => Some(e),
|
||||||
|
Error::FromHexError(e) => Some(e),
|
||||||
|
Error::PadError(_) => None,
|
||||||
|
Error::InvalidStatusWord => None,
|
||||||
|
Error::IllegalCommandCode => None,
|
||||||
|
Error::IntegrityError => None,
|
||||||
|
Error::NoSuchKey => None,
|
||||||
|
Error::LengthError => None,
|
||||||
|
Error::PermissionDenied => None,
|
||||||
|
Error::ParameterError => None,
|
||||||
|
Error::AuthenticationDelay => None,
|
||||||
|
Error::AuthenticationError => None,
|
||||||
|
Error::BoundaryError => None,
|
||||||
|
Error::CommandAborted => None,
|
||||||
|
Error::DuplicateError => None,
|
||||||
|
Error::FileNotFound => None,
|
||||||
|
Error::InvalidApplicationID => None,
|
||||||
|
Error::InvalidAPDUResponse => None,
|
||||||
|
Error::InvalidIsoCase => None,
|
||||||
|
Error::InvalidKeyID => None,
|
||||||
|
Error::InvalidKeyVersion => None,
|
||||||
|
Error::InvalidPICCChallenge => None,
|
||||||
|
Error::InvalidFileID => None,
|
||||||
|
Error::NumKeys => None,
|
||||||
|
Error::CardError => None,
|
||||||
|
Error::ApplicationNotFound => None,
|
||||||
|
Error::InvalidLength => None,
|
||||||
|
Error::Boxed(e) => e.source(),
|
||||||
|
Error::Simple(e) => Some(e),
|
||||||
|
Error::BlockModeError(e) => Some(e),
|
||||||
|
Error::InvalidKeyIvLength(e) => Some(e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,7 @@ impl fmt::Display for APDUResponse {
|
|||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
match &self.body {
|
match &self.body {
|
||||||
None => {
|
None => {
|
||||||
write!(f, "SW1: {:#X} | SW2: 0x{:#X}", self.sw1, self.sw2)
|
write!(f, "SW1: {:#X} | SW2: {:#X}", self.sw1, self.sw2)
|
||||||
}
|
}
|
||||||
Some(body) => {
|
Some(body) => {
|
||||||
write!(
|
write!(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user