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;