diff --git a/NFC/NXP MIFARE DESFire/MIFARE_DESFire_V2.cs b/NFC/NXP MIFARE DESFire/MIFARE_DESFire_V2.cs index 6222665..df5fbe2 100644 --- a/NFC/NXP MIFARE DESFire/MIFARE_DESFire_V2.cs +++ b/NFC/NXP MIFARE DESFire/MIFARE_DESFire_V2.cs @@ -299,7 +299,7 @@ namespace NFC.Mifare_DESFire /// Generates SessionKey for DES Authentification /// /// 16Byte SessionKey - private byte[] GenerateSesionKey_DES(byte[] rndA, byte[] rndB) + public byte[] GenerateSesionKey_DES(byte[] rndA, byte[] rndB) { byte[] sesssionkey = new byte[8]; @@ -319,6 +319,37 @@ namespace NFC.Mifare_DESFire return 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; + } + /// /// Set KeyVersion in DES Key /// KeyVersion is stored in LSB of the first 8 Bytes of the DES Key @@ -347,6 +378,30 @@ namespace NFC.Mifare_DESFire return new_key; } + + /// + /// Expand Array to Block Size, fill with 0x00 + /// + /// + public byte[] ExpandToBlockSize(byte[] data, uint bocksize) + { + int diff = data.Length % (int)bocksize; + if (diff == 0) + { + return data; + } + + byte[] expand = new byte[data.Length + bocksize - diff]; + + data.CopyTo(expand, 0); + + for (int i = expand.Length - 1; i > data.Length - 1; i--) + { + expand[i] = 0x00; + } + + return expand; + } #endregion #region DESFire Commands @@ -530,6 +585,147 @@ namespace NFC.Mifare_DESFire _Log.Debug("End SelectApplication"); } + + /// + /// Authenticate to PICC, with ISO Authenticate + /// + /// 0x01 - 0x0D + /// + /// !!! WARNING For Testing only !!! + /// Retry after short Time + public void AuthenticateISO_AES(byte key_id, byte[] key, byte[] rndA = null) + { + _Log.Debug("Start AuthenticateISO_AES"); + + // 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.DebugFormat("APDU_CMD(cmd_challange_request): {0}", ConvertToHexString(cmd_challange_request.ToArray())); + + APDUResponse response = _Card.Transmit(cmd_challange_request); + _Log.DebugFormat("APDU_RES(cmd_challange_request): {0}", ConvertToHexString(response.ToArray())); + + CheckAPDUResponse(response); + + byte[] rndB_enc = response.Body; + _Log.DebugFormat("rndB_enc: {0}", ConvertToHexString(rndB_enc)); + + AES aes = new AES(); + byte[] rndB = aes.Decrypt(rndB_enc, key, GenerateEmptyKey(16)); + _Log.DebugFormat("rndB: {0}", ConvertToHexString(rndB)); + + rndB.CopyTo(iv, 0); + + byte[] rndB_rl = RotateLeft(rndB); + _Log.DebugFormat("rndB_enc: {0}", ConvertToHexString(rndB_rl)); + + if (rndA == null) + { + Random rnd = new Random(); + rndA = new byte[16]; + rnd.NextBytes(rndA); + } + _Log.DebugFormat("rndA: {0}", ConvertToHexString(rndA)); + + byte[] rndAB = Concatenate(rndA, rndB_rl); + _Log.DebugFormat("rndAB: {0}", ConvertToHexString(rndAB)); + + byte[] rndAB_enc = aes.Encrypt(rndAB, key, rndB_enc); + _Log.DebugFormat("rndA_rndB_enc: {0}", ConvertToHexString(rndAB_enc)); + iv = ExtractLastBlock(rndAB_enc, 16); + + APDUCommand cmd_challange_response = new APDUCommand(IsoCase.Case4Short) + { + CLA = 0x90, + INS = 0xAF, + Data = rndAB_enc + }; + _Log.DebugFormat("APDU_CMD(cmd_challange_response): {0}", ConvertToHexString(cmd_challange_response.ToArray())); + + response = _Card.Transmit(cmd_challange_response); + _Log.DebugFormat("APDU_RES(cmd_challange_response): {0}", ConvertToHexString(cmd_challange_response.ToArray())); + + CheckAPDUResponse(response); + + byte[] encryptedRndAFromCard = response.Body; + _Log.DebugFormat("encryptedRndAFromCard: {0}", ConvertToHexString(encryptedRndAFromCard)); + + byte[] rotatedRndAFromCard = aes.Decrypt(encryptedRndAFromCard, key, iv); + _Log.DebugFormat("rotatedRndAFromCard: {0}", ConvertToHexString(rotatedRndAFromCard)); + + byte[] rndAFromCard = RotateRight(rotatedRndAFromCard); + _Log.DebugFormat("rndAFromCard: {0}", ConvertToHexString(rndAFromCard)); + + if (!rndA.SequenceEqual(rndAFromCard)) + { + throw new Exception("Authentication failed, PICC Challenge is not corret"); + } + + _SessionKey = GenerateSesionKey_AES(rndA, rndB); + _Log.DebugFormat("_SessionKey: {0}", ConvertToHexString(_SessionKey)); + + _IV = GenerateEmptyKey(16); + _Log.DebugFormat("_IV: {0}", ConvertToHexString(_IV)); + + _Log.Debug("End AuthenticateISO_DES"); + } + + public void ChangeKey_AES(byte key_id, byte[] new_key, byte key_version) + { + _Log.Debug("Start ChangeKey_AES"); + + byte[] header = new byte[] + { + 0xC4, key_id + }; + _Log.DebugFormat("header: {0}", ConvertToHexString(header)); + + // AES Key Version is Append to Key + byte[] key_and_version = Concatenate(new_key, new byte[] { key_version }); + byte[] command = Concatenate(header, key_and_version); + _Log.DebugFormat("command: {0}", ConvertToHexString(command)); + + CRC32 crc32 = new CRC32(); + byte[] crc = crc32.Calculate(command); + _Log.DebugFormat("crc: {0}", ConvertToHexString(crc)); + + byte[] cryptogram = Concatenate(key_and_version, crc); + _Log.DebugFormat("cryptogram: {0}", ConvertToHexString(cryptogram)); + + byte[] cryptogram_block = ExpandToBlockSize(cryptogram, 16); + _Log.DebugFormat("cryptogram_block: {0}", ConvertToHexString(cryptogram_block)); + + AES aes = new AES(); + byte[] cryptogram_enc = aes.Encrypt(cryptogram_block, _SessionKey, _IV); + _Log.DebugFormat("cryptogram_enc: {0}", ConvertToHexString(cryptogram_enc)); + + byte[] data = Concatenate(new byte[] { key_id }, cryptogram_enc); + _Log.DebugFormat("data: {0}", ConvertToHexString(data)); + + APDUCommand cmd_ChangeKey = new APDUCommand(IsoCase.Case4Short) + { + CLA = 0x90, + INS = 0xC4, + Data = data + }; + _Log.DebugFormat("APDU_CMD(cmd_ChangeKey): {0}", ConvertToHexString(cmd_ChangeKey.ToArray())); + + APDUResponse response = _Card.Transmit(cmd_ChangeKey); + _Log.DebugFormat("APDU_RES(cmd_ChangeKey): {0}", ConvertToHexString(response.ToArray())); + + CheckAPDUResponse(response); + + _Log.Debug("End ChangeKey_AES"); + } #endregion #region Configuration Generator diff --git a/NFC_Test/MIFARE_DESFire_V2_Test.cs b/NFC_Test/MIFARE_DESFire_V2_Test.cs index 8ddbe05..2b7a7c2 100644 --- a/NFC_Test/MIFARE_DESFire_V2_Test.cs +++ b/NFC_Test/MIFARE_DESFire_V2_Test.cs @@ -3,8 +3,11 @@ using NFC.ISO7816_4; using NFC.Mifare_DESFire; using NFC.NXP_MIFARE_DESFire.Exceptions; using NSubstitute; +using NSubstitute.Core; using NUnit.Framework; +using PCSC.Iso7816; using System; +using System.Collections.Generic; using System.Net; namespace NFC_Test @@ -508,6 +511,24 @@ namespace NFC_Test byte[] lastblock = desfire.XOR(null, null); }); } + + [Test] + public void GenerateSessionKey_AES() + { + MIFARE_DESFire_V2 desfire = new MIFARE_DESFire_V2(null); + + byte[] rndA = desfire.ConvertFromHexString("bc14dfde20074617e45a8822f06fdd91"); + Console.WriteLine(desfire.ConvertToHexString(rndA)); + byte[] rndB = desfire.ConvertFromHexString("482ddc54426e6dee560413b8d95471f5"); + Console.WriteLine(desfire.ConvertToHexString(rndB)); + + byte[] expected_sessionkey = desfire.ConvertFromHexString("bc14dfde482ddc54f06fdd91d95471f5"); + Console.WriteLine(desfire.ConvertToHexString(expected_sessionkey)); + + byte[] sessionkey = desfire.GenerateSesionKey_AES(rndA, rndB); + Console.WriteLine(desfire.ConvertToHexString(sessionkey)); + Assert.AreEqual(expected_sessionkey, sessionkey); + } #endregion #region DESFire Commands @@ -546,6 +567,68 @@ namespace NFC_Test Assert.AreEqual(expected_sessionkey, desfire._SessionKey); Assert.AreEqual(expected_iv, desfire._IV); } + + [Test] + public void AuthenticateISO_AES() + { + ICard card = Substitute.For(); + + MIFARE_DESFire_V2 desfire = new MIFARE_DESFire_V2(card); + + APDUResponse response_challenge_request = new APDUResponse() + { + SW1 = 0x91, + SW2 = 0xAF, + Body = desfire.ConvertFromHexString("43a28e28c653df83cd85039714bccb51") + }; + + APDUResponse response_challenge_response = new APDUResponse() + { + SW1 = 0x91, + SW2 = 0x00, + Body = desfire.ConvertFromHexString("d8f70a0f9a43f522f775a56f5688592f") + }; + + byte[] rndA = desfire.ConvertFromHexString("8a8b3c15e576ae3a21c2b18e6aead1f1"); + byte[] key = desfire.ConvertFromHexString("00000000000000000000000000000000"); + + card.Transmit(Arg.Is(x => x.INS == 0xAA)).Returns(response_challenge_request); + card.Transmit(Arg.Is(x => x.INS == 0xAF)).Returns(response_challenge_response); + + desfire.AuthenticateISO_AES(0x00, key, rndA); + + byte[] expected_sessionkey = desfire.ConvertFromHexString("8a8b3c15c71d0cf46aead1f148f27703"); + byte[] expected_iv = desfire.GenerateEmptyKey(16); + + Assert.AreEqual(expected_sessionkey, desfire._SessionKey); + Assert.AreEqual(expected_iv, desfire._IV); + } + + [Test] + public void ChangeKey_AES() + { + ICard card = Substitute.For(); + + MIFARE_DESFire_V2 desfire = new MIFARE_DESFire_V2(card); + + APDUResponse response = new APDUResponse() + { + SW1 = 0x91, + SW2 = 0x00 + }; + + byte[] new_key = desfire.ConvertFromHexString("45eeb8338ae8f49a032e85bb11143530"); + + byte[] sessionkey = desfire.ConvertFromHexString("2f96515262e1beb0129de2df3e97feb3"); + byte[] iv = desfire.ConvertFromHexString("00000000000000000000000000000000"); + + desfire._SessionKey = sessionkey; + desfire._IV = iv; + + card.Transmit(null).ReturnsForAnyArgs(response); + + desfire.ChangeKey_AES(0x00, new_key, 0x10); + } #endregion } } diff --git a/NFC_Test/OTA.cs b/NFC_Test/OTA.cs index 8ff2752..b5eb63f 100644 --- a/NFC_Test/OTA.cs +++ b/NFC_Test/OTA.cs @@ -39,9 +39,49 @@ namespace NFC_Test byte keySetting1 = desfire.GenerateKeySetting1(ChangeApplicationKey.MASTERKEY, ChangeMasterKeySettings.WITHMASTERKEY, CreateDeleteFile.NOKEY, FileDirectoryAccess.NOKEY, ChangeMasterKey.CHANGEABLE); byte keySetting2 = desfire.GenerateKeySetting2(CryptoOperationsType.AES, FileIdentifies.NOTUSED, 0x03); - desfire.CreateApplication(0xAAFFEE, keySetting1, keySetting2); + desfire.CreateApplication(0xC0FFEE, keySetting1, keySetting2); - desfire.SelectApplication(0xAAFFEE); + desfire.SelectApplication(0xC0FFEE); + desfire.AuthenticateISO_AES(0x00, desfire.GenerateEmptyKey(16)); + + byte[] new_key = desfire.ConvertFromHexString("45eeb8338ae8f49a032e85bb11143530"); + + desfire.ChangeKey_AES(0x00, new_key, 0x10); + + transmit_successfully = true; + + card.Disconnect(); + }; + + reader.CardDiscovered += handler; + reader.Start(); + + Assert.AreEqual(true, transmit_successfully); + + reader.Stop(); + reader.CardDiscovered -= handler; + } + + [Test] + public void ChangeKey() + { + IHardware hardware = new Hardware(); + IReader reader = hardware.OpenReader(_ReaderID); + + bool transmit_successfully = false; + + ReaderEventHandler handler = (sender, card) => + { + card.Connect(); + + MIFARE_DESFire_V2 desfire = new MIFARE_DESFire_V2(card); + + desfire.SelectApplication(0xC0FFEE); + desfire.AuthenticateISO_AES(0x00, desfire.GenerateEmptyKey(16)); + + byte[] new_key = desfire.ConvertFromHexString("45eeb8338ae8f49a032e85bb11143530"); + + desfire.ChangeKey_AES(0x00, new_key, 0x10); transmit_successfully = true;