diff --git a/src/crypto/cipher/aes.rs b/src/crypto/cipher/aes.rs index ae3bcf3..c055d1f 100644 --- a/src/crypto/cipher/aes.rs +++ b/src/crypto/cipher/aes.rs @@ -42,8 +42,9 @@ mod tests { let data_enc = crate::crypto::cipher::aes::AES::encrypt(&data, &key, &iv).unwrap(); - let data_enc_expected = hex!("3c79d74a4969ba7123e5d8f6df24493112d221fd131a4617d0eda5d92ccc1b46"); + let data_enc_expected = + hex!("3c79d74a4969ba7123e5d8f6df24493112d221fd131a4617d0eda5d92ccc1b46"); assert_eq!(data_enc, data_enc_expected); } -} \ No newline at end of file +} diff --git a/src/desfire/desfire.rs b/src/desfire/desfire.rs index f81ce22..f06e5ea 100644 --- a/src/desfire/desfire.rs +++ b/src/desfire/desfire.rs @@ -1,17 +1,22 @@ -use std::fs::read; -use std::usize::MAX; -use crate::{Card, error}; -use crate::iso7816_4::apducommand::{APDUCommand, IsoCase}; -use crate::desfire::apduinstructions::APDUInstructions; -use crate::crypto::cipher::tdes::Tdes; use crate::crypto::cipher::aes::AES; +use crate::crypto::cipher::tdes::Tdes; use crate::crypto::cipher::Cipher; use crate::crypto::util; -use crate::error::{Result, Error}; -use crate::error::Error::{InvalidApplicationID, InvalidKeyID, InvalidFileID, NumKeys, InvalidKeyVersion, InvalidLength}; use crate::crypto::util::expand_to_blocksize; -use crate::desfire::{FileCommunication, ChangeMasterKeySettings, CreateDeleteFile, FileDirectoryAccess, ChangeMasterKey, ChangeApplicationKey, CryptoOperationsType, FileIdentifiers}; +use crate::desfire::apduinstructions::APDUInstructions; +use crate::desfire::{ + ChangeApplicationKey, ChangeMasterKey, ChangeMasterKeySettings, CreateDeleteFile, + CryptoOperationsType, FileCommunication, FileDirectoryAccess, FileIdentifiers, +}; +use crate::error::Error::{ + InvalidApplicationID, InvalidFileID, InvalidKeyID, InvalidKeyVersion, InvalidLength, NumKeys, +}; +use crate::error::{Error, Result}; +use crate::iso7816_4::apducommand::{APDUCommand, IsoCase}; +use crate::{error, Card}; use num_traits::FromPrimitive; +use std::fs::read; +use std::usize::MAX; pub const MAX_BYTES_PER_TRANSACTION: usize = 47; @@ -24,7 +29,6 @@ pub struct Desfire { impl Desfire { // Desfire commands - pub fn select_application_cmd(&self, aid: u32) -> Result { if aid > 0xFFFFFF { return Err(InvalidApplicationID); @@ -57,7 +61,12 @@ impl Desfire { /// 0x01 - 0x0D /// Array of 8/16 Bytes /// !!! WARNING For Testing only !!! - pub fn authenticate_iso_des(&mut self, key_id: u8, key: &[u8], rnd_a: Option<[u8; 8]>) -> Result<()> { + pub fn authenticate_iso_des( + &mut self, + key_id: u8, + key: &[u8], + rnd_a: Option<[u8; 8]>, + ) -> Result<()> { if key_id > 0x0E { return Err(InvalidKeyID); } @@ -66,15 +75,22 @@ impl Desfire { case: IsoCase::Case4Short, cla: 0x90, ins: APDUInstructions::AuthenticateIso as u8, - data: Option::from(vec!(key_id)), + data: Option::from(vec![key_id]), ..Default::default() }; - let response = self.card.as_ref().unwrap().transmit(cmd_challenge_request).unwrap(); + let response = self + .card + .as_ref() + .unwrap() + .transmit(cmd_challenge_request) + .unwrap(); match response.check() { Ok(_) => {} - Err(e) => { return Err(e); } + Err(e) => { + return Err(e); + } } let rnd_b_response_body = response.body.unwrap(); @@ -91,8 +107,8 @@ impl Desfire { //FIXME: This is ugly, we should find a better way to make the function testable //TODO: Check if we need a CSPRNG here let rnd_a = match rnd_a { - None => { rand::random() } - Some(i) => { i } + None => rand::random(), + Some(i) => i, }; // println!("RND_A: {:x?}", rnd_a); @@ -110,17 +126,25 @@ impl Desfire { ..Default::default() }; - let response = self.card.as_ref().unwrap().transmit(cmd_challenge_response).unwrap(); + let response = self + .card + .as_ref() + .unwrap() + .transmit(cmd_challenge_response) + .unwrap(); match response.check() { Ok(_) => {} - Err(e) => { return Err(e); } + Err(e) => { + return Err(e); + } } let iv: &[u8] = util::extract_last_block(rnd_ab_enc.as_slice(), 8).unwrap(); let rnd_a_enc_from_card = response.body.unwrap(); // println!("RND_A_ENC_FROM_CARD: {:x?}", rnd_a_enc_from_card.as_slice()); - let mut rnd_a_rot_from_card = Tdes::decrypt(rnd_a_enc_from_card.as_slice(), key, iv).unwrap(); + let mut rnd_a_rot_from_card = + Tdes::decrypt(rnd_a_enc_from_card.as_slice(), key, iv).unwrap(); // println!("RND_A_ROT_FROM_CARD: {:x?}", rnd_a_rot_from_card); rnd_a_rot_from_card.rotate_right(1); let rnd_a_from_card = rnd_a_rot_from_card.as_slice(); @@ -148,7 +172,7 @@ impl Desfire { case: IsoCase::Case4Short, cla: 0x90, ins: APDUInstructions::AuthenticateAes as u8, - data: Option::from(vec!(key_id)), + data: Option::from(vec![key_id]), ..Default::default() }; @@ -156,7 +180,12 @@ impl Desfire { } /// 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, Vec)> { + pub fn authenticate_iso_aes_response_cmd( + &mut self, + challenge: &[u8], + key: &[u8], + rnd_a: &[u8], + ) -> Result<(APDUCommand, Vec, Vec)> { // println!("RND_B_ENC: {:x?}", challenge); let rnd_b = AES::decrypt(challenge, key, vec![0 as u8; 16].as_slice()).unwrap(); @@ -187,7 +216,14 @@ impl Desfire { } /// Verify response from card - pub fn authenticate_iso_aes_verify(&mut self, response: &[u8], expected_response: &[u8], challenge: &[u8], key: &[u8], iv: &[u8]) -> Result<()> { + 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); @@ -202,7 +238,7 @@ impl Desfire { // 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()); + // println!("CBC_IV: {:x?}", self.cbc_iv.as_ref().unwrap()); Ok(()) } @@ -213,15 +249,26 @@ impl Desfire { /// 0x01 - 0x0D /// Array of 8/16 Bytes /// !!! WARNING For Testing only !!! - pub fn authenticate_iso_aes(&mut self, key_id: u8, key: &[u8], rnd_a: Option<[u8; 16]>) -> Result<()> { + 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); - let response = self.card.as_ref().unwrap().transmit(cmd_challenge_request)?; + let response = self + .card + .as_ref() + .unwrap() + .transmit(cmd_challenge_request)?; match response.check() { Ok(_) => {} - Err(e) => { return Err(e); } + Err(e) => { + return Err(e); + } } // println!("RESPONSE: {}", response); @@ -231,24 +278,37 @@ impl Desfire { //FIXME: This is ugly, we should find a better way to make the function testable //TODO: Check if we need a CSPRNG here let rnd_a = match rnd_a { - None => { rand::random() } - Some(i) => { i } + None => rand::random(), + Some(i) => i, }; // println!("RND_A: {:x?}", rnd_a); - let (cmd_challenge_response, rnd_b, iv) = self.authenticate_iso_aes_response_cmd(rnd_b_enc, key, &rnd_a)?; + let (cmd_challenge_response, rnd_b, iv) = + self.authenticate_iso_aes_response_cmd(rnd_b_enc, key, &rnd_a)?; - let response = self.card.as_ref().unwrap().transmit(cmd_challenge_response)?; + let response = self + .card + .as_ref() + .unwrap() + .transmit(cmd_challenge_response)?; match response.check() { Ok(_) => {} - Err(e) => { return Err(e); } + Err(e) => { + return Err(e); + } } // println!("RESPONSE: {}", response); let rnd_a_enc_from_card = response.body.unwrap(); // println!("RND_A_ENC_FROM_CARD: {:x?}", rnd_a_enc_from_card.as_slice()); - self.authenticate_iso_aes_verify(rnd_a_enc_from_card.as_slice(), rnd_a.as_slice(), rnd_b.as_slice(), key, iv.as_slice()) + self.authenticate_iso_aes_verify( + rnd_a_enc_from_card.as_slice(), + rnd_a.as_slice(), + rnd_b.as_slice(), + key, + iv.as_slice(), + ) } /// @@ -288,7 +348,12 @@ impl Desfire { ..Default::default() }; - let response = self.card.as_ref().unwrap().transmit(cmd_create_application).unwrap(); + let response = self + .card + .as_ref() + .unwrap() + .transmit(cmd_create_application) + .unwrap(); response.check() } @@ -327,7 +392,11 @@ impl Desfire { let plaintext_pad = expand_to_blocksize(plaintext.as_mut_slice(), 16)?; // println!("PLAINTEXT_PAD: {:x?}", plaintext_pad); - let cryptogram = AES::encrypt(plaintext_pad.as_slice(), self.session_key.as_ref().unwrap(), self.cbc_iv.as_ref().unwrap())?; + let cryptogram = AES::encrypt( + plaintext_pad.as_slice(), + self.session_key.as_ref().unwrap(), + self.cbc_iv.as_ref().unwrap(), + )?; // println!("CRYPTOGRAM: {:x?}", cryptogram); self.cbc_iv = Some(util::extract_last_block(cryptogram.as_slice(), 16)?.to_vec()); @@ -346,7 +415,12 @@ impl Desfire { }; // println!("CMD_CHANGE_KEY: {}", cmd_change_key); - let response = self.card.as_ref().unwrap().transmit(cmd_change_key).unwrap(); + let response = self + .card + .as_ref() + .unwrap() + .transmit(cmd_change_key) + .unwrap(); // println!("RESPONSE: {}", response); response.check() @@ -359,7 +433,13 @@ impl Desfire { /// Array of 16 Bytes /// Array of 16 Bytes /// Version of Key(min. 0x10) - pub fn change_other_key_aes(&mut self, key_id: u8, new_key: &[u8], old_key: &[u8], key_version: u8) -> Result<()> { + pub fn change_other_key_aes( + &mut self, + key_id: u8, + new_key: &[u8], + old_key: &[u8], + key_version: u8, + ) -> Result<()> { if key_id >= 0x0E { return Err(InvalidKeyID); } @@ -370,7 +450,11 @@ impl Desfire { let header = vec![0xC4, key_id]; // println!("HEADER: {:x?}", header); - let key_xor: Vec = new_key.iter().zip(old_key.iter()).map(|(&x1, &x2)| x1 ^ x2).collect(); + let key_xor: Vec = new_key + .iter() + .zip(old_key.iter()) + .map(|(&x1, &x2)| x1 ^ x2) + .collect(); // println!("KEY_XOR: {:x?}", key_xor); let key_and_version: Vec = [key_xor, vec![key_version]].concat(); @@ -396,7 +480,11 @@ impl Desfire { let plaintext_pad = expand_to_blocksize(plaintext.as_mut_slice(), 16)?; // println!("PLAINTEXT_PAD: {:x?}", plaintext_pad); - let cryptogram = AES::encrypt(plaintext_pad.as_slice(), self.session_key.as_ref().unwrap(), self.cbc_iv.as_ref().unwrap())?; + let cryptogram = AES::encrypt( + plaintext_pad.as_slice(), + self.session_key.as_ref().unwrap(), + self.cbc_iv.as_ref().unwrap(), + )?; // println!("CRYPTOGRAM: {:x?}", cryptogram); self.cbc_iv = Some(util::extract_last_block(cryptogram.as_slice(), 16)?.to_vec()); @@ -415,13 +503,24 @@ impl Desfire { }; // println!("CMD_CHANGE_KEY: {}", cmd_change_key); - let response = self.card.as_ref().unwrap().transmit(cmd_change_key).unwrap(); + let response = self + .card + .as_ref() + .unwrap() + .transmit(cmd_change_key) + .unwrap(); // println!("RESPONSE: {}", response); response.check() } - pub fn create_file_standard(&self, file_id: u8, communication: FileCommunication, access_rights: u16, size: u32) -> Result<()> { + pub fn create_file_standard( + &self, + file_id: u8, + communication: FileCommunication, + access_rights: u16, + size: u32, + ) -> Result<()> { if file_id >= 0x20 { return Err(InvalidFileID); } @@ -441,7 +540,12 @@ impl Desfire { }; // println!("CMD_CREATE_FILE_STANDARD: {}", cmd_create_file_standard); - let response = self.card.as_ref().unwrap().transmit(cmd_create_file_standard).unwrap(); + let response = self + .card + .as_ref() + .unwrap() + .transmit(cmd_create_file_standard) + .unwrap(); // println!("RESPONSE: {}", response); response.check() @@ -460,16 +564,17 @@ impl Desfire { // println!("Writing data to file {}", file_id); - let mut bytes_writen: usize = 0; let length: usize = data.len(); - let mut ret: Result<()> = Err(error::Error::Simple(simple_error::simple_error!("Error while writing data to card"))); //TODO: Replace with better error + let mut ret: Result<()> = Err(error::Error::Simple(simple_error::simple_error!( + "Error while writing data to card" + ))); //TODO: Replace with better error while bytes_writen != data.len() { let bytes_towrite = match length - bytes_writen < MAX_BYTES_PER_TRANSACTION { - true => { length - bytes_writen } - false => { MAX_BYTES_PER_TRANSACTION } + true => length - bytes_writen, + false => MAX_BYTES_PER_TRANSACTION, }; let mut write_buffer = vec![file_id]; @@ -490,16 +595,26 @@ impl Desfire { }; // println!("CMD_WRITE_DATA: {}", cmd_write_data); - let response = self.card.as_ref().unwrap().transmit(cmd_write_data).unwrap(); + let response = self + .card + .as_ref() + .unwrap() + .transmit(cmd_write_data) + .unwrap(); // println!("RESPONSE: {}", response); ret = response.check(); - }; + } return ret; } - pub fn read_data_chunk_cmd(&self, file_id: u8, offset: u32, length: usize) -> Result { + pub fn read_data_chunk_cmd( + &self, + file_id: u8, + offset: u32, + length: usize, + ) -> Result { if file_id >= 0x20 { return Err(InvalidFileID); } @@ -540,11 +655,13 @@ impl Desfire { while bytes_read != length { let bytes_toread = match length - bytes_read < MAX_BYTES_PER_TRANSACTION { - true => { length - bytes_read } - false => { MAX_BYTES_PER_TRANSACTION } + true => length - bytes_read, + false => MAX_BYTES_PER_TRANSACTION, }; - let cmd_read_data = self.read_data_chunk_cmd(file_id, (offset as usize + bytes_read) as u32, bytes_toread).unwrap(); + let cmd_read_data = self + .read_data_chunk_cmd(file_id, (offset as usize + bytes_read) as u32, bytes_toread) + .unwrap(); // println!("CMD_READ_DATA: {}", cmd_read_data); bytes_read += bytes_toread; @@ -556,14 +673,12 @@ impl Desfire { // // 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()); - }; + } return Ok(read_buffer); - } } - /// /// Generates SessionKey for DES Authentification /// @@ -587,14 +702,30 @@ fn generate_session_key_aes(rnd_a: &[u8], rnd_b: &[u8]) -> Option> { /// /// ID of Key for changing Application Keys /// generated keysettings -pub fn generate_keysetting1(change_key: u8, change_masterkey_settings: ChangeMasterKeySettings, create_delete_file: CreateDeleteFile, file_directory_access: FileDirectoryAccess, change_master_key: ChangeMasterKey) -> Result { +pub fn generate_keysetting1( + change_key: u8, + change_masterkey_settings: ChangeMasterKeySettings, + create_delete_file: CreateDeleteFile, + file_directory_access: FileDirectoryAccess, + change_master_key: ChangeMasterKey, +) -> Result { return match FromPrimitive::from_u8(change_key) { - Some(ChangeApplicationKey::MASTERKEY) | Some(ChangeApplicationKey::SAMEKEY) | Some(ChangeApplicationKey::ALLKEYS) => Ok((change_key << 4) | change_masterkey_settings as u8 | create_delete_file as u8 | file_directory_access as u8 | change_master_key as u8), + Some(ChangeApplicationKey::MASTERKEY) + | Some(ChangeApplicationKey::SAMEKEY) + | Some(ChangeApplicationKey::ALLKEYS) => Ok((change_key << 4) + | change_masterkey_settings as u8 + | create_delete_file as u8 + | file_directory_access as u8 + | change_master_key as u8), None => { if change_key < 0x01 || change_key >= 0x08 { Err(InvalidKeyID) } else { - Ok((change_key << 4) | change_masterkey_settings as u8 | create_delete_file as u8 | file_directory_access as u8 | change_master_key as u8) + Ok((change_key << 4) + | change_masterkey_settings as u8 + | create_delete_file as u8 + | file_directory_access as u8 + | change_master_key as u8) } } }; @@ -605,7 +736,11 @@ pub fn generate_keysetting1(change_key: u8, change_masterkey_settings: ChangeMas /// /// Number of keys that can be stored within the application (0x01-0x0D) /// generated keysettings -pub fn generate_keysetting2(crypto_operations: CryptoOperationsType, file_identifier: FileIdentifiers, num_keys: u8) -> Result { +pub fn generate_keysetting2( + crypto_operations: CryptoOperationsType, + file_identifier: FileIdentifiers, + num_keys: u8, +) -> Result { return if num_keys < 0x01 || num_keys >= 0x0D { Err(NumKeys) } else { @@ -621,33 +756,47 @@ pub fn generate_keysetting2(crypto_operations: CryptoOperationsType, file_identi /// KeyID for Write Access /// KeyID for Read and Write Access /// KeyID for Configuration Access -pub fn generate_file_access_rights(read: u8, write: u8, read_write: u8, configure: u8) -> Result { +pub fn generate_file_access_rights( + read: u8, + write: u8, + read_write: u8, + configure: u8, +) -> Result { return if read > 0x0F || write >= 0x0F || read_write >= 0x0F || configure >= 0x0F { Err(InvalidKeyID) } else { - Ok((((read as u16) << 12) | ((write as u16) << 8) | ((read_write as u16) << 4) | configure as u16) as u16) + Ok((((read as u16) << 12) + | ((write as u16) << 8) + | ((read_write as u16) << 4) + | configure as u16) as u16) }; } - #[cfg(test)] mod tests { - use std::convert::TryFrom; - use std::ffi::{CStr, CString}; - use std::fs::{File, read}; - use std::ops::Deref; - use hex_literal::hex; - use mockall::{mock, predicate}; - use pcsc::{Context, MAX_BUFFER_SIZE, Protocols, Scope, ShareMode, Card, Error, Disposition}; - use crate::{APDUResponse, APDUCommand, Card as CardTrait}; - use crate::error::Result; use crate::crypto::cipher_key::CipherKey; use crate::crypto::cipher_type::CipherType; - use crate::desfire::desfire::{generate_session_key_des, generate_session_key_aes, generate_keysetting1, generate_keysetting2, generate_file_access_rights, Desfire}; - use crate::desfire::{ChangeApplicationKey, ChangeMasterKeySettings, CreateDeleteFile, FileDirectoryAccess, ChangeMasterKey, CryptoOperationsType, FileIdentifiers, FileAccessRights, FileCommunication}; + use crate::desfire::desfire::{ + generate_file_access_rights, generate_keysetting1, generate_keysetting2, + generate_session_key_aes, generate_session_key_des, Desfire, + }; use crate::desfire::CryptoOperationsType::TDES; - use crate::error::Error::{InvalidKeyID, CardError}; + use crate::desfire::{ + ChangeApplicationKey, ChangeMasterKey, ChangeMasterKeySettings, CreateDeleteFile, + CryptoOperationsType, FileAccessRights, FileCommunication, FileDirectoryAccess, + FileIdentifiers, + }; + use crate::error::Error::{CardError, InvalidKeyID}; + use crate::error::Result; use crate::iso7816_4::apdustatuswords::APDUStatusWord::OK; + use crate::{APDUCommand, APDUResponse, Card as CardTrait}; + use hex_literal::hex; + use mockall::{mock, predicate}; + use pcsc::{Card, Context, Disposition, Error, Protocols, Scope, ShareMode, MAX_BUFFER_SIZE}; + use std::convert::TryFrom; + use std::ffi::{CStr, CString}; + use std::fs::{read, File}; + use std::ops::Deref; mock! { pub VirtualCard {} @@ -666,15 +815,18 @@ mod tests { impl CardTrait for PCSCCard { fn connect(&mut self) -> Result<()> { - self.card = match self.ctx.connect(&self.reader, ShareMode::Shared, Protocols::ANY) { + self.card = match self + .ctx + .connect(&self.reader, ShareMode::Shared, Protocols::ANY) + { Ok(card) => Some(card), Err(err) => { // eprintln!("Failed to connect to card: {}", err); - return Err(CardError) + return Err(CardError); } }; - return Ok(()) + return Ok(()); } fn disconnect(&mut self) -> Result<()> { @@ -685,14 +837,20 @@ mod tests { // println!("{}", apdu_cmd); let apdu = Vec::::try_from(apdu_cmd).unwrap(); let mut rapdu_buf = [0; MAX_BUFFER_SIZE]; - let rapdu = match self.card.as_ref().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, Err(err) => { // eprintln!("Failed to transmit APDU command to card: {}", err); - return Err(CardError) + return Err(CardError); } }; - return Ok(APDUResponse::new(rapdu)) + return Ok(APDUResponse::new(rapdu)); } } @@ -728,50 +886,68 @@ mod tests { #[test] fn generate_key_setting1() { - assert_eq!(0x0B, - generate_keysetting1(ChangeApplicationKey::MASTERKEY as u8, - ChangeMasterKeySettings::WITHMASTERKEY, - CreateDeleteFile::ONLYMASTERKEY, - FileDirectoryAccess::NOKEY, - ChangeMasterKey::CHANGEABLE).unwrap() + assert_eq!( + 0x0B, + generate_keysetting1( + ChangeApplicationKey::MASTERKEY as u8, + ChangeMasterKeySettings::WITHMASTERKEY, + CreateDeleteFile::ONLYMASTERKEY, + FileDirectoryAccess::NOKEY, + ChangeMasterKey::CHANGEABLE + ) + .unwrap() ) } #[test] fn generate_key_setting1_changekey() { - assert_eq!(0x1B, - generate_keysetting1(0x01, - ChangeMasterKeySettings::WITHMASTERKEY, - CreateDeleteFile::ONLYMASTERKEY, - FileDirectoryAccess::NOKEY, - ChangeMasterKey::CHANGEABLE).unwrap() + assert_eq!( + 0x1B, + generate_keysetting1( + 0x01, + ChangeMasterKeySettings::WITHMASTERKEY, + CreateDeleteFile::ONLYMASTERKEY, + FileDirectoryAccess::NOKEY, + ChangeMasterKey::CHANGEABLE + ) + .unwrap() ) } #[test] - #[should_panic(expected= "InvalidKeyID")] + #[should_panic(expected = "InvalidKeyID")] fn generate_key_setting1_invalid_keyid() { - generate_keysetting1(0x10, - ChangeMasterKeySettings::WITHMASTERKEY, - CreateDeleteFile::ONLYMASTERKEY, - FileDirectoryAccess::NOKEY, - ChangeMasterKey::CHANGEABLE).unwrap(); + generate_keysetting1( + 0x10, + ChangeMasterKeySettings::WITHMASTERKEY, + CreateDeleteFile::ONLYMASTERKEY, + FileDirectoryAccess::NOKEY, + ChangeMasterKey::CHANGEABLE, + ) + .unwrap(); } #[test] fn generate_key_setting2() { - assert_eq!(0x82, generate_keysetting2(CryptoOperationsType::AES, FileIdentifiers::NOTUSED, 0x02).unwrap()); + assert_eq!( + 0x82, + generate_keysetting2(CryptoOperationsType::AES, FileIdentifiers::NOTUSED, 0x02) + .unwrap() + ); } #[test] - #[should_panic(expected= "NumKeys")] + #[should_panic(expected = "NumKeys")] fn generate_key_setting2_wrong_num_keys() { generate_keysetting2(CryptoOperationsType::AES, FileIdentifiers::NOTUSED, 0x10).unwrap(); } #[test] fn generate_file_accessrights() { - assert_eq!(0x1234, generate_file_access_rights(0x01, 0x02, 0x03, 0x04).unwrap()) + assert_eq!( + 0x1234, + generate_file_access_rights(0x01, 0x02, 0x03, 0x04).unwrap() + ) } #[test] @@ -783,16 +959,23 @@ mod tests { #[test] fn select_application() { let mut mock = MockVirtualCard::new(); - mock.expect_transmit().withf(|x: &APDUCommand| (Vec::::try_from(x.clone()).unwrap()) == vec![0x90, 0x5a, 0x00, 0x00, 0x03, 0x33, 0x22, 0x11, 0x00]).return_once(move |_| Ok(APDUResponse{ - body: None, - sw1: 0x91, - sw2: 0x00 - })); + mock.expect_transmit() + .withf(|x: &APDUCommand| { + (Vec::::try_from(x.clone()).unwrap()) + == vec![0x90, 0x5a, 0x00, 0x00, 0x03, 0x33, 0x22, 0x11, 0x00] + }) + .return_once(move |_| { + Ok(APDUResponse { + body: None, + sw1: 0x91, + sw2: 0x00, + }) + }); - let desfire = Desfire{ + let desfire = Desfire { card: Some(Box::new(mock)), cbc_iv: None, - session_key: None + session_key: None, }; assert!(desfire.select_application(0x112233).is_ok()) @@ -803,10 +986,10 @@ mod tests { fn select_application_invalidaid() { let mock = MockVirtualCard::new(); - let mut desfire = Desfire{ + let mut desfire = Desfire { card: Some(Box::new(mock)), cbc_iv: None, - session_key: None + session_key: None, }; desfire.select_application(0xff000000).unwrap(); @@ -846,15 +1029,15 @@ mod tests { let mut card = PCSCCard { ctx, reader: CString::from(reader), - card: None + card: None, }; card.connect(); - let desfire = Desfire{ + let desfire = Desfire { card: Some(Box::new(card)), cbc_iv: None, - session_key: None + session_key: None, }; assert!(desfire.select_application(0x000000).is_ok()); @@ -863,36 +1046,51 @@ mod tests { #[test] fn authenticate_des() { let mut mock = MockVirtualCard::new(); - mock.expect_transmit().withf(|x: &APDUCommand| (Vec::::try_from(x.clone()).unwrap()) == vec![0x90, 0x1a, 0x00, 0x00, 0x01, 0x00, 0x00]).return_once(move |_| Ok(APDUResponse{ - sw1: 0x91, - sw2: 0xAF, - body: Some(vec![0x2b, 0xf9, 0xa9, 0x38, 0xec, 0xca, 0x02, 0xe2]) - })); + mock.expect_transmit() + .withf(|x: &APDUCommand| { + (Vec::::try_from(x.clone()).unwrap()) + == vec![0x90, 0x1a, 0x00, 0x00, 0x01, 0x00, 0x00] + }) + .return_once(move |_| { + Ok(APDUResponse { + sw1: 0x91, + sw2: 0xAF, + body: Some(vec![0x2b, 0xf9, 0xa9, 0x38, 0xec, 0xca, 0x02, 0xe2]), + }) + }); - mock.expect_transmit().withf(|x: &APDUCommand| (Vec::::try_from(x.clone()).unwrap()) == hex!("90af000010f8cdb2eaa42a3167dfcb53852ce267fd00")).return_once(move |_| Ok(APDUResponse{ - sw1: 0x91, - sw2: 0x00, - body: Some(vec![0x07, 0xd8, 0x25, 0x60, 0x7a, 0x55, 0x2e, 0x2e]) - })); + mock.expect_transmit() + .withf(|x: &APDUCommand| { + (Vec::::try_from(x.clone()).unwrap()) + == hex!("90af000010f8cdb2eaa42a3167dfcb53852ce267fd00") + }) + .return_once(move |_| { + Ok(APDUResponse { + sw1: 0x91, + sw2: 0x00, + body: Some(vec![0x07, 0xd8, 0x25, 0x60, 0x7a, 0x55, 0x2e, 0x2e]), + }) + }); let rndA = hex!("5f7d1dd12d979173"); let mut key = CipherKey::new_empty(CipherType::TDES).unwrap(); // println!("{:x?}", key.key.deref()); - let mut desfire = Desfire{ + let mut desfire = Desfire { card: Some(Box::new(mock)), cbc_iv: None, - session_key: None + session_key: None, }; - desfire.authenticate_iso_des(0x00, key.key.as_ref(), Some(rndA)).unwrap(); + desfire + .authenticate_iso_des(0x00, key.key.as_ref(), Some(rndA)) + .unwrap(); let sessionkey_expected = hex!("5f7d1dd1f449db5c5f7d1dd1f449db5c"); let iv_expected = hex!("0000000000000000"); assert_eq!(desfire.session_key.unwrap(), sessionkey_expected); assert_eq!(desfire.cbc_iv.unwrap(), iv_expected); - } #[test] @@ -900,13 +1098,15 @@ mod tests { fn authenticate_des_invalid_keyno() { let mock = MockVirtualCard::new(); - let mut desfire = Desfire{ + let mut desfire = Desfire { card: Some(Box::new(mock)), cbc_iv: None, - session_key: None + session_key: None, }; - desfire.authenticate_iso_des(0x0F, &[0x00 as u8], None).unwrap(); + desfire + .authenticate_iso_des(0x0F, &[0x00 as u8], None) + .unwrap(); } #[test] @@ -943,22 +1143,22 @@ mod tests { let mut card = PCSCCard { ctx, reader: CString::from(reader), - card: None + card: None, }; card.connect(); - let mut desfire = Desfire{ + let mut desfire = Desfire { card: Some(Box::new(card)), cbc_iv: None, - session_key: None + session_key: None, }; desfire.select_application(0x000000); let mut key = CipherKey::new_empty(CipherType::TDES).unwrap(); match desfire.authenticate_iso_des(0x00, key.key.as_ref(), None) { - Ok(_) => {}, + Ok(_) => {} Err(err) => { // eprintln!("Failed to authenticate: {}", err); panic!("Failed to authenticate"); @@ -969,11 +1169,17 @@ mod tests { #[test] fn authenticate_aes() { let mut mock = MockVirtualCard::new(); - mock.expect_transmit().withf(|x: &APDUCommand| (Vec::::try_from(x.clone()).unwrap()) == hex!("90aa0000010000")).return_once(move |_| Ok(APDUResponse{ - sw1: 0x91, - sw2: 0xAF, - body: Some(hex!("a33856932308775cf464610c2b17a558").to_vec()) - })); + mock.expect_transmit() + .withf(|x: &APDUCommand| { + (Vec::::try_from(x.clone()).unwrap()) == hex!("90aa0000010000") + }) + .return_once(move |_| { + Ok(APDUResponse { + sw1: 0x91, + sw2: 0xAF, + body: Some(hex!("a33856932308775cf464610c2b17a558").to_vec()), + }) + }); mock.expect_transmit().withf(|x: &APDUCommand| (Vec::::try_from(x.clone()).unwrap()) == hex!("90af000020cbe9726faf54bc76b2055d0b9700e7dc97ecad5627f1d1702a16e8408d2a0ada00")).return_once(move |_| Ok(APDUResponse{ sw1: 0x91, @@ -985,20 +1191,21 @@ mod tests { let mut key = CipherKey::new_empty(CipherType::AES).unwrap(); // println!("{:x?}", key.key.deref()); - let mut desfire = Desfire{ + let mut desfire = Desfire { card: Some(Box::new(mock)), cbc_iv: None, - session_key: None + session_key: None, }; - desfire.authenticate_iso_aes(0x00, key.key.as_ref(), Some(rndA)).unwrap(); + desfire + .authenticate_iso_aes(0x00, key.key.as_ref(), Some(rndA)) + .unwrap(); let sessionkey_expected = hex!("2176770e11c512ca201d1e57fde6e15a"); let iv_expected = hex!("00000000000000000000000000000000"); assert_eq!(desfire.session_key.unwrap(), sessionkey_expected); assert_eq!(desfire.cbc_iv.unwrap(), iv_expected); - } #[test] @@ -1006,53 +1213,63 @@ mod tests { fn authenticate_aes_invalid_keyno() { let mock = MockVirtualCard::new(); - let mut desfire = Desfire{ + let mut desfire = Desfire { card: Some(Box::new(mock)), cbc_iv: None, - session_key: None + session_key: None, }; - desfire.authenticate_iso_aes(0x0F, &[0x00 as u8], None).unwrap(); + desfire + .authenticate_iso_aes(0x0F, &[0x00 as u8], None) + .unwrap(); } #[test] fn format_picc() { let mut mock = MockVirtualCard::new(); - mock.expect_transmit().withf(|x: &APDUCommand| (Vec::::try_from(x.clone()).unwrap()) == hex!("90fc000000")).return_once(move |_| Ok(APDUResponse{ - sw1: 0x91, - sw2: 0x00, - body: None - })); + mock.expect_transmit() + .withf(|x: &APDUCommand| { + (Vec::::try_from(x.clone()).unwrap()) == hex!("90fc000000") + }) + .return_once(move |_| { + Ok(APDUResponse { + sw1: 0x91, + sw2: 0x00, + body: None, + }) + }); - let mut desfire = Desfire{ + let mut desfire = Desfire { card: Some(Box::new(mock)), cbc_iv: None, - session_key: None + session_key: None, }; assert!(desfire.format_picc().is_ok()); - - - } #[test] fn create_application() { let mut mock = MockVirtualCard::new(); - mock.expect_transmit().withf(|x: &APDUCommand| (Vec::::try_from(x.clone()).unwrap()) == hex!("90ca000005eeffaa0b8200")).return_once(move |_| Ok(APDUResponse{ - sw1: 0x91, - sw2: 0x00, - body: None - })); + mock.expect_transmit() + .withf(|x: &APDUCommand| { + (Vec::::try_from(x.clone()).unwrap()) == hex!("90ca000005eeffaa0b8200") + }) + .return_once(move |_| { + Ok(APDUResponse { + sw1: 0x91, + sw2: 0x00, + body: None, + }) + }); - let mut desfire = Desfire{ + let mut desfire = Desfire { card: Some(Box::new(mock)), cbc_iv: None, - session_key: None + session_key: None, }; assert!(desfire.create_application(0xAAFFEE, 0x0b, 0x82).is_ok()); - } #[test] @@ -1060,10 +1277,10 @@ mod tests { fn create_application_invalid_aid() { let mut mock = MockVirtualCard::new(); - let mut desfire = Desfire{ + let mut desfire = Desfire { card: Some(Box::new(mock)), cbc_iv: None, - session_key: None + session_key: None, }; desfire.create_application(0xFF000000, 0x0b, 0x82).unwrap(); @@ -1083,7 +1300,7 @@ mod tests { let mut desfire = Desfire { card: Some(Box::new(mock)), cbc_iv: Some(hex!("00000000000000000000000000000000").to_vec()), - session_key: Some(hex!("a8514dd0350f3dfbc86e80744bcc9b57").to_vec()) + session_key: Some(hex!("a8514dd0350f3dfbc86e80744bcc9b57").to_vec()), }; assert!(desfire.change_key_aes(0x00, new_key.as_ref(), 0x10).is_ok()); @@ -1097,7 +1314,7 @@ mod tests { let mut desfire = Desfire { card: Some(Box::new(mock)), cbc_iv: None, - session_key: None + session_key: None, }; desfire.change_key_aes(0x0F, &[0x00 as u8], 0x10).unwrap(); @@ -1118,10 +1335,12 @@ mod tests { let mut desfire = Desfire { card: Some(Box::new(mock)), cbc_iv: Some(hex!("00000000000000000000000000000000").to_vec()), - session_key: Some(hex!("1677623e1e158a62dc3d128db55f947d").to_vec()) + session_key: Some(hex!("1677623e1e158a62dc3d128db55f947d").to_vec()), }; - assert!(desfire.change_other_key_aes(0x01, new_key.as_ref(), old_key.as_ref(), 0x10).is_ok()); + assert!(desfire + .change_other_key_aes(0x01, new_key.as_ref(), old_key.as_ref(), 0x10) + .is_ok()); } #[test] @@ -1132,29 +1351,40 @@ mod tests { let mut desfire = Desfire { card: Some(Box::new(mock)), cbc_iv: None, - session_key: None + session_key: None, }; - desfire.change_other_key_aes(0x0F, &[0x00 as u8], &[0x00 as u8],0x10).unwrap(); + desfire + .change_other_key_aes(0x0F, &[0x00 as u8], &[0x00 as u8], 0x10) + .unwrap(); } #[test] fn create_file_standard() { let mut mock = MockVirtualCard::new(); - mock.expect_transmit().withf(|x: &APDUCommand| (Vec::::try_from(x.clone()).unwrap()) == hex!("90cd000007010000e0f0000000")).return_once(move |_| Ok(APDUResponse { - sw1: 0x91, - sw2: 0x00, - body: None - })); + mock.expect_transmit() + .withf(|x: &APDUCommand| { + (Vec::::try_from(x.clone()).unwrap()) == hex!("90cd000007010000e0f0000000") + }) + .return_once(move |_| { + Ok(APDUResponse { + sw1: 0x91, + sw2: 0x00, + body: None, + }) + }); let mut desfire = Desfire { card: Some(Box::new(mock)), cbc_iv: None, - session_key: None + session_key: None, }; - let access_rights = generate_file_access_rights(FileAccessRights::FREE as u8, 0x00, 0x00, 0x00).unwrap(); - assert!(desfire.create_file_standard(0x01, FileCommunication::PLAIN, access_rights, 0xF0).is_ok()); + let access_rights = + generate_file_access_rights(FileAccessRights::FREE as u8, 0x00, 0x00, 0x00).unwrap(); + assert!(desfire + .create_file_standard(0x01, FileCommunication::PLAIN, access_rights, 0xF0) + .is_ok()); } #[test] @@ -1165,26 +1395,36 @@ mod tests { let mut desfire = Desfire { card: Some(Box::new(mock)), cbc_iv: None, - session_key: None + session_key: None, }; - let access_rights = generate_file_access_rights(FileAccessRights::FREE as u8, 0x00, 0x00, 0x00).unwrap(); - desfire.create_file_standard(0xFF, FileCommunication::PLAIN, access_rights, 0xF0).unwrap(); + let access_rights = + generate_file_access_rights(FileAccessRights::FREE as u8, 0x00, 0x00, 0x00).unwrap(); + desfire + .create_file_standard(0xFF, FileCommunication::PLAIN, access_rights, 0xF0) + .unwrap(); } #[test] fn write_data() { let mut mock = MockVirtualCard::new(); - mock.expect_transmit().withf(|x: &APDUCommand| (Vec::::try_from(x.clone()).unwrap()) == hex!("903d00000f01000000080000546573743132333400")).return_once(move |_| Ok(APDUResponse { - sw1: 0x91, - sw2: 0x00, - body: None - })); + mock.expect_transmit() + .withf(|x: &APDUCommand| { + (Vec::::try_from(x.clone()).unwrap()) + == hex!("903d00000f01000000080000546573743132333400") + }) + .return_once(move |_| { + Ok(APDUResponse { + sw1: 0x91, + sw2: 0x00, + body: None, + }) + }); let mut desfire = Desfire { card: Some(Box::new(mock)), cbc_iv: None, - session_key: None + session_key: None, }; let data = "Test1234".as_bytes(); @@ -1206,16 +1446,23 @@ mod tests { body: None })); - mock.expect_transmit().withf(|x: &APDUCommand| (Vec::::try_from(x.clone()).unwrap()) == hex!("903d000019015e000012000033345465737431323334546573743132333400")).return_once(move |_| Ok(APDUResponse { - sw1: 0x91, - sw2: 0x00, - body: None - })); + mock.expect_transmit() + .withf(|x: &APDUCommand| { + (Vec::::try_from(x.clone()).unwrap()) + == hex!("903d000019015e000012000033345465737431323334546573743132333400") + }) + .return_once(move |_| { + Ok(APDUResponse { + sw1: 0x91, + sw2: 0x00, + body: None, + }) + }); let mut desfire = Desfire { card: Some(Box::new(mock)), cbc_iv: None, - session_key: None + session_key: None, }; let data = "Test1234Test1234Test1234Test1234Test1234Test1234Test1234Test1234Test1234Test1234Test1234Test1234Test1234Test1234".as_bytes(); @@ -1230,7 +1477,7 @@ mod tests { let mut desfire = Desfire { card: Some(Box::new(mock)), cbc_iv: None, - session_key: None + session_key: None, }; desfire.write_data(0xFF, 0x00, &[0 as u8; 1]).unwrap(); @@ -1239,20 +1486,34 @@ mod tests { #[test] fn read_data() { let mut mock = MockVirtualCard::new(); - mock.expect_transmit().withf(|x: &APDUCommand| (Vec::::try_from(x.clone()).unwrap()) == hex!("90bd0000070100000020000000")).return_once(move |_| Ok(APDUResponse { - sw1: 0x91, - sw2: 0x00, - body: Some(hex!("54657374313233340000000000000000000000000000000000000000000000009100").to_vec()) - })); + mock.expect_transmit() + .withf(|x: &APDUCommand| { + (Vec::::try_from(x.clone()).unwrap()) == hex!("90bd0000070100000020000000") + }) + .return_once(move |_| { + Ok(APDUResponse { + sw1: 0x91, + sw2: 0x00, + body: Some( + hex!( + "54657374313233340000000000000000000000000000000000000000000000009100" + ) + .to_vec(), + ), + }) + }); let mut desfire = Desfire { card: Some(Box::new(mock)), cbc_iv: None, - session_key: None + session_key: None, }; let data = desfire.read_data(0x01, 0x00, 0x20).unwrap(); - assert_eq!("Test1234", String::from_utf8(data).unwrap().replace("\0", "")); + assert_eq!( + "Test1234", + String::from_utf8(data).unwrap().replace("\0", "") + ); } #[test] @@ -1267,11 +1528,14 @@ mod tests { let mut desfire = Desfire { card: Some(Box::new(mock)), cbc_iv: None, - session_key: None + session_key: None, }; let data = desfire.read_data(0x01, 0x00, 0x20).unwrap(); - assert_eq!("Test1234", String::from_utf8(data).unwrap().replace("\0", "")); + assert_eq!( + "Test1234", + String::from_utf8(data).unwrap().replace("\0", "") + ); } #[test] @@ -1289,20 +1553,29 @@ mod tests { body: Some(hex!("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009100").to_vec()) })); - mock.expect_transmit().withf(|x: &APDUCommand| (Vec::::try_from(x.clone()).unwrap()) == hex!("90bd000007015e000002000000")).return_once(move |_| Ok(APDUResponse { - sw1: 0x91, - sw2: 0x00, - body: Some(hex!("00009100").to_vec()) - })); + mock.expect_transmit() + .withf(|x: &APDUCommand| { + (Vec::::try_from(x.clone()).unwrap()) == hex!("90bd000007015e000002000000") + }) + .return_once(move |_| { + Ok(APDUResponse { + sw1: 0x91, + sw2: 0x00, + body: Some(hex!("00009100").to_vec()), + }) + }); let mut desfire = Desfire { card: Some(Box::new(mock)), cbc_iv: None, - session_key: None + session_key: None, }; let data = desfire.read_data(0x01, 0x00, 0x60).unwrap(); - assert_eq!("Test1234", String::from_utf8(data).unwrap().replace("\0", "")); + assert_eq!( + "Test1234", + String::from_utf8(data).unwrap().replace("\0", "") + ); } #[test] @@ -1313,9 +1586,9 @@ mod tests { let mut desfire = Desfire { card: Some(Box::new(mock)), cbc_iv: None, - session_key: None + session_key: None, }; desfire.read_data(0xFF, 0x00, 0x20).unwrap(); } -} \ No newline at end of file +} diff --git a/src/desfire/mod.rs b/src/desfire/mod.rs index 448dde7..792a413 100644 --- a/src/desfire/mod.rs +++ b/src/desfire/mod.rs @@ -167,4 +167,4 @@ pub enum FileTypes { mod apduinstructions; mod apdustatuscodes; pub mod desfire; -pub use desfire::Desfire; \ No newline at end of file +pub use desfire::Desfire; diff --git a/src/lib.rs b/src/lib.rs index 044210a..c4470e6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,7 +3,7 @@ //! **WARNING** This is alpha quality software at best and should not be relied upon. **WARNING** //! //!The `desfire` Crate provides the cryptographic methods and specific commands for interfacing -//! with NXP MiFare Desfire cards. +//! with NXP MiFare Desfire cards. //! use crate::iso7816_4::apducommand::APDUCommand; @@ -33,4 +33,3 @@ pub trait Card { /// Application Protocol Data Unit Response - ISO 7816 fn transmit(&self, apdu_cmd: APDUCommand) -> Result; } -