using log4net; using NFC.Cards.NXP_MIFARE_DESFire.Enums; using NFC.Cards.NXP_MIFARE_DESFire.Exceptions; using NFC.Helper; using NFC.Helper.Crypto.Cipher; using NFC.Helper.Crypto.CRC; using NFC.Interfaces; using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Engines; using Org.BouncyCastle.Crypto.Macs; using Org.BouncyCastle.Crypto.Parameters; using System; using System.Collections.Generic; using System.Linq; using System.Security.Cryptography; namespace NFC.Cards.NXP_MIFARE_DESFire { public class NXP_MIFARE_DESFire { // Docs https://hackmd.io/qATu8uYdRnOC40aFrB9afg #region Log private static readonly ILog _Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); #endregion #region Contructors /// /// Construct MIFRARE_DESFire Object with ICard Interface /// /// Implementation of ICard, only transmit is used public NXP_MIFARE_DESFire(INFCService nfcService) { _NFCService = nfcService; } #endregion #region Properties /// /// ICard Implementation used to transmit APDUCommands and recive APDUResponses /// private readonly INFCService _NFCService; /// /// SessionKey, is set after Successfull Authentication /// public byte[] _SessionKey; /// /// Initialation Vector for CBC Encryption /// Is 0 bytes after Successfull Authentication /// public byte[] _IV; #endregion #region Methods #region Helper Methods /// /// Check APDU Response for DESFire Error Codes /// https://www.nxp.com/docs/en/data-sheet/MF2DLHX0.pdf /// Section: 11.3 /// public void CheckAPDUResponse(APDUResponse response) { if (response == null) { throw new ArgumentNullException("Response cannot be null."); } if (response.SW1 == 0x91) { switch (response.SW2) { case 0x00: // OPERATION_OK return; case 0x0C: // NO_CHANGES return; case 0x1C: // ILLEGAL_COMMAND_CODE throw new IllegalCommandCodeException(); case 0x1E: // INTEGRITY_ERROR throw new IntegrityErrorException(); case 0x40: // NO_SUCH_KEY throw new NoSuchKeyException(); case 0x7E: // LENGTH_ERROR throw new LengthErrorException(); case 0x9D: // PERMISSION_DENIED throw new PermissionDeniedException(); case 0x9E: // PARAMETER_ERROR throw new ParameterErrorException(); case 0xAD: // AUTHENTICATION_DELAY throw new AuthenticationDelayException(); case 0xAE: // AUTHENTICATION_ERROR throw new AuthenticationErrorException(); case 0xAF: // ADDITIONAL_FRAME return; case 0xBE: // BOUNDARY_ERROR throw new BoundaryErrorException(); case 0xCA: // COMMAND_ABORTED throw new CommandAbortedException(); case 0xDE: // DUPLICATE_ERROR throw new DuplicateErrorException(); case 0xF0: // FILE_NOT_FOUND throw new FileNotFoundException(); default: break; } } throw new Exception(string.Format("Unknown Response Code: 0x{0}.", BitConverter.ToString(new byte[] { response.SW1, response.SW2 }).Replace("-", string.Empty))); } #endregion #region Crypto Operation /// /// Generates SessionKey for DES Authentification /// /// 16Byte SessionKey public byte[] GenerateSesionKey_DES(byte[] rndA, byte[] rndB) { byte[] sesssionkey = new byte[8]; for (int i = 0; i < sesssionkey.Length; i++) { if (i < 4) { sesssionkey[i] = rndA[i]; } else { sesssionkey[i] = rndB[i - 4]; } } // DES SessionKey is a double DES Key return ByteOperation.Concatenate(sesssionkey, sesssionkey); } /// /// Generates SessionKey for AES Authentification /// /// 16Byte SessionKey public byte[] GenerateSesionKey_AES(byte[] rndA, byte[] rndB) { byte[] sesssionkey = new byte[16]; for (int i = 0; i < sesssionkey.Length; i++) { if (i < 4) { sesssionkey[i] = rndA[i]; } else if (i >= 4 && i < 8) { sesssionkey[i] = rndB[i - 4]; } else if (i >= 8 && i < 12) { sesssionkey[i] = rndA[i + 4]; } else { sesssionkey[i] = rndB[i]; } } return sesssionkey; } #endregion #region Configuration Generator /// /// Genearte KeySetting1 for Application Settings or PICC Setting /// public byte GenerateKeySetting1(ChangeApplicationKey changeKey, ChangeMasterKeySettings changeMasterKeySettings, CreateDeleteFile createDeleteFile, FileDirectoryAccess fileDirectoryAccess, ChangeMasterKey changeMasterKey) { return (byte)(((byte)changeKey << 4) | (byte)changeMasterKeySettings | (byte)createDeleteFile | (byte)fileDirectoryAccess | (byte)changeMasterKey); } /// /// Genearte KeySetting1 for Application Settings or PICC Setting /// /// ID of Key for changing Application Keys /// public byte GenerateKeySetting1(byte changeKey, ChangeMasterKeySettings changeMasterKeySettings, CreateDeleteFile createDeleteFile, FileDirectoryAccess fileDirectoryAccess, ChangeMasterKey changeMasterKey) { if (changeKey < 0x01 || changeKey >= 0x0E) { throw new ArgumentOutOfRangeException(); } return GenerateKeySetting1((ChangeApplicationKey)changeKey, changeMasterKeySettings, createDeleteFile, fileDirectoryAccess, changeMasterKey); } /// /// Genearte KeySetting2 for Application Creation /// /// Number of keys that can be stored within the application (0x01-0x0D) /// public byte GenerateKeySetting2(CryptoOperationsType cryptoOperations, FileIdentifies fileIdentifies, byte numberOfKeys) { if (numberOfKeys < 0x01 || numberOfKeys >= 0x0D) { throw new ArgumentOutOfRangeException(); } return (byte)((byte)cryptoOperations | (byte)fileIdentifies | numberOfKeys); } /// /// Generate FileAccess Rights for File Settings /// Use enum AccesRights for Free or Never Option /// /// KeyID for Read Access /// KeyID for Write Access /// KeyID for Read and Write Access /// KeyID for Configuration Access public UInt16 GenerateFileAccessRights(byte read, byte write, byte read_write, byte configure) { if (read > 0x0F || write > 0x0F || read_write > 0x0F || configure > 0x0F) { throw new ArgumentOutOfRangeException("One KeyID is not valid"); } return (UInt16)((read << 12) | (write << 8) | (read_write << 4) | configure); } #endregion #region DESFire Commands /// /// Select Application by ApplicationID (AID) /// /// 3 Byte AID public void SelectApplication(UInt32 aid) { if (aid > 0xFFFFFF) { throw new ArgumentOutOfRangeException("AID is too large"); } byte[] id_byte = BitConverter.GetBytes(aid); _Log.InfoFormat("Select Application: {0}", HexConverter.ConvertToHexString(id_byte.ToArray())); APDUCommand cmd_SelectApplication = new APDUCommand(IsoCase.Case4Short) { CLA = 0x90, INS = 0x5A, Data = new byte[] { id_byte[0], id_byte[1], id_byte[2] } }; _Log.Debug(cmd_SelectApplication.ToString()); HexConverter.ConvertToHexString(cmd_SelectApplication.ToArray()); APDUResponse response = _NFCService.Transmit(cmd_SelectApplication); _Log.DebugFormat(response.ToString()); CheckAPDUResponse(response); } /// /// Authenticate to PICC, with ISO Authenticate for DES Key /// /// 0x01 - 0x0D /// Array of 8/16 Bytes /// !!! WARNING For Testing only !!! public void AuthenticateISO_DES(byte key_id, byte[] key, byte[] rndA = null) { if (key_id >= 0x0E) { throw new ArgumentOutOfRangeException("KeyID is invalid"); } _Log.InfoFormat("Authenticate with DES Key No: 0x{0:x}", key_id); // Sepearte Initialisation Vector for Authentication Process byte[] iv = new byte[8]; APDUCommand cmd_challange_request = new APDUCommand(IsoCase.Case4Short) { CLA = 0x90, INS = 0x1A, Data = new byte[] { key_id } }; _Log.Debug(cmd_challange_request.ToString()); APDUResponse response = _NFCService.Transmit(cmd_challange_request); _Log.Debug(response.ToString()); CheckAPDUResponse(response); byte[] rndB_enc = response.Body; _Log.DebugFormat("rndB_enc: {0}", HexConverter.ConvertToHexString(rndB_enc)); TDES des = new TDES(); byte[] rndB = des.Decrypt(rndB_enc, key, ByteOperation.GenerateEmptyArray(8)); _Log.DebugFormat("rndB: {0}", HexConverter.ConvertToHexString(rndB)); rndB.CopyTo(iv, 0); byte[] rndB_rl = ByteOperation.RotateLeft(rndB); _Log.DebugFormat("rndB_enc: {0}", HexConverter.ConvertToHexString(rndB_rl)); if (rndA == null) { rndA = new byte[8]; RNGCryptoServiceProvider cryptoProvider = new RNGCryptoServiceProvider(); cryptoProvider.GetBytes(rndA); } _Log.DebugFormat("rndA: {0}", HexConverter.ConvertToHexString(rndA)); byte[] rndAB = ByteOperation.Concatenate(rndA, rndB_rl); _Log.DebugFormat("rndAB: {0}", HexConverter.ConvertToHexString(rndAB)); byte[] rndAB_enc = des.Encrypt(rndAB, key, rndB_enc); _Log.DebugFormat("rndAB_enc: {0}", HexConverter.ConvertToHexString(rndAB_enc)); iv = ByteOperation.ExtractLastBlock(rndAB_enc, 8); APDUCommand cmd_challange_response = new APDUCommand(IsoCase.Case4Short) { CLA = 0x90, INS = 0xAF, Data = rndAB_enc }; _Log.Debug(cmd_challange_response.ToString()); response = _NFCService.Transmit(cmd_challange_response); _Log.Debug(response.ToString()); CheckAPDUResponse(response); byte[] encryptedRndAFromCard = response.Body; _Log.DebugFormat("encryptedRndAFromCard: {0}", HexConverter.ConvertToHexString(encryptedRndAFromCard)); byte[] rotatedRndAFromCard = des.Decrypt(encryptedRndAFromCard, key, iv); _Log.DebugFormat("rotatedRndAFromCard: {0}", HexConverter.ConvertToHexString(rotatedRndAFromCard)); byte[] rndAFromCard = ByteOperation.RotateRight(rotatedRndAFromCard); _Log.DebugFormat("rndAFromCard: {0}", HexConverter.ConvertToHexString(rndAFromCard)); if (!rndA.SequenceEqual(rndAFromCard)) { throw new Exception("Authentication failed, PICC Challenge is invalid."); } _Log.Info("Authenticated"); _SessionKey = GenerateSesionKey_DES(rndA, rndB); _Log.DebugFormat("SessionKey: {0}", HexConverter.ConvertToHexString(_SessionKey)); _IV = ByteOperation.GenerateEmptyArray(8); _Log.DebugFormat("IV: {0}", HexConverter.ConvertToHexString(_IV)); } /// /// Format PICC /// Need Authentication for PICC / Application 0x000000 /// public void Format() { _Log.Info("Format PICC"); APDUCommand cmd_format = new APDUCommand(IsoCase.Case2Short) { CLA = 0x90, INS = 0xFC, }; _Log.Debug(cmd_format.ToString()); APDUResponse response = _NFCService.Transmit(cmd_format); _Log.Debug(response.ToString()); CheckAPDUResponse(response); } /// /// Create Application for ApplicationID /// /// 3 Byte ID public void CreateApplication(UInt32 aid, byte keysetting1, byte keysetting2) { if (aid > 0xFFFFFF) { throw new ArgumentOutOfRangeException("AID is too large"); } byte[] id_byte = BitConverter.GetBytes(aid); _Log.InfoFormat("Create Application: {0}", HexConverter.ConvertToHexString(id_byte.ToArray())); APDUCommand cmd_CreateApplication = new APDUCommand(IsoCase.Case4Short) { CLA = 0x90, INS = 0xCA, Data = new byte[] { id_byte[0], id_byte[1], id_byte[2], keysetting1, keysetting2 } }; _Log.Debug(cmd_CreateApplication.ToString()); APDUResponse response = _NFCService.Transmit(cmd_CreateApplication); _Log.Debug(response.ToString()); CheckAPDUResponse(response); } /// /// Authenticate to PICC, with ISO Authenticate /// /// 0x01 - 0x0D /// Array of 16 Bytes /// !!! WARNING For Testing only !!! public void AuthenticateISO_AES(byte key_id, byte[] key, byte[] rndA = null) { if (key_id >= 0x0E) { throw new ArgumentOutOfRangeException("KeyID is invalid"); } _Log.InfoFormat("Authenticate with AES Key No: 0x{0:x}", key_id); // Sepearte InitialisationVector for Authentication byte[] iv = new byte[16]; APDUCommand cmd_challange_request = new APDUCommand(IsoCase.Case4Short) { CLA = 0x90, INS = 0xAA, Data = new byte[] { key_id } }; _Log.Debug(cmd_challange_request.ToString()); APDUResponse response = _NFCService.Transmit(cmd_challange_request); _Log.Debug(response.ToString()); CheckAPDUResponse(response); byte[] rndB_enc = response.Body; _Log.DebugFormat("rndB_enc: {0}", HexConverter.ConvertToHexString(rndB_enc)); AES aes = new AES(); byte[] rndB = aes.Decrypt(rndB_enc, key, ByteOperation.GenerateEmptyArray(16)); _Log.DebugFormat("rndB: {0}", HexConverter.ConvertToHexString(rndB)); rndB.CopyTo(iv, 0); byte[] rndB_rl = ByteOperation.RotateLeft(rndB); _Log.DebugFormat("rndB_enc: {0}", HexConverter.ConvertToHexString(rndB_rl)); if (rndA == null) { rndA = new byte[16]; RNGCryptoServiceProvider cryptoProvider = new RNGCryptoServiceProvider(); cryptoProvider.GetBytes(rndA); } _Log.DebugFormat("rndA: {0}", HexConverter.ConvertToHexString(rndA)); byte[] rndAB = ByteOperation.Concatenate(rndA, rndB_rl); _Log.DebugFormat("rndAB: {0}", HexConverter.ConvertToHexString(rndAB)); byte[] rndAB_enc = aes.Encrypt(rndAB, key, rndB_enc); _Log.DebugFormat("rndAB_enc: {0}", HexConverter.ConvertToHexString(rndAB_enc)); iv = ByteOperation.ExtractLastBlock(rndAB_enc, 16); APDUCommand cmd_challange_response = new APDUCommand(IsoCase.Case4Short) { CLA = 0x90, INS = 0xAF, Data = rndAB_enc }; _Log.Debug(cmd_challange_response.ToString()); response = _NFCService.Transmit(cmd_challange_response); _Log.Debug(response.ToString()); CheckAPDUResponse(response); byte[] encryptedRndAFromCard = response.Body; _Log.DebugFormat("encryptedRndAFromCard: {0}", HexConverter.ConvertToHexString(encryptedRndAFromCard)); byte[] rotatedRndAFromCard = aes.Decrypt(encryptedRndAFromCard, key, iv); _Log.DebugFormat("rotatedRndAFromCard: {0}", HexConverter.ConvertToHexString(rotatedRndAFromCard)); byte[] rndAFromCard = ByteOperation.RotateRight(rotatedRndAFromCard); _Log.DebugFormat("rndAFromCard: {0}", HexConverter.ConvertToHexString(rndAFromCard)); if (!rndA.SequenceEqual(rndAFromCard)) { throw new Exception("Authentication failed, PICC Challenge is invalid."); } _SessionKey = GenerateSesionKey_AES(rndA, rndB); _Log.DebugFormat("SessionKey: {0}", HexConverter.ConvertToHexString(_SessionKey)); _IV = ByteOperation.GenerateEmptyArray(16); _Log.DebugFormat("IV: {0}", HexConverter.ConvertToHexString(_IV)); } /// /// Change AES key, the same as Authenticated /// /// 0x01 - 0x0D /// Array of 16 Bytes /// Version of Key(min. 0x10) public void ChangeKey_AES(byte key_id, byte[] new_key, byte key_version) { if (key_id >= 0x0E) { throw new ArgumentOutOfRangeException("KeyID is invalid"); } _Log.InfoFormat("Change AES Key No: 0x{0:x}", key_id); byte[] header = new byte[] { 0xC4, key_id }; _Log.DebugFormat("header: {0}", HexConverter.ConvertToHexString(header)); // AES Key Version is Append to Key byte[] key_and_version = ByteOperation.Concatenate(new_key, new byte[] { key_version }); byte[] command = ByteOperation.Concatenate(header, key_and_version); _Log.DebugFormat("command: {0}", HexConverter.ConvertToHexString(command)); CRC32 crc32 = new CRC32(); byte[] crc = crc32.Calculate(command); _Log.DebugFormat("crc: {0}", HexConverter.ConvertToHexString(crc)); byte[] cryptogram = ByteOperation.Concatenate(key_and_version, crc); _Log.DebugFormat("cryptogram: {0}", HexConverter.ConvertToHexString(cryptogram)); byte[] cryptogram_block = ByteOperation.ExpandToBlockSize(cryptogram, 16); _Log.DebugFormat("cryptogram_block: {0}", HexConverter.ConvertToHexString(cryptogram_block)); AES aes = new AES(); byte[] cryptogram_enc = aes.Encrypt(cryptogram_block, _SessionKey, _IV); _Log.DebugFormat("cryptogram_enc: {0}", HexConverter.ConvertToHexString(cryptogram_enc)); _IV = ByteOperation.ExtractLastBlock(cryptogram_enc, 16); _Log.DebugFormat("_IV: {0}", HexConverter.ConvertToHexString(_IV)); byte[] data = ByteOperation.Concatenate(new byte[] { key_id }, cryptogram_enc); _Log.DebugFormat("data: {0}", HexConverter.ConvertToHexString(data)); APDUCommand cmd_ChangeKey = new APDUCommand(IsoCase.Case4Short) { CLA = 0x90, INS = 0xC4, Data = data }; _Log.Debug(cmd_ChangeKey.ToString()); APDUResponse response = _NFCService.Transmit(cmd_ChangeKey); _Log.Debug(response.ToString()); CheckAPDUResponse(response); } /// /// Change AES key, other than Authenticated /// /// 0x01 - 0x0D /// Array of 16 Bytes /// Version of Key(min. 0x10) public void ChangeOtherKey_AES(byte key_id, byte[] new_key, byte[] old_key, byte key_version) { if (key_id >= 0x0E) { throw new ArgumentOutOfRangeException("KeyID is invalid"); } _Log.InfoFormat("Change AES Key No: 0x{0:x}", key_id); byte[] header = new byte[] { 0xC4, key_id }; _Log.DebugFormat("header: {0}", HexConverter.ConvertToHexString(header)); byte[] key_xor = ByteOperation.XOR(new_key, old_key); // AES Key Version is Append to Key byte[] key_and_version = ByteOperation.Concatenate(key_xor, new byte[] { key_version }); byte[] command = ByteOperation.Concatenate(header, key_and_version); _Log.DebugFormat("command: {0}", HexConverter.ConvertToHexString(command)); CRC32 crc32 = new CRC32(); byte[] crc_cmd = crc32.Calculate(command); _Log.DebugFormat("crc_cmd: {0}", HexConverter.ConvertToHexString(crc_cmd)); byte[] crc_key = crc32.Calculate(new_key); _Log.DebugFormat("crc_key: {0}", HexConverter.ConvertToHexString(crc_key)); byte[] cryptogram = ByteOperation.Concatenate(key_and_version, crc_cmd); cryptogram = ByteOperation.Concatenate(cryptogram, crc_key); _Log.DebugFormat("cryptogram: {0}", HexConverter.ConvertToHexString(cryptogram)); byte[] cryptogram_block = ByteOperation.ExpandToBlockSize(cryptogram, 16); _Log.DebugFormat("cryptogram_block: {0}", HexConverter.ConvertToHexString(cryptogram_block)); AES aes = new AES(); byte[] cryptogram_enc = aes.Encrypt(cryptogram_block, _SessionKey, _IV); _Log.DebugFormat("cryptogram_enc: {0}", HexConverter.ConvertToHexString(cryptogram_enc)); _IV = ByteOperation.ExtractLastBlock(cryptogram_enc, 16); _Log.DebugFormat("_IV: {0}", HexConverter.ConvertToHexString(_IV)); byte[] data = ByteOperation.Concatenate(new byte[] { key_id }, cryptogram_enc); _Log.DebugFormat("data: {0}", HexConverter.ConvertToHexString(data)); APDUCommand cmd_ChangeKey = new APDUCommand(IsoCase.Case4Short) { CLA = 0x90, INS = 0xC4, Data = data }; _Log.Debug(cmd_ChangeKey.ToString()); APDUResponse response = _NFCService.Transmit(cmd_ChangeKey); _Log.Debug(response.ToString()); CheckAPDUResponse(response); } /// /// Create Standard Data File /// /// ID of File (0x00 - 0x20) /// Type of File Communicaton /// Access Rights for File /// Size of File in Bytes public void CreateFile_Standard(byte file_id, FileCommunication communication, UInt16 accessRights, UInt32 size) { if (file_id >= 0x20) { throw new ArgumentOutOfRangeException("FileID is to large"); } _Log.DebugFormat("Create STD File: {0}", file_id); byte[] accessRights_byte = BitConverter.GetBytes(accessRights); byte[] size_byte_tolong = BitConverter.GetBytes(size); // Use only 3 Bytes byte[] size_byte = new byte[] { size_byte_tolong[0], size_byte_tolong[1], size_byte_tolong[2], }; byte[] data = new byte[] { file_id, (byte)communication }; APDUCommand cmd_CreateFile_Standard = new APDUCommand(IsoCase.Case4Short) { CLA = 0x90, INS = 0xCD, Data = ByteOperation.Concatenate(data, accessRights_byte, size_byte) }; _Log.Debug(cmd_CreateFile_Standard.ToString()); APDUResponse response = _NFCService.Transmit(cmd_CreateFile_Standard); _Log.DebugFormat(response.ToString()); CheckAPDUResponse(response); } /// /// Write Data to File /// /// ID of File (0x00 - 0x20) /// Offset for File /// Data to write public void WriteData(byte file_id, UInt32 offset, byte[] data) { if (file_id >= 0x20) { throw new ArgumentOutOfRangeException("FileID is to large"); } _Log.DebugFormat("Write Data to File: {0}", file_id); int max_write_bytes_pre_transaction = 47; byte[] write_buffer; long bytes_writed = 0; long length = data.Length; while (bytes_writed != data.Length) { byte[] file_id_array = new byte[] { file_id }; byte[] offset_byte_tolong = BitConverter.GetBytes(offset + bytes_writed); // Use only 3 Bytes byte[] offset_byte = new byte[] { offset_byte_tolong[0], offset_byte_tolong[1], offset_byte_tolong[2], }; long bytes_towrite; if (length - bytes_writed < max_write_bytes_pre_transaction) { bytes_towrite = length - bytes_writed; } else { bytes_towrite = max_write_bytes_pre_transaction; } byte[] length_byte_tolong = BitConverter.GetBytes(bytes_towrite); write_buffer = ByteOperation.GetSubArray(data, bytes_writed, bytes_towrite); bytes_writed += bytes_towrite; // Use only 3 Bytes byte[] length_byte = new byte[] { length_byte_tolong[0], length_byte_tolong[1], length_byte_tolong[2], }; APDUCommand cmd_WriteData = new APDUCommand(IsoCase.Case4Short) { CLA = 0x90, INS = 0x3D, Data = ByteOperation.Concatenate(file_id_array, offset_byte, length_byte, write_buffer) }; _Log.Debug(cmd_WriteData.ToString()); /* IBlockCipher cipher = new AesEngine(); CMac mac = new CMac(cipher, 8 * 8); KeyParameter keyParameter = new KeyParameter(_SessionKey); mac.Init(keyParameter); byte[] mac_buffer = ByteOperation.GenerateEmptyArray(8); byte[] cmd_raw_buffer = cmd_WriteData.ToArray(); mac.BlockUpdate(ByteOperation.Concatenate(new byte[] { 0x90, 0x3D }, cmd_WriteData.Data), 0, cmd_WriteData.Data.Length); mac.DoFinal(mac_buffer, 0); cmd_WriteData.Data = ByteOperation.Concatenate(cmd_WriteData.Data, mac_buffer); _Log.Debug(cmd_WriteData.ToString()); */ APDUResponse response = _NFCService.Transmit(cmd_WriteData); _Log.Debug(response.ToString()); CheckAPDUResponse(response); } } /// /// Read Data from File /// /// ID of File (0x00 - 0x20) /// Offset for File /// Lenght of Data public byte[] ReadData(byte file_id, UInt32 offset, UInt32 length) { if (file_id >= 0x20) { throw new ArgumentOutOfRangeException("FileID is to large"); } _Log.DebugFormat("Read Data from File: {0}", file_id); int max_read_bytes_pre_transaction = 47; long bytes_readed = 0; List read_data = new List(); while (bytes_readed != length) { byte[] data = new byte[] { file_id }; byte[] offset_byte_tolong = BitConverter.GetBytes(offset + bytes_readed); // Use only 3 Bytes byte[] offset_byte = new byte[] { offset_byte_tolong[0], offset_byte_tolong[1], offset_byte_tolong[2], }; long bytes_toread; if (length - bytes_readed < max_read_bytes_pre_transaction) { bytes_toread = length - bytes_readed; } else { bytes_toread = max_read_bytes_pre_transaction; } byte[] length_byte_tolong = BitConverter.GetBytes(bytes_toread); bytes_readed += bytes_toread; // Use only 3 Bytes byte[] length_byte = new byte[] { length_byte_tolong[0], length_byte_tolong[1], length_byte_tolong[2], }; APDUCommand cmd_ReadData = new APDUCommand(IsoCase.Case4Short) { CLA = 0x90, INS = 0xBD, Data = ByteOperation.Concatenate(data, offset_byte, length_byte) }; _Log.Debug(cmd_ReadData.ToString()); APDUResponse response = _NFCService.Transmit(cmd_ReadData); _Log.Debug(response.ToString()); CheckAPDUResponse(response); // Remove CMAC from Body read_data.AddRange(ByteOperation.GetSubArray(response.Body, 0, bytes_toread)); } return read_data.ToArray(); } /// /// Get all ApplicationIDS from PICC /// /// AIDs (3 Byte) as Array //public UInt32[] GetApplicationIDs() //{ // _Log.Debug("Start GetApplicationIDs"); // APDUCommand cmd = new APDUCommand(IsoCase.Case2Short) // { // CLA = 0x90, // INS = (byte)APDUInstructions.GET_APPLICATION_IDS // }; // APDUResponse response = _NFCService.Transmit(cmd); // CheckAPDUResponse(response); // if (response.Body.Length % 3 != 0) // { // throw new Exception(string.Format("Invalid body length (was: {0}).", response.Body.Length)); // } // if (response.Body.Length == 0) // { // throw new Exception("Missing PICC Entry 0x000000."); // } // List applicationIDs = new List(); // for (int i = 0; i < response.Body.Length; i += 3) // { // UInt32 new_applicationID = 0; // new_applicationID = (UInt32)((response.Body[i] << 16) + (response.Body[i + 1] << 8) + response.Body[i + 2]); // applicationIDs.Add(new_applicationID); // } // _Log.Debug("End GetApplicationIDs"); // return applicationIDs.ToArray(); //} /// /// Delete Application by ID /// /// 3 Byte ID //public void DeleteApplication(UInt32 id) //{ // byte[] id_byte = BitConverter.GetBytes(id); // APDUCommand cmd = new APDUCommand(IsoCase.Case4Short) // { // CLA = 0x90, // INS = (byte)APDUInstructions.DELETE_APPLICATION, // Data = new byte[] // { // id_byte[0], // id_byte[1], // id_byte[2] // }, // Le = 0x00 // }; // APDUResponse response = _NFCService.Transmit(cmd); // CheckAPDUResponse(response); //} #endregion #endregion } }