using NFC.Crypto; using NFC.Mifare_DESFire.Enums; using PCSC.Iso7816; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net.Http; using System.Text; namespace NFC.Mifare_DESFire { public class MifareDESFire { public byte[] GenerateDefaultKey(int size) { List key = new List(); for (int i = 0; i < size; i++) { key.Add(0); } return key.ToArray(); } private ICard _Card; public MifareDESFire(ICard card) { _Card = card; } /// /// Create new Application with AID /// /// Appilication ID public APDUCommand CreateApplication(UInt64 aid) { throw new NotImplementedException(); } public APDUCommand GetApplicationIDs() { APDUCommand cmd = new APDUCommand(IsoCase.Case2Short) { CLA = 0x90, INS = (byte)APDUInstructions.GET_APPLICATION_IDS }; return cmd; } public UInt32[] ConvertApplicationIDs(APDUResponse response) { if(response.Body.Length % 3 != 0) { throw new Exception("Invalid Body Length."); } 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); } return applicationIDs.ToArray(); } public void Format() { throw new NotImplementedException(); } /// /// Authenticate to Card /// /// 0x01 - 0x0D /// public void Authenticate(byte key_id, byte[] key) { APDUCommand cmd_challange_request = new APDUCommand(IsoCase.Case4Short) { CLA = 0x90, INS = (byte)0x1A, Data = new byte[] { key_id } }; APDUResponse response = _Card.Transmit(cmd_challange_request); byte[] rndB_enc = response.Body; Console.WriteLine("rndB_enc: {0}", toHexString(rndB_enc)); DES des = new DES(); byte[] rndB = des.Decrypt(rndB_enc, key, GenerateDefaultKey(8)); Console.WriteLine("rndB: {0}", toHexString(rndB)); byte[] iv = new byte[8]; rndB.CopyTo(iv, 0); byte[] rndB_rl = rotateLeft(rndB); Console.WriteLine("rndB_enc: {0}", toHexString(rndB_rl)); byte[] rndA = new byte[] { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 }; Console.WriteLine("rndA: {0}", toHexString(rndA)); byte[] rndAB = concatenate(rndA, rndB_rl); Console.WriteLine("rndAB: {0}", toHexString(rndAB)); byte[] rndAB_enc = des.Encrypt(rndAB, key, rndB_enc); Console.WriteLine("rndA_rndB_enc: {0}", toHexString(rndAB_enc)); iv = lastBlock(rndAB_enc); APDUCommand cmd_challange_response = new APDUCommand(IsoCase.Case4Short) { CLA = 0x90, INS = (byte)0xAF, Data = rndAB_enc }; Console.WriteLine("cmd_challange_response: {0}", toHexString(cmd_challange_response.ToArray())); response = _Card.Transmit(cmd_challange_response); byte[] encryptedRndAFromCard = response.Body; Console.WriteLine("encryptedRndAFromCard: {0}", toHexString(encryptedRndAFromCard)); byte[] rotatedRndAFromCard = des.Decrypt(encryptedRndAFromCard, key, iv); Console.WriteLine("rotatedRndAFromCard: {0}", toHexString(rotatedRndAFromCard)); byte[] rndAFromCard = rotateRight(rotatedRndAFromCard); Console.WriteLine("rndAFromCard: {0}", toHexString(rndAFromCard)); if (!rndA.SequenceEqual(rndAFromCard)) { throw new Exception("???"); } } public byte[] lastBlock(byte[] data) { byte[] block = new byte[8]; for(int i = 0; i < block.Length; i++) { block[i] = data[data.Length - block.Length + i]; } return block; } public byte[] exclusiveOR(byte[] a, byte[] b) { byte[] c = new byte[a.Length]; for (int i = 0; i < a.Length; i++) { c[i] = (byte)(a[i] ^ b[i]); } return c; } private String toHexString(byte[] data) { return BitConverter.ToString(data).Replace("-", string.Empty); } public byte[] rotateLeft(byte[] data) { byte[] rotate = new byte[data.Length]; data.CopyTo(rotate, 0); byte temp = rotate[0]; for (var i = 0; i < rotate.Length - 1; i++) { rotate[i] = rotate[i + 1]; } rotate[rotate.Length - 1] = temp; return rotate; } public byte[] rotateRight(byte[] data) { byte[] rotate = new byte[data.Length]; data.CopyTo(rotate, 0); byte temp = rotate[rotate.Length - 1]; for (var i = rotate.Length - 1; i > 0; i--) { rotate[i] = rotate[i - 1]; } rotate[0] = temp; return rotate; } public byte[] concatenate(byte[] a, byte[] b) { byte[] c = new byte[a.Length + b.Length]; a.CopyTo(c, 0); b.CopyTo(c, a.Length); return c; } /// /// Select Application by ID /// /// 3 Byte ID public APDUCommand SelectApplication(UInt32 id) { byte[] id_byte = BitConverter.GetBytes(id); APDUCommand cmd = new APDUCommand(IsoCase.Case4Short) { CLA = 0x90, INS = (byte)APDUInstructions.SELECT_APPLICATION, Data = new byte[] { id_byte[0], id_byte[1], id_byte[2] }, Le = 0x00 }; return cmd; } public void ChangeApplicationMasterKey(byte[] aPP_MasterKey) { throw new NotImplementedException(); } /// /// Delete Application by ID /// /// 3 Byte ID public APDUCommand 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 }; return cmd; } public void ChangeApplicationKey(int v, byte[] aPP_Key_1) { throw new NotImplementedException(); } /// /// Select Application by ID /// /// 3 Byte ID public APDUCommand CreateApplication(UInt32 id, byte keysetting1, byte keysetting2) { byte[] id_byte = BitConverter.GetBytes(id); APDUCommand cmd = new APDUCommand(IsoCase.Case4Short) { CLA = 0x90, INS = (byte)APDUInstructions.CREATE_APPLICATION, Data = new byte[] { id_byte[0], id_byte[1], id_byte[2], keysetting1, keysetting2 }, Le = 0x00 }; return cmd; } public void CreateFile(byte fabAccessIdentFileID, FileCommunication pLAIN, ushort fileAccessRight, UInt32 v) { throw new NotImplementedException(); } public ushort GenerateFileAccessRight(AccessRights fREE, int v1, int v2, int v3) { throw new NotImplementedException(); } public void WirteData(byte fabAccessIdentFileID, int v1, int v2, byte[] vs) { throw new NotImplementedException(); } public void CreateFile(byte fabAccessIdentFileID, object plain, AccessRights fREE, int v1, int v2, int v3) { throw new NotImplementedException(); } public byte[] ReadData(byte identFileID, int v1, int v2) { throw new NotImplementedException(); } /// /// 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(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); } } }