mirror of
https://gitlab.com/fabinfra/fabaccess/borepin.git
synced 2025-04-20 10:26:31 +02:00
Added: DESFire Class V2
This commit is contained in:
parent
f5dffcec5b
commit
7381341d37
45
NFC/Crypto/AES.cs
Normal file
45
NFC/Crypto/AES.cs
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
using Org.BouncyCastle.Crypto;
|
||||||
|
using Org.BouncyCastle.Crypto.Engines;
|
||||||
|
using Org.BouncyCastle.Crypto.Modes;
|
||||||
|
using Org.BouncyCastle.Crypto.Paddings;
|
||||||
|
using Org.BouncyCastle.Crypto.Parameters;
|
||||||
|
|
||||||
|
namespace NFC.Crypto
|
||||||
|
{
|
||||||
|
public class AES
|
||||||
|
{
|
||||||
|
public byte[] Encrypt(byte[] data, byte[] key, byte[] iv)
|
||||||
|
{
|
||||||
|
AesEngine engine = new AesEngine();
|
||||||
|
CbcBlockCipher blockCipher = new CbcBlockCipher(engine);
|
||||||
|
BufferedBlockCipher cipher = new BufferedBlockCipher(blockCipher);
|
||||||
|
KeyParameter keyParam = new KeyParameter(key);
|
||||||
|
ParametersWithIV keyParamWithIV = new ParametersWithIV(keyParam, iv);
|
||||||
|
|
||||||
|
// Encrypt
|
||||||
|
cipher.Init(true, keyParamWithIV);
|
||||||
|
byte[] outputBytes = new byte[cipher.GetOutputSize(data.Length)];
|
||||||
|
int length = cipher.ProcessBytes(data, outputBytes, 0);
|
||||||
|
cipher.DoFinal(outputBytes, length);
|
||||||
|
|
||||||
|
return outputBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] Decrypt(byte[] data, byte[] key, byte[] iv)
|
||||||
|
{
|
||||||
|
AesEngine engine = new AesEngine();
|
||||||
|
CbcBlockCipher blockCipher = new CbcBlockCipher(engine);
|
||||||
|
BufferedBlockCipher cipher = new BufferedBlockCipher(blockCipher);
|
||||||
|
KeyParameter keyParam = new KeyParameter(key);
|
||||||
|
ParametersWithIV keyParamWithIV = new ParametersWithIV(keyParam, iv);
|
||||||
|
|
||||||
|
// Decrypt
|
||||||
|
cipher.Init(false, keyParamWithIV);
|
||||||
|
byte[] outputBytes = new byte[cipher.GetOutputSize(data.Length)];
|
||||||
|
int length = cipher.ProcessBytes(data, outputBytes, 0);
|
||||||
|
cipher.DoFinal(outputBytes, length);
|
||||||
|
|
||||||
|
return outputBytes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
44
NFC/Crypto/CRC16.cs
Normal file
44
NFC/Crypto/CRC16.cs
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace NFC.Crypto
|
||||||
|
{
|
||||||
|
public class CRC16
|
||||||
|
{
|
||||||
|
public byte[] Calculate(byte[] data)
|
||||||
|
{
|
||||||
|
UInt16 crc16 = 0x6363;
|
||||||
|
|
||||||
|
crc16 = Calculate(data, crc16);
|
||||||
|
|
||||||
|
return BitConverter.GetBytes(crc16);
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] Calculate(byte[] cmd, byte[] data)
|
||||||
|
{
|
||||||
|
UInt16 crc16 = 0x6363;
|
||||||
|
|
||||||
|
crc16 = Calculate(cmd, crc16);
|
||||||
|
crc16 = Calculate(data, crc16);
|
||||||
|
|
||||||
|
return BitConverter.GetBytes(crc16);
|
||||||
|
}
|
||||||
|
|
||||||
|
public UInt16 Calculate(byte[] data, UInt16 crc16)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < data.Length; i++)
|
||||||
|
{
|
||||||
|
crc16 ^= data[i];
|
||||||
|
for (int b = 0; b < 8; b++)
|
||||||
|
{
|
||||||
|
bool b_Bit = (crc16 & 0x01) > 0;
|
||||||
|
crc16 >>= 1;
|
||||||
|
if (b_Bit)
|
||||||
|
{
|
||||||
|
crc16 ^= 0x8408;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return crc16;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace NFC.Crypto
|
namespace NFC.Crypto
|
||||||
{
|
{
|
||||||
|
@ -107,5 +107,28 @@ namespace NFC.ISO7816_4
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
#region Methodes
|
||||||
|
public byte[] ToArray()
|
||||||
|
{
|
||||||
|
byte[] array = null;
|
||||||
|
if (Body != null)
|
||||||
|
{
|
||||||
|
array = new byte[Body.Length + 2];
|
||||||
|
Body.CopyTo(array, 0);
|
||||||
|
|
||||||
|
array[Body.Length] = SW1;
|
||||||
|
array[Body.Length + 1] = SW2;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
array = new byte[2];
|
||||||
|
array[0] = SW1;
|
||||||
|
array[1] = SW2;
|
||||||
|
}
|
||||||
|
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="log4net" Version="2.0.11" />
|
||||||
<PackageReference Include="PCSC" Version="5.0.0" />
|
<PackageReference Include="PCSC" Version="5.0.0" />
|
||||||
<PackageReference Include="PCSC.Iso7816" Version="5.0.0" />
|
<PackageReference Include="PCSC.Iso7816" Version="5.0.0" />
|
||||||
<PackageReference Include="Portable.BouncyCastle" Version="1.8.6.7" />
|
<PackageReference Include="Portable.BouncyCastle" Version="1.8.6.7" />
|
||||||
|
@ -0,0 +1,26 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace NFC.NXP_MIFARE_DESFire.Exceptions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Currently not allowed to authenticate. Keeptrying until full delay is spent.
|
||||||
|
/// 0x91AD
|
||||||
|
/// </summary>
|
||||||
|
public class AuthenticationDelayException : Exception
|
||||||
|
{
|
||||||
|
public AuthenticationDelayException()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public AuthenticationDelayException(string message) : base(message)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public AuthenticationDelayException(string message, Exception inner) : base(message, inner)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace NFC.NXP_MIFARE_DESFire.Exceptions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Current authentication status does not allow there- quested command.
|
||||||
|
/// 0x91AE
|
||||||
|
/// </summary>
|
||||||
|
public class AuthenticationErrorException : Exception
|
||||||
|
{
|
||||||
|
public AuthenticationErrorException()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public AuthenticationErrorException(string message) : base(message)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public AuthenticationErrorException(string message, Exception inner) : base(message, inner)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
26
NFC/NXP MIFARE DESFire/Exceptions/BoundaryErrorException.cs
Normal file
26
NFC/NXP MIFARE DESFire/Exceptions/BoundaryErrorException.cs
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace NFC.NXP_MIFARE_DESFire.Exceptions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Attempt toread/write data from/to beyond thefile’s/record’s limits. Attempt to exceed the limitsof a value file.
|
||||||
|
/// 0x91BE
|
||||||
|
/// </summary>
|
||||||
|
public class BoundaryErrorException : Exception
|
||||||
|
{
|
||||||
|
public BoundaryErrorException()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public BoundaryErrorException(string message) : base(message)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public BoundaryErrorException(string message, Exception inner) : base(message, inner)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
26
NFC/NXP MIFARE DESFire/Exceptions/CommandAbortedException.cs
Normal file
26
NFC/NXP MIFARE DESFire/Exceptions/CommandAbortedException.cs
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace NFC.NXP_MIFARE_DESFire.Exceptions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Previous Command was not fully completed.Not all Frames were requested or provided bythe PCD.
|
||||||
|
/// 0x91CA
|
||||||
|
/// </summary>
|
||||||
|
public class CommandAbortedException : Exception
|
||||||
|
{
|
||||||
|
public CommandAbortedException()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommandAbortedException(string message) : base(message)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommandAbortedException(string message, Exception inner) : base(message, inner)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
26
NFC/NXP MIFARE DESFire/Exceptions/DuplicateErrorException.cs
Normal file
26
NFC/NXP MIFARE DESFire/Exceptions/DuplicateErrorException.cs
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace NFC.NXP_MIFARE_DESFire.Exceptions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Creation of file/application failed because file/application with same number already exists
|
||||||
|
/// 0x91DE
|
||||||
|
/// </summary>
|
||||||
|
public class DuplicateErrorException : Exception
|
||||||
|
{
|
||||||
|
public DuplicateErrorException()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public DuplicateErrorException(string message) : base(message)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public DuplicateErrorException(string message, Exception inner) : base(message, inner)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
26
NFC/NXP MIFARE DESFire/Exceptions/FileNotFoundException.cs
Normal file
26
NFC/NXP MIFARE DESFire/Exceptions/FileNotFoundException.cs
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace NFC.NXP_MIFARE_DESFire.Exceptions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Specified file number does not exist.
|
||||||
|
/// 0x91F0
|
||||||
|
/// </summary>
|
||||||
|
public class FileNotFoundException : Exception
|
||||||
|
{
|
||||||
|
public FileNotFoundException()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public FileNotFoundException(string message) : base(message)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public FileNotFoundException(string message, Exception inner) : base(message, inner)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace NFC.NXP_MIFARE_DESFire.Exceptions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Command code not supported.
|
||||||
|
/// 0x911C
|
||||||
|
/// </summary>
|
||||||
|
public class IllegalCommandCodeException : Exception
|
||||||
|
{
|
||||||
|
public IllegalCommandCodeException()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public IllegalCommandCodeException(string message) : base(message)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public IllegalCommandCodeException(string message, Exception inner) : base(message, inner)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
26
NFC/NXP MIFARE DESFire/Exceptions/IntegrityErrorException.cs
Normal file
26
NFC/NXP MIFARE DESFire/Exceptions/IntegrityErrorException.cs
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace NFC.NXP_MIFARE_DESFire.Exceptions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// CRC or MAC does not match data. Paddingbytes not valid.
|
||||||
|
/// 0x911E
|
||||||
|
/// </summary>
|
||||||
|
public class IntegrityErrorException : Exception
|
||||||
|
{
|
||||||
|
public IntegrityErrorException()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public IntegrityErrorException(string message) : base(message)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public IntegrityErrorException(string message, Exception inner) : base(message, inner)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
26
NFC/NXP MIFARE DESFire/Exceptions/LengthErrorException.cs
Normal file
26
NFC/NXP MIFARE DESFire/Exceptions/LengthErrorException.cs
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace NFC.NXP_MIFARE_DESFire.Exceptions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Length of command string invalid.
|
||||||
|
/// 0x917E
|
||||||
|
/// </summary>
|
||||||
|
public class LengthErrorException : Exception
|
||||||
|
{
|
||||||
|
public LengthErrorException()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public LengthErrorException(string message) : base(message)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public LengthErrorException(string message, Exception inner) : base(message, inner)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
26
NFC/NXP MIFARE DESFire/Exceptions/NoSuchKeyException.cs
Normal file
26
NFC/NXP MIFARE DESFire/Exceptions/NoSuchKeyException.cs
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace NFC.NXP_MIFARE_DESFire.Exceptions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Invalid key number specified.
|
||||||
|
/// 0x9140
|
||||||
|
/// </summary>
|
||||||
|
public class NoSuchKeyException : Exception
|
||||||
|
{
|
||||||
|
public NoSuchKeyException()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public NoSuchKeyException(string message) : base(message)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public NoSuchKeyException(string message, Exception inner) : base(message, inner)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
26
NFC/NXP MIFARE DESFire/Exceptions/ParameterErrorException.cs
Normal file
26
NFC/NXP MIFARE DESFire/Exceptions/ParameterErrorException.cs
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace NFC.NXP_MIFARE_DESFire.Exceptions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Value of the parameter(s) invalid.
|
||||||
|
/// 0x919E
|
||||||
|
/// </summary>
|
||||||
|
public class ParameterErrorException : Exception
|
||||||
|
{
|
||||||
|
public ParameterErrorException()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public ParameterErrorException(string message) : base(message)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public ParameterErrorException(string message, Exception inner) : base(message, inner)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace NFC.NXP_MIFARE_DESFire.Exceptions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Current configuration / status does not allow the requested command.
|
||||||
|
/// 0x919D
|
||||||
|
/// </summary>
|
||||||
|
public class PermissionDeniedException : Exception
|
||||||
|
{
|
||||||
|
public PermissionDeniedException()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public PermissionDeniedException(string message) : base(message)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public PermissionDeniedException(string message, Exception inner) : base(message, inner)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -56,9 +56,10 @@ namespace NFC.Mifare_DESFire
|
|||||||
/// <param name="data">Data</param>
|
/// <param name="data">Data</param>
|
||||||
public string ConvertToHexString(byte[] data)
|
public string ConvertToHexString(byte[] data)
|
||||||
{
|
{
|
||||||
return BitConverter.ToString(data).Replace("-", string.Empty);
|
return BitConverter.ToString(data).Replace("-", " ");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void CheckAPDUResponse(APDUResponse response)
|
public void CheckAPDUResponse(APDUResponse response)
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -297,7 +298,7 @@ namespace NFC.Mifare_DESFire
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
byte[] expand = new byte[data.Length + diff];
|
byte[] expand = new byte[data.Length + bocksize - diff];
|
||||||
|
|
||||||
data.CopyTo(expand, 0);
|
data.CopyTo(expand, 0);
|
||||||
|
|
||||||
@ -311,44 +312,43 @@ namespace NFC.Mifare_DESFire
|
|||||||
|
|
||||||
public void ChangeKeyDES(byte key_no, byte[] key_new, byte[] key_current)
|
public void ChangeKeyDES(byte key_no, byte[] key_new, byte[] key_current)
|
||||||
{
|
{
|
||||||
if(!CheckKey(key_new))
|
Console.WriteLine("key_new: {0}", ConvertToHexString(key_new));
|
||||||
|
key_new = concatenate(key_new, key_new);
|
||||||
|
Console.WriteLine("key_new_double: {0}", ConvertToHexString(key_new));
|
||||||
|
|
||||||
|
byte[] header = new byte[]
|
||||||
{
|
{
|
||||||
throw new ArgumentException("key_new is invalid");
|
0xC4, key_no
|
||||||
}
|
};
|
||||||
|
Console.WriteLine("header: {0}", ConvertToHexString(header));
|
||||||
|
|
||||||
if (!CheckKey(key_current))
|
byte[] cmd_ = concatenate(header, key_new);
|
||||||
{
|
Console.WriteLine("cmd_: {0}", ConvertToHexString(cmd_));
|
||||||
throw new ArgumentException("key_new is invalid");
|
|
||||||
}
|
|
||||||
|
|
||||||
if(GetKeyTypeDES(key_new) != GetKeyTypeDES(key_current))
|
CRC32 crc = new CRC32();
|
||||||
{
|
byte[] cmd_crc = crc.Calculate(cmd_);
|
||||||
throw new ArgumentException("key_new and key_current are not same KeyType");
|
Console.WriteLine("cmd_crc: {0}", ConvertToHexString(cmd_crc));
|
||||||
}
|
byte[] cryptogram = concatenate(key_new, cmd_crc);
|
||||||
|
Console.WriteLine("cryptogram: {0}", ConvertToHexString(cryptogram));
|
||||||
byte[] keys_xor = xor(key_new, key_current);
|
|
||||||
|
|
||||||
keys_xor = concatenate(keys_xor, keys_xor);
|
|
||||||
|
|
||||||
CRC32 crc32 = new CRC32();
|
|
||||||
byte[] crc = crc32.Calculate(new byte[] { (byte)APDUInstructions.CHANGE_KEY, key_no }, keys_xor);
|
|
||||||
|
|
||||||
byte[] key_xor_crc = concatenate(keys_xor, crc);
|
|
||||||
|
|
||||||
byte[] key_xor_crc_block = expandToBlockSize(key_xor_crc, 8);
|
|
||||||
|
|
||||||
|
byte[] cryptogram_block = expandToBlockSize(cryptogram, 8);
|
||||||
|
Console.WriteLine("cryptogram_block: {0}", ConvertToHexString(cryptogram_block));
|
||||||
|
|
||||||
DES des = new DES();
|
DES des = new DES();
|
||||||
|
|
||||||
byte[] key_xor_crc_block_enc = des.Encrypt(key_xor_crc_block, _SessionKey, _IV);
|
byte[] cryptogram_block_enc = des.Encrypt(cryptogram_block, _SessionKey, _IV);
|
||||||
|
Console.WriteLine("cryptogram_block_enc: {0}", ConvertToHexString(cryptogram_block_enc));
|
||||||
|
|
||||||
|
byte[] data = concatenate(new byte[] { key_no }, cryptogram_block_enc);
|
||||||
|
Console.WriteLine("data: {0}", ConvertToHexString(data));
|
||||||
|
|
||||||
APDUCommand cmd = new APDUCommand(IsoCase.Case4Short)
|
APDUCommand cmd = new APDUCommand(IsoCase.Case4Short)
|
||||||
{
|
{
|
||||||
CLA = 0x90,
|
CLA = 0x90,
|
||||||
INS = (byte)APDUInstructions.CHANGE_KEY,
|
INS = 0xC4,
|
||||||
Data = key_xor_crc_block_enc,
|
Data = data
|
||||||
Le = 0x00
|
|
||||||
};
|
};
|
||||||
|
Console.WriteLine("cmd: {0}", ConvertToHexString(cmd.ToArray()));
|
||||||
|
|
||||||
APDUResponse response = _Card.Transmit(cmd);
|
APDUResponse response = _Card.Transmit(cmd);
|
||||||
|
|
||||||
@ -592,33 +592,6 @@ namespace NFC.Mifare_DESFire
|
|||||||
return (byte)(((byte)changeKey << 4) | (byte)changeMasterKeySettings | (byte)createDeleteFile | (byte)fileDirectoryAccess | (byte)changeMasterKey);
|
return (byte)(((byte)changeKey << 4) | (byte)changeMasterKeySettings | (byte)createDeleteFile | (byte)fileDirectoryAccess | (byte)changeMasterKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Genearte KeySetting1 for Application Settings or PICC Setting
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="changeKey">ID of Key for changing Application Keys</param>
|
|
||||||
/// <returns></returns>
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Genearte KeySetting2 for Application Creation
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="numberOfKeys">Number of keys that can be stored within the application (0x01-0x0D)</param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public byte GenerateKeySetting2(CryptoOperationsType cryptoOperations, FileIdentifies fileIdentifies, byte numberOfKeys)
|
|
||||||
{
|
|
||||||
if(numberOfKeys < 0x01 || numberOfKeys >= 0x0D)
|
|
||||||
{
|
|
||||||
throw new ArgumentOutOfRangeException();
|
|
||||||
}
|
|
||||||
|
|
||||||
return (byte)((byte)cryptoOperations | (byte)fileIdentifies | numberOfKeys);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
575
NFC/NXP MIFARE DESFire/MIFARE_DESFire_V2.cs
Normal file
575
NFC/NXP MIFARE DESFire/MIFARE_DESFire_V2.cs
Normal file
@ -0,0 +1,575 @@
|
|||||||
|
using log4net.Repository.Hierarchy;
|
||||||
|
using NFC.Crypto;
|
||||||
|
using NFC.ISO7816_4;
|
||||||
|
using NFC.Mifare_DESFire.Enums;
|
||||||
|
using NFC.NXP_MIFARE_DESFire.Exceptions;
|
||||||
|
using PCSC.Iso7816;
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace NFC.Mifare_DESFire
|
||||||
|
{
|
||||||
|
public class MIFARE_DESFire_V2
|
||||||
|
{
|
||||||
|
// Docs https://hackmd.io/qATu8uYdRnOC40aFrB9afg
|
||||||
|
|
||||||
|
#region Log
|
||||||
|
private static readonly log4net.ILog _Log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Contructors
|
||||||
|
/// <summary>
|
||||||
|
/// Construct MIFRARE_DESFire Object with ICard Interface
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="card">Implementation of ICard, only transmit is used</param>
|
||||||
|
public MIFARE_DESFire_V2(ICard card)
|
||||||
|
{
|
||||||
|
_Card = card;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Properties
|
||||||
|
/// <summary>
|
||||||
|
/// ICard Implementation used to transmit APDUCommands and recive APDUResponses
|
||||||
|
/// </summary>
|
||||||
|
private ICard _Card;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// SessionKey, is set after Successfull Authentication
|
||||||
|
/// </summary>
|
||||||
|
public byte[] _SessionKey;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initialation Vector for CBC Encryption
|
||||||
|
/// Is 0 bytes after Successfull Authentication
|
||||||
|
/// </summary>
|
||||||
|
public byte[] _IV;
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Methods
|
||||||
|
#region Helper Methods
|
||||||
|
/// <summary>
|
||||||
|
/// Generate Byte Array filled with 0
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="size">Size of Array</param>
|
||||||
|
public byte[] GenerateEmptyKey(uint size)
|
||||||
|
{
|
||||||
|
byte[] key = new byte[size];
|
||||||
|
for (int i = 0; i < size; i++)
|
||||||
|
{
|
||||||
|
key[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts byte[] to string with HEX Code
|
||||||
|
/// No 0x is created
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="data">Data</param>
|
||||||
|
public string ConvertToHexString(byte[] data)
|
||||||
|
{
|
||||||
|
return BitConverter.ToString(data).Replace("-", " ");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts string with HEX Code to byte[]
|
||||||
|
/// No 0x is requiered
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="data">Data</param>
|
||||||
|
public byte[] ConvertFromHexString(string data)
|
||||||
|
{
|
||||||
|
if (data.Length % 2 == 1)
|
||||||
|
throw new Exception("Data Length is uneven.");
|
||||||
|
|
||||||
|
byte[] arr = new byte[data.Length >> 1];
|
||||||
|
|
||||||
|
for (int i = 0; i < data.Length >> 1; ++i)
|
||||||
|
{
|
||||||
|
arr[i] = (byte)((GetHexVal(data[i << 1]) << 4) + (GetHexVal(data[(i << 1) + 1])));
|
||||||
|
}
|
||||||
|
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int GetHexVal(char hex)
|
||||||
|
{
|
||||||
|
int val = (int)hex;
|
||||||
|
//For uppercase A-F letters:
|
||||||
|
//return val - (val < 58 ? 48 : 55);
|
||||||
|
//For lowercase a-f letters:
|
||||||
|
//return val - (val < 58 ? 48 : 87);
|
||||||
|
//Or the two combined, but a bit slower:
|
||||||
|
return val - (val < 58 ? 48 : (val < 97 ? 55 : 87));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Check APDU Response for DESFire Error Codes
|
||||||
|
/// https://www.nxp.com/docs/en/data-sheet/MF2DLHX0.pdf
|
||||||
|
/// Section: 11.3
|
||||||
|
/// </summary>
|
||||||
|
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
|
||||||
|
/// <summary>
|
||||||
|
/// Return a copy of the last Block of data
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="data">Data compatible to blocksize</param>
|
||||||
|
/// <param name="blocksize">in byte</param>
|
||||||
|
public byte[] ExtractLastBlock(byte[] data, uint blocksize)
|
||||||
|
{
|
||||||
|
if (data == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("Data cannot be null.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.Length % blocksize != 0)
|
||||||
|
{
|
||||||
|
throw new ArgumentException(string.Format("Data is not compatible with blocksize(data(length):{0}, blocksize:{1}.", data.Length, blocksize));
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] lastblock = new byte[blocksize];
|
||||||
|
|
||||||
|
for (int i = 0; i < blocksize; i++)
|
||||||
|
{
|
||||||
|
lastblock[i] = data[data.Length - blocksize + i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return lastblock;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Rotates Array to the left
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="data">Data</param>
|
||||||
|
/// <returns>Copy of data</returns>
|
||||||
|
public byte[] RotateLeft(byte[] data)
|
||||||
|
{
|
||||||
|
if (data == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("Data cannot be null.");
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] rotate = new byte[data.Length];
|
||||||
|
data.CopyTo(rotate, 0);
|
||||||
|
|
||||||
|
byte tmp = rotate[0];
|
||||||
|
for (var i = 0; i < rotate.Length - 1; i++)
|
||||||
|
{
|
||||||
|
rotate[i] = rotate[i + 1];
|
||||||
|
}
|
||||||
|
rotate[rotate.Length - 1] = tmp;
|
||||||
|
|
||||||
|
return rotate;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Rotates Array to the right
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="data">Data</param>
|
||||||
|
/// <returns>Copy of data</returns>
|
||||||
|
public byte[] RotateRight(byte[] data)
|
||||||
|
{
|
||||||
|
if (data == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("Data cannot be null.");
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] rotate = new byte[data.Length];
|
||||||
|
data.CopyTo(rotate, 0);
|
||||||
|
|
||||||
|
byte tmp = rotate[rotate.Length - 1];
|
||||||
|
for (var i = rotate.Length - 1; i > 0; i--)
|
||||||
|
{
|
||||||
|
rotate[i] = rotate[i - 1];
|
||||||
|
}
|
||||||
|
rotate[0] = tmp;
|
||||||
|
|
||||||
|
return rotate;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Concatenates two Arrays, Array A start at index 0
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="a">Array A</param>
|
||||||
|
/// <param name="b">Array B</param>
|
||||||
|
/// <returns>Copy of Data (a.Size + b.Size)</returns>
|
||||||
|
public byte[] Concatenate(byte[] a, byte[] b)
|
||||||
|
{
|
||||||
|
if (a == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("Array A cannot be null.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (b == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("Array B cannot be null.");
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] c = new byte[a.Length + b.Length];
|
||||||
|
a.CopyTo(c, 0);
|
||||||
|
b.CopyTo(c, a.Length);
|
||||||
|
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Boolean Operation XOR on all Bytes
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="a">Array A</param>
|
||||||
|
/// <param name="b">Array B</param>
|
||||||
|
/// <returns>Copy of Data</returns>
|
||||||
|
public byte[] XOR(byte[] a, byte[] b)
|
||||||
|
{
|
||||||
|
if (a == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("Array A cannot be null.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (b == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("Array B cannot be null.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (a.Length != b.Length)
|
||||||
|
{
|
||||||
|
throw new ArgumentException(string.Format("Arrays are not same Length(Length A:{0}, Lenght B:{1})", a.Length, b.Length));
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] c = new byte[a.Length];
|
||||||
|
|
||||||
|
for(int i = 0; i < a.Length; i++)
|
||||||
|
{
|
||||||
|
c[i] = (byte)(a[i] ^ b[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generates SessionKey for DES Authentification
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>16Byte SessionKey</returns>
|
||||||
|
private 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 Concatenate(sesssionkey, sesssionkey);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set KeyVersion in DES Key
|
||||||
|
/// KeyVersion is stored in LSB of the first 8 Bytes of the DES Key
|
||||||
|
/// </summary>
|
||||||
|
public byte[] SetKeyVersion(byte[] key, byte keyversion)
|
||||||
|
{
|
||||||
|
byte[] pow2 = new byte[]
|
||||||
|
{
|
||||||
|
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80
|
||||||
|
};
|
||||||
|
|
||||||
|
byte[] new_key = new byte[key.Length];
|
||||||
|
key.CopyTo(new_key, 0);
|
||||||
|
|
||||||
|
for (int i = 0; i < 8; i++)
|
||||||
|
{
|
||||||
|
if ((keyversion & pow2[i]) > 0)
|
||||||
|
{
|
||||||
|
new_key[i] = (byte)(new_key[5] | 0x01);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
new_key[i] = (byte)(new_key[5] & 0x7F);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new_key;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region DESFire Commands
|
||||||
|
/// <summary>
|
||||||
|
/// Authenticate to PICC, with ISO Authenticate
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key_id">0x01 - 0x0D</param>
|
||||||
|
/// <param name="key"></param>
|
||||||
|
/// <param name="rndA">!!! WARNING For Testing only !!!</param>
|
||||||
|
/// <exception cref="AuthenticationDelayException">Retry after short Time</exception>
|
||||||
|
public void AuthenticateISO_DES(byte key_id, byte[] key, byte[] rndA = null)
|
||||||
|
{
|
||||||
|
_Log.Debug("Start AuthenticateISO_DES");
|
||||||
|
|
||||||
|
// Sepearte InitialisationVector for Authentication
|
||||||
|
byte[] iv = new byte[8];
|
||||||
|
|
||||||
|
APDUCommand cmd_challange_request = new APDUCommand(IsoCase.Case4Short)
|
||||||
|
{
|
||||||
|
CLA = 0x90,
|
||||||
|
INS = 0x1A,
|
||||||
|
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));
|
||||||
|
|
||||||
|
DES des = new DES();
|
||||||
|
byte[] rndB = des.Decrypt(rndB_enc, key, GenerateEmptyKey(8));
|
||||||
|
_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[8];
|
||||||
|
rnd.NextBytes(rndA);
|
||||||
|
}
|
||||||
|
_Log.DebugFormat("rndA: {0}", ConvertToHexString(rndA));
|
||||||
|
|
||||||
|
byte[] rndAB = Concatenate(rndA, rndB_rl);
|
||||||
|
_Log.DebugFormat("rndAB: {0}", ConvertToHexString(rndAB));
|
||||||
|
|
||||||
|
byte[] rndAB_enc = des.Encrypt(rndAB, key, rndB_enc);
|
||||||
|
_Log.DebugFormat("rndA_rndB_enc: {0}", ConvertToHexString(rndAB_enc));
|
||||||
|
iv = ExtractLastBlock(rndAB_enc, 8);
|
||||||
|
|
||||||
|
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 = des.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_DES(rndA, rndB);
|
||||||
|
_Log.DebugFormat("_SessionKey: {0}", ConvertToHexString(_SessionKey));
|
||||||
|
|
||||||
|
_IV = GenerateEmptyKey(8);
|
||||||
|
_Log.DebugFormat("_IV: {0}", ConvertToHexString(_IV));
|
||||||
|
|
||||||
|
_Log.Debug("End AuthenticateISO_DES");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Format PICC
|
||||||
|
/// </summary>
|
||||||
|
public void Format()
|
||||||
|
{
|
||||||
|
_Log.Debug("Start Format");
|
||||||
|
|
||||||
|
APDUCommand cmd_format = new APDUCommand(IsoCase.Case2Short)
|
||||||
|
{
|
||||||
|
CLA = 0x90,
|
||||||
|
INS = 0xFC,
|
||||||
|
};
|
||||||
|
_Log.DebugFormat("APDU_CMD(cmd_format): {0}", ConvertToHexString(cmd_format.ToArray()));
|
||||||
|
|
||||||
|
APDUResponse response = _Card.Transmit(cmd_format);
|
||||||
|
_Log.DebugFormat("APDU_RES(cmd_format): {0}", ConvertToHexString(response.ToArray()));
|
||||||
|
|
||||||
|
CheckAPDUResponse(response);
|
||||||
|
|
||||||
|
_Log.Debug("End Format");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create Application for ID
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="aid">3 Byte ID</param>
|
||||||
|
public void CreateApplication(UInt32 aid, byte keysetting1, byte keysetting2)
|
||||||
|
{
|
||||||
|
_Log.Debug("Start CreateApplication");
|
||||||
|
|
||||||
|
byte[] id_byte = BitConverter.GetBytes(aid);
|
||||||
|
_Log.DebugFormat("AID: {0}", 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.DebugFormat("APDU_CMD(cmd_CreateApplication): {0}", ConvertToHexString(cmd_CreateApplication.ToArray()));
|
||||||
|
|
||||||
|
APDUResponse response = _Card.Transmit(cmd_CreateApplication);
|
||||||
|
_Log.DebugFormat("APDU_RES(cmd_CreateApplication): {0}", ConvertToHexString(response.ToArray()));
|
||||||
|
|
||||||
|
CheckAPDUResponse(response);
|
||||||
|
|
||||||
|
_Log.Debug("End CreateApplication");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Select Application by AID
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="aid">3 Byte AID</param>
|
||||||
|
public void SelectApplication(UInt32 aid)
|
||||||
|
{
|
||||||
|
_Log.Debug("Start SelectApplication");
|
||||||
|
|
||||||
|
byte[] id_byte = BitConverter.GetBytes(aid);
|
||||||
|
_Log.DebugFormat("AID: {0}", ConvertToHexString(id_byte.ToArray()));
|
||||||
|
|
||||||
|
APDUCommand cmd_SelectApplication = new APDUCommand(IsoCase.Case4Short)
|
||||||
|
{
|
||||||
|
CLA = 0x90,
|
||||||
|
INS = (byte)APDUInstructions.SELECT_APPLICATION,
|
||||||
|
Data = new byte[]
|
||||||
|
{
|
||||||
|
id_byte[0],
|
||||||
|
id_byte[1],
|
||||||
|
id_byte[2]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
_Log.DebugFormat("APDU_CMD(cmd_SelectApplication): {0}", ConvertToHexString(cmd_SelectApplication.ToArray()));
|
||||||
|
|
||||||
|
APDUResponse response = _Card.Transmit(cmd_SelectApplication);
|
||||||
|
_Log.DebugFormat("APDU_RES(cmd_SelectApplication): {0}", ConvertToHexString(response.ToArray()));
|
||||||
|
|
||||||
|
CheckAPDUResponse(response);
|
||||||
|
|
||||||
|
_Log.Debug("End SelectApplication");
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Configuration Generator
|
||||||
|
/// <summary>
|
||||||
|
/// Genearte KeySetting1 for Application Settings or PICC Setting
|
||||||
|
/// </summary>
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Genearte KeySetting1 for Application Settings or PICC Setting
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="changeKey">ID of Key for changing Application Keys</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Genearte KeySetting2 for Application Creation
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="numberOfKeys">Number of keys that can be stored within the application (0x01-0x0D)</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public byte GenerateKeySetting2(CryptoOperationsType cryptoOperations, FileIdentifies fileIdentifies, byte numberOfKeys)
|
||||||
|
{
|
||||||
|
if (numberOfKeys < 0x01 || numberOfKeys >= 0x0D)
|
||||||
|
{
|
||||||
|
throw new ArgumentOutOfRangeException();
|
||||||
|
}
|
||||||
|
|
||||||
|
return (byte)((byte)cryptoOperations | (byte)fileIdentifies | numberOfKeys);
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
@ -126,5 +126,86 @@ namespace NFC_Test
|
|||||||
|
|
||||||
mifareDESFire.AuthenticateDES(0x00, mifareDESFire.GenerateDefaultKey(16));
|
mifareDESFire.AuthenticateDES(0x00, mifareDESFire.GenerateDefaultKey(16));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void ChangeKey()
|
||||||
|
{
|
||||||
|
ICard card = Substitute.For<ICard>();
|
||||||
|
MIFARE_DESFire mifareDESFire = new MIFARE_DESFire(card);
|
||||||
|
|
||||||
|
mifareDESFire._SessionKey = new byte[]
|
||||||
|
{
|
||||||
|
0xDC, 0xB0, 0x96, 0xC2, 0xA4, 0x0E, 0x78, 0xE0, 0xA0, 0xE4, 0x7A, 0x96, 0xF4, 0x2E, 0x62, 0xAE
|
||||||
|
};
|
||||||
|
mifareDESFire._IV = new byte[]
|
||||||
|
{
|
||||||
|
0x33, 0x45 , 0xAA , 0x95 , 0xF2 , 0xD9 , 0x56 , 0xCF
|
||||||
|
};
|
||||||
|
|
||||||
|
mifareDESFire.ChangeKeyDES(0x00, GenerateDefaultKey(16), GenerateDefaultKey(16));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void CRC()
|
||||||
|
{
|
||||||
|
byte[] data = StringToByteArrayFastest("c40045eeb8338ae8f49a032e85bb1114353010");
|
||||||
|
|
||||||
|
CRC32 crc32 = new CRC32();
|
||||||
|
|
||||||
|
byte[] crc = crc32.Calculate(data);
|
||||||
|
|
||||||
|
MIFARE_DESFire dESFire = new MIFARE_DESFire(null);
|
||||||
|
|
||||||
|
Console.WriteLine("data: {0}", dESFire.ConvertToHexString(data));
|
||||||
|
Console.WriteLine("crc: {0}", dESFire.ConvertToHexString(crc));
|
||||||
|
|
||||||
|
byte[] data_crc = dESFire.concatenate(data, crc);
|
||||||
|
Console.WriteLine("data_crc: {0}", dESFire.ConvertToHexString(data_crc));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] StringToByteArrayFastest(string hex)
|
||||||
|
{
|
||||||
|
if (hex.Length % 2 == 1)
|
||||||
|
throw new Exception("The binary key cannot have an odd number of digits");
|
||||||
|
|
||||||
|
byte[] arr = new byte[hex.Length >> 1];
|
||||||
|
|
||||||
|
for (int i = 0; i < hex.Length >> 1; ++i)
|
||||||
|
{
|
||||||
|
arr[i] = (byte)((GetHexVal(hex[i << 1]) << 4) + (GetHexVal(hex[(i << 1) + 1])));
|
||||||
|
}
|
||||||
|
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int GetHexVal(char hex)
|
||||||
|
{
|
||||||
|
int val = (int)hex;
|
||||||
|
//For uppercase A-F letters:
|
||||||
|
//return val - (val < 58 ? 48 : 55);
|
||||||
|
//For lowercase a-f letters:
|
||||||
|
//return val - (val < 58 ? 48 : 87);
|
||||||
|
//Or the two combined, but a bit slower:
|
||||||
|
return val - (val < 58 ? 48 : (val < 97 ? 55 : 87));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void AES()
|
||||||
|
{
|
||||||
|
byte[] data = StringToByteArrayFastest("45eeb8338ae8f49a032e85bb111435301095c3894b0000000000000000000000");
|
||||||
|
byte[] key = StringToByteArrayFastest("d99aca2b5b4de3a949fa2cf12b0eb673");
|
||||||
|
byte[] iv = StringToByteArrayFastest("00000000000000000000000000000000");
|
||||||
|
|
||||||
|
MIFARE_DESFire dESFire = new MIFARE_DESFire(null);
|
||||||
|
|
||||||
|
Console.WriteLine("data: {0}", dESFire.ConvertToHexString(data));
|
||||||
|
Console.WriteLine("key: {0}", dESFire.ConvertToHexString(key));
|
||||||
|
Console.WriteLine("iv: {0}", dESFire.ConvertToHexString(iv));
|
||||||
|
|
||||||
|
AES aes = new AES();
|
||||||
|
|
||||||
|
byte[] data_enc = aes.Encrypt(data, key, iv);
|
||||||
|
Console.WriteLine("data_enc: {0}", dESFire.ConvertToHexString(data_enc));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
551
NFC_Test/MIFARE_DESFire_V2_Test.cs
Normal file
551
NFC_Test/MIFARE_DESFire_V2_Test.cs
Normal file
@ -0,0 +1,551 @@
|
|||||||
|
using NFC;
|
||||||
|
using NFC.ISO7816_4;
|
||||||
|
using NFC.Mifare_DESFire;
|
||||||
|
using NFC.NXP_MIFARE_DESFire.Exceptions;
|
||||||
|
using NSubstitute;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using System;
|
||||||
|
using System.Net;
|
||||||
|
|
||||||
|
namespace NFC_Test
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
public class MIFARE_DESFire_V2_Test
|
||||||
|
{
|
||||||
|
#region Helper Methods
|
||||||
|
[Test]
|
||||||
|
public void GenerateDefaultKey()
|
||||||
|
{
|
||||||
|
uint i = 16;
|
||||||
|
|
||||||
|
MIFARE_DESFire_V2 desfire = new MIFARE_DESFire_V2(null);
|
||||||
|
|
||||||
|
byte[] data = desfire.GenerateEmptyKey(i);
|
||||||
|
|
||||||
|
for(int e = 0; e < i; e++)
|
||||||
|
{
|
||||||
|
if(data[e] != 0x00)
|
||||||
|
{
|
||||||
|
Assert.Fail("Data is not 0x00");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void CheckAPDUResponse__NULL()
|
||||||
|
{
|
||||||
|
MIFARE_DESFire_V2 desfire = new MIFARE_DESFire_V2(null);
|
||||||
|
Assert.Throws<ArgumentNullException>(
|
||||||
|
delegate
|
||||||
|
{
|
||||||
|
desfire.CheckAPDUResponse(null);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void CheckAPDUResponse__UNKNOWN()
|
||||||
|
{
|
||||||
|
APDUResponse response = new APDUResponse()
|
||||||
|
{
|
||||||
|
SW1 = 0x00,
|
||||||
|
SW2 = 0x00
|
||||||
|
};
|
||||||
|
|
||||||
|
MIFARE_DESFire_V2 desfire = new MIFARE_DESFire_V2(null);
|
||||||
|
Assert.Throws<Exception>(
|
||||||
|
delegate
|
||||||
|
{
|
||||||
|
desfire.CheckAPDUResponse(response);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void CheckAPDUResponse__OPERATION_OK()
|
||||||
|
{
|
||||||
|
APDUResponse response = new APDUResponse()
|
||||||
|
{
|
||||||
|
SW1 = 0x91,
|
||||||
|
SW2 = 0x00
|
||||||
|
};
|
||||||
|
|
||||||
|
MIFARE_DESFire_V2 desfire = new MIFARE_DESFire_V2(null);
|
||||||
|
|
||||||
|
desfire.CheckAPDUResponse(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void CheckAPDUResponse__NO_CHANGES()
|
||||||
|
{
|
||||||
|
APDUResponse response = new APDUResponse()
|
||||||
|
{
|
||||||
|
SW1 = 0x91,
|
||||||
|
SW2 = 0x0C
|
||||||
|
};
|
||||||
|
|
||||||
|
MIFARE_DESFire_V2 desfire = new MIFARE_DESFire_V2(null);
|
||||||
|
desfire.CheckAPDUResponse(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void CheckAPDUResponse__ILLEGAL_COMMAND_CODE()
|
||||||
|
{
|
||||||
|
APDUResponse response = new APDUResponse()
|
||||||
|
{
|
||||||
|
SW1 = 0x91,
|
||||||
|
SW2 = 0x1C
|
||||||
|
};
|
||||||
|
|
||||||
|
MIFARE_DESFire_V2 desfire = new MIFARE_DESFire_V2(null);
|
||||||
|
|
||||||
|
Assert.Throws<IllegalCommandCodeException>(
|
||||||
|
delegate
|
||||||
|
{
|
||||||
|
desfire.CheckAPDUResponse(response);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void CheckAPDUResponse__INTEGRITY_ERROR()
|
||||||
|
{
|
||||||
|
APDUResponse response = new APDUResponse()
|
||||||
|
{
|
||||||
|
SW1 = 0x91,
|
||||||
|
SW2 = 0x1E
|
||||||
|
};
|
||||||
|
|
||||||
|
MIFARE_DESFire_V2 desfire = new MIFARE_DESFire_V2(null);
|
||||||
|
|
||||||
|
Assert.Throws<IntegrityErrorException>(
|
||||||
|
delegate
|
||||||
|
{
|
||||||
|
desfire.CheckAPDUResponse(response);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void CheckAPDUResponse__NO_SUCH_KEY()
|
||||||
|
{
|
||||||
|
APDUResponse response = new APDUResponse()
|
||||||
|
{
|
||||||
|
SW1 = 0x91,
|
||||||
|
SW2 = 0x40
|
||||||
|
};
|
||||||
|
|
||||||
|
MIFARE_DESFire_V2 desfire = new MIFARE_DESFire_V2(null);
|
||||||
|
|
||||||
|
Assert.Throws<NoSuchKeyException>(
|
||||||
|
delegate
|
||||||
|
{
|
||||||
|
desfire.CheckAPDUResponse(response);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void CheckAPDUResponse__LENGTH_ERROR()
|
||||||
|
{
|
||||||
|
APDUResponse response = new APDUResponse()
|
||||||
|
{
|
||||||
|
SW1 = 0x91,
|
||||||
|
SW2 = 0x7E
|
||||||
|
};
|
||||||
|
|
||||||
|
MIFARE_DESFire_V2 desfire = new MIFARE_DESFire_V2(null);
|
||||||
|
|
||||||
|
Assert.Throws<LengthErrorException>(
|
||||||
|
delegate
|
||||||
|
{
|
||||||
|
desfire.CheckAPDUResponse(response);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void CheckAPDUResponse__PERMISSION_DENIED()
|
||||||
|
{
|
||||||
|
APDUResponse response = new APDUResponse()
|
||||||
|
{
|
||||||
|
SW1 = 0x91,
|
||||||
|
SW2 = 0x9D
|
||||||
|
};
|
||||||
|
|
||||||
|
MIFARE_DESFire_V2 desfire = new MIFARE_DESFire_V2(null);
|
||||||
|
|
||||||
|
Assert.Throws<PermissionDeniedException>(
|
||||||
|
delegate
|
||||||
|
{
|
||||||
|
desfire.CheckAPDUResponse(response);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void CheckAPDUResponse__PARAMETER_ERROR()
|
||||||
|
{
|
||||||
|
APDUResponse response = new APDUResponse()
|
||||||
|
{
|
||||||
|
SW1 = 0x91,
|
||||||
|
SW2 = 0x9E
|
||||||
|
};
|
||||||
|
|
||||||
|
MIFARE_DESFire_V2 desfire = new MIFARE_DESFire_V2(null);
|
||||||
|
|
||||||
|
Assert.Throws<ParameterErrorException>(
|
||||||
|
delegate
|
||||||
|
{
|
||||||
|
desfire.CheckAPDUResponse(response);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void CheckAPDUResponse__AUTHENTICATION_DELAY()
|
||||||
|
{
|
||||||
|
APDUResponse response = new APDUResponse()
|
||||||
|
{
|
||||||
|
SW1 = 0x91,
|
||||||
|
SW2 = 0xAD
|
||||||
|
};
|
||||||
|
|
||||||
|
MIFARE_DESFire_V2 desfire = new MIFARE_DESFire_V2(null);
|
||||||
|
|
||||||
|
Assert.Throws<AuthenticationDelayException>(
|
||||||
|
delegate
|
||||||
|
{
|
||||||
|
desfire.CheckAPDUResponse(response);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void CheckAPDUResponse__AUTHENTICATION_ERROR()
|
||||||
|
{
|
||||||
|
APDUResponse response = new APDUResponse()
|
||||||
|
{
|
||||||
|
SW1 = 0x91,
|
||||||
|
SW2 = 0xAE
|
||||||
|
};
|
||||||
|
|
||||||
|
MIFARE_DESFire_V2 desfire = new MIFARE_DESFire_V2(null);
|
||||||
|
|
||||||
|
Assert.Throws<AuthenticationErrorException>(
|
||||||
|
delegate
|
||||||
|
{
|
||||||
|
desfire.CheckAPDUResponse(response);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void CheckAPDUResponse__ADDITIONAL_FRAME()
|
||||||
|
{
|
||||||
|
APDUResponse response = new APDUResponse()
|
||||||
|
{
|
||||||
|
SW1 = 0x91,
|
||||||
|
SW2 = 0xAF
|
||||||
|
};
|
||||||
|
|
||||||
|
MIFARE_DESFire_V2 desfire = new MIFARE_DESFire_V2(null);
|
||||||
|
|
||||||
|
desfire.CheckAPDUResponse(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void CheckAPDUResponse__BOUNDARY_ERROR()
|
||||||
|
{
|
||||||
|
APDUResponse response = new APDUResponse()
|
||||||
|
{
|
||||||
|
SW1 = 0x91,
|
||||||
|
SW2 = 0xBE
|
||||||
|
};
|
||||||
|
|
||||||
|
MIFARE_DESFire_V2 desfire = new MIFARE_DESFire_V2(null);
|
||||||
|
|
||||||
|
Assert.Throws<BoundaryErrorException>(
|
||||||
|
delegate
|
||||||
|
{
|
||||||
|
desfire.CheckAPDUResponse(response);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void CheckAPDUResponse__COMMAND_ABORTED()
|
||||||
|
{
|
||||||
|
APDUResponse response = new APDUResponse()
|
||||||
|
{
|
||||||
|
SW1 = 0x91,
|
||||||
|
SW2 = 0xCA
|
||||||
|
};
|
||||||
|
|
||||||
|
MIFARE_DESFire_V2 desfire = new MIFARE_DESFire_V2(null);
|
||||||
|
|
||||||
|
Assert.Throws<CommandAbortedException>(
|
||||||
|
delegate
|
||||||
|
{
|
||||||
|
desfire.CheckAPDUResponse(response);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void CheckAPDUResponse__DUPLICATE_ERROR()
|
||||||
|
{
|
||||||
|
APDUResponse response = new APDUResponse()
|
||||||
|
{
|
||||||
|
SW1 = 0x91,
|
||||||
|
SW2 = 0xDE
|
||||||
|
};
|
||||||
|
|
||||||
|
MIFARE_DESFire_V2 desfire = new MIFARE_DESFire_V2(null);
|
||||||
|
|
||||||
|
Assert.Throws<DuplicateErrorException>(
|
||||||
|
delegate
|
||||||
|
{
|
||||||
|
desfire.CheckAPDUResponse(response);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void CheckAPDUResponse__FILE_NOT_FOUND()
|
||||||
|
{
|
||||||
|
APDUResponse response = new APDUResponse()
|
||||||
|
{
|
||||||
|
SW1 = 0x91,
|
||||||
|
SW2 = 0xF0
|
||||||
|
};
|
||||||
|
|
||||||
|
MIFARE_DESFire_V2 desfire = new MIFARE_DESFire_V2(null);
|
||||||
|
|
||||||
|
Assert.Throws<FileNotFoundException>(
|
||||||
|
delegate
|
||||||
|
{
|
||||||
|
desfire.CheckAPDUResponse(response);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Crypto Operation
|
||||||
|
[Test]
|
||||||
|
public void ExtractLastBlock()
|
||||||
|
{
|
||||||
|
byte[] data = new byte[]
|
||||||
|
{
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01
|
||||||
|
};
|
||||||
|
|
||||||
|
byte[] expected_lastblock = new byte[]
|
||||||
|
{
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01
|
||||||
|
};
|
||||||
|
|
||||||
|
MIFARE_DESFire_V2 desfire = new MIFARE_DESFire_V2(null);
|
||||||
|
|
||||||
|
byte[] lastblock = desfire.ExtractLastBlock(data, 8);
|
||||||
|
|
||||||
|
Assert.AreEqual(expected_lastblock, lastblock);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void ExtractLastBlock_WrongBlocksize()
|
||||||
|
{
|
||||||
|
byte[] data = new byte[]
|
||||||
|
{
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01
|
||||||
|
};
|
||||||
|
|
||||||
|
MIFARE_DESFire_V2 desfire = new MIFARE_DESFire_V2(null);
|
||||||
|
|
||||||
|
Assert.Throws<ArgumentException>(
|
||||||
|
delegate
|
||||||
|
{
|
||||||
|
byte[] lastblock = desfire.ExtractLastBlock(data, 7);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void ExtractLastBlock_Null()
|
||||||
|
{
|
||||||
|
byte[] data = null;
|
||||||
|
|
||||||
|
MIFARE_DESFire_V2 desfire = new MIFARE_DESFire_V2(null);
|
||||||
|
|
||||||
|
Assert.Throws<ArgumentNullException>(
|
||||||
|
delegate
|
||||||
|
{
|
||||||
|
byte[] lastblock = desfire.ExtractLastBlock(data, 7);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void RotateLeft()
|
||||||
|
{
|
||||||
|
byte[] data = new byte[]
|
||||||
|
{
|
||||||
|
0x01, 0x02, 0x03, 0x04
|
||||||
|
};
|
||||||
|
|
||||||
|
byte[] expected_data_left = new byte[]
|
||||||
|
{
|
||||||
|
0x02, 0x03, 0x04, 0x01
|
||||||
|
};
|
||||||
|
|
||||||
|
MIFARE_DESFire_V2 desfire = new MIFARE_DESFire_V2(null);
|
||||||
|
|
||||||
|
byte[] data_left = desfire.RotateLeft(data);
|
||||||
|
|
||||||
|
Assert.AreEqual(expected_data_left, data_left);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void RotateLeft_Null()
|
||||||
|
{
|
||||||
|
MIFARE_DESFire_V2 desfire = new MIFARE_DESFire_V2(null);
|
||||||
|
|
||||||
|
Assert.Throws<ArgumentNullException>(
|
||||||
|
delegate
|
||||||
|
{
|
||||||
|
byte[] lastblock = desfire.RotateLeft(null);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void RotateRight()
|
||||||
|
{
|
||||||
|
byte[] data = new byte[]
|
||||||
|
{
|
||||||
|
0x01, 0x02, 0x03, 0x04
|
||||||
|
};
|
||||||
|
|
||||||
|
byte[] expected_data_left = new byte[]
|
||||||
|
{
|
||||||
|
0x04, 0x01, 0x02, 0x03
|
||||||
|
};
|
||||||
|
|
||||||
|
MIFARE_DESFire_V2 desfire = new MIFARE_DESFire_V2(null);
|
||||||
|
|
||||||
|
byte[] data_left = desfire.RotateRight(data);
|
||||||
|
|
||||||
|
Assert.AreEqual(expected_data_left, data_left);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void RotateRight_Null()
|
||||||
|
{
|
||||||
|
MIFARE_DESFire_V2 desfire = new MIFARE_DESFire_V2(null);
|
||||||
|
|
||||||
|
Assert.Throws<ArgumentNullException>(
|
||||||
|
delegate
|
||||||
|
{
|
||||||
|
byte[] lastblock = desfire.RotateRight(null);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Concatenate()
|
||||||
|
{
|
||||||
|
byte[] data_a = new byte[]
|
||||||
|
{
|
||||||
|
0x01, 0x02, 0x03, 0x04
|
||||||
|
};
|
||||||
|
|
||||||
|
byte[] data_b = new byte[]
|
||||||
|
{
|
||||||
|
0x05, 0x06, 0x07, 0x08
|
||||||
|
};
|
||||||
|
|
||||||
|
byte[] expected_data_c = new byte[]
|
||||||
|
{
|
||||||
|
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08
|
||||||
|
};
|
||||||
|
|
||||||
|
MIFARE_DESFire_V2 desfire = new MIFARE_DESFire_V2(null);
|
||||||
|
|
||||||
|
byte[] data_c = desfire.Concatenate(data_a, data_b);
|
||||||
|
|
||||||
|
Assert.AreEqual(expected_data_c, data_c);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Concatenate_Null()
|
||||||
|
{
|
||||||
|
MIFARE_DESFire_V2 desfire = new MIFARE_DESFire_V2(null);
|
||||||
|
|
||||||
|
Assert.Throws<ArgumentNullException>(
|
||||||
|
delegate
|
||||||
|
{
|
||||||
|
byte[] lastblock = desfire.Concatenate(null, null);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void XOR()
|
||||||
|
{
|
||||||
|
byte[] data_a = new byte[]
|
||||||
|
{
|
||||||
|
0x00, 0xF0, 0x00, 0xF0
|
||||||
|
};
|
||||||
|
|
||||||
|
byte[] data_b = new byte[]
|
||||||
|
{
|
||||||
|
0x0F, 0x00, 0x0F, 0x00
|
||||||
|
};
|
||||||
|
|
||||||
|
byte[] expected_data_c = new byte[]
|
||||||
|
{
|
||||||
|
0x0F, 0xF0, 0x0F, 0xF0
|
||||||
|
};
|
||||||
|
|
||||||
|
MIFARE_DESFire_V2 desfire = new MIFARE_DESFire_V2(null);
|
||||||
|
|
||||||
|
byte[] data_c = desfire.XOR(data_a, data_b);
|
||||||
|
|
||||||
|
Assert.AreEqual(expected_data_c, data_c);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void XOR_null()
|
||||||
|
{
|
||||||
|
MIFARE_DESFire_V2 desfire = new MIFARE_DESFire_V2(null);
|
||||||
|
|
||||||
|
Assert.Throws<ArgumentNullException>(
|
||||||
|
delegate
|
||||||
|
{
|
||||||
|
byte[] lastblock = desfire.XOR(null, null);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region DESFire Commands
|
||||||
|
[Test]
|
||||||
|
public void AuthenticateISO_DES()
|
||||||
|
{
|
||||||
|
ICard card = Substitute.For<ICard>();
|
||||||
|
|
||||||
|
MIFARE_DESFire_V2 desfire = new MIFARE_DESFire_V2(card);
|
||||||
|
|
||||||
|
APDUResponse response_challenge_request = new APDUResponse()
|
||||||
|
{
|
||||||
|
SW1 = 0x91,
|
||||||
|
SW2 = 0xAF,
|
||||||
|
Body = desfire.ConvertFromHexString("5D994CE085F24089")
|
||||||
|
};
|
||||||
|
|
||||||
|
APDUResponse response_challenge_response = new APDUResponse()
|
||||||
|
{
|
||||||
|
SW1 = 0x91,
|
||||||
|
SW2 = 0x00,
|
||||||
|
Body = desfire.ConvertFromHexString("913C6DED84221C41")
|
||||||
|
};
|
||||||
|
|
||||||
|
byte[] rndA = desfire.ConvertFromHexString("849B36C5F8BF4A09");
|
||||||
|
byte[] key = desfire.ConvertFromHexString("00000000000000000000000000000000");
|
||||||
|
|
||||||
|
card.Transmit(Arg.Is<APDUCommand>(x => x.INS == 0x1A)).Returns(response_challenge_request);
|
||||||
|
card.Transmit(Arg.Is<APDUCommand>(x => x.INS == 0xAF)).Returns(response_challenge_response);
|
||||||
|
|
||||||
|
desfire.AuthenticateISO_DES(0x00, key, rndA);
|
||||||
|
|
||||||
|
byte[] expected_sessionkey = desfire.ConvertFromHexString("849B36C54FD1B759849B36C54FD1B759");
|
||||||
|
byte[] expected_iv = desfire.GenerateEmptyKey(8);
|
||||||
|
|
||||||
|
Assert.AreEqual(expected_sessionkey, desfire._SessionKey);
|
||||||
|
Assert.AreEqual(expected_iv, desfire._IV);
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
18
NFC_Test/NamespaceSetUp.cs
Normal file
18
NFC_Test/NamespaceSetUp.cs
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
using log4net;
|
||||||
|
using log4net.Config;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace NFC_Test
|
||||||
|
{
|
||||||
|
[SetUpFixture]
|
||||||
|
public class NamespaceSetUp
|
||||||
|
{
|
||||||
|
private static readonly ILog log = LogManager.GetLogger(typeof(NamespaceSetUp));
|
||||||
|
[OneTimeSetUp]
|
||||||
|
public void OneTimeSetUp()
|
||||||
|
{
|
||||||
|
BasicConfigurator.Configure();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
60
NFC_Test/OTA.cs
Normal file
60
NFC_Test/OTA.cs
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
using NUnit.Framework;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using NFC;
|
||||||
|
using NFC.Readers.PCSC;
|
||||||
|
using System.Threading;
|
||||||
|
using NFC.Mifare_DESFire;
|
||||||
|
using NFC.Mifare_DESFire.Enums;
|
||||||
|
using NFC.ISO7816_4;
|
||||||
|
using PCSC.Iso7816;
|
||||||
|
using log4net.Config;
|
||||||
|
|
||||||
|
namespace NFC_Test
|
||||||
|
{
|
||||||
|
[TestFixture, Explicit]
|
||||||
|
public class OTA
|
||||||
|
{
|
||||||
|
private string _ReaderID = "ACS ACR122U PICC Interface 0";
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Init()
|
||||||
|
{
|
||||||
|
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.AuthenticateISO_DES(0x00, desfire.GenerateEmptyKey(16));
|
||||||
|
desfire.Format();
|
||||||
|
|
||||||
|
desfire.AuthenticateISO_DES(0x00, desfire.GenerateEmptyKey(16));
|
||||||
|
|
||||||
|
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.SelectApplication(0xAAFFEE);
|
||||||
|
|
||||||
|
transmit_successfully = true;
|
||||||
|
|
||||||
|
card.Disconnect();
|
||||||
|
};
|
||||||
|
|
||||||
|
reader.CardDiscovered += handler;
|
||||||
|
reader.Start();
|
||||||
|
|
||||||
|
Assert.AreEqual(true, transmit_successfully);
|
||||||
|
|
||||||
|
reader.Stop();
|
||||||
|
reader.CardDiscovered -= handler;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -8,6 +8,7 @@ using System.Threading;
|
|||||||
using NFC.Mifare_DESFire;
|
using NFC.Mifare_DESFire;
|
||||||
using NFC.Mifare_DESFire.Enums;
|
using NFC.Mifare_DESFire.Enums;
|
||||||
using NFC.ISO7816_4;
|
using NFC.ISO7816_4;
|
||||||
|
using PCSC.Iso7816;
|
||||||
|
|
||||||
namespace NFC_Test
|
namespace NFC_Test
|
||||||
{
|
{
|
||||||
@ -140,41 +141,41 @@ namespace NFC_Test
|
|||||||
[TestCase("ACS ACR122U PICC Interface 0", (UInt32)0xAAFFEE)]
|
[TestCase("ACS ACR122U PICC Interface 0", (UInt32)0xAAFFEE)]
|
||||||
public void CreateApplication(string readerID, UInt32 applicationID)
|
public void CreateApplication(string readerID, UInt32 applicationID)
|
||||||
{
|
{
|
||||||
IHardware hardware = new Hardware();
|
//IHardware hardware = new Hardware();
|
||||||
IReader reader = hardware.OpenReader(readerID);
|
//IReader reader = hardware.OpenReader(readerID);
|
||||||
|
|
||||||
bool transmit_successfully = false;
|
//bool transmit_successfully = false;
|
||||||
|
|
||||||
ReaderEventHandler handler = (sender, card) =>
|
//ReaderEventHandler handler = (sender, card) =>
|
||||||
{
|
//{
|
||||||
card.Connect();
|
// card.Connect();
|
||||||
|
|
||||||
MIFARE_DESFire desfire = new MIFARE_DESFire(card);
|
// MIFARE_DESFire desfire = new MIFARE_DESFire(card);
|
||||||
|
|
||||||
byte keysetting1 = desfire.GenerateKeySetting1(ChangeApplicationKey.SAMEKEY, ChangeMasterKeySettings.WITHMASTERKEY, CreateDeleteFile.NOKEY, FileDirectoryAccess.NOKEY, ChangeMasterKey.CHANGEABLE);
|
// byte keysetting1 = desfire.GenerateKeySetting1(ChangeApplicationKey.MASTERKEY, ChangeMasterKeySettings.WITHMASTERKEY, CreateDeleteFile.NOKEY, FileDirectoryAccess.NOKEY, ChangeMasterKey.CHANGEABLE);
|
||||||
byte keysetting2 = desfire.GenerateKeySetting2(CryptoOperationsType.TDES, FileIdentifies.NOTUSED, 0x03);
|
// byte keysetting2 = desfire.GenerateKeySetting2(CryptoOperationsType.TDES, FileIdentifies.NOTUSED, 0x03);
|
||||||
|
|
||||||
APDUCommand cmd = desfire.CreateApplication(applicationID, keysetting1, keysetting2);
|
// APDUCommand cmd = desfire.CreateApplication(applicationID, keysetting1, keysetting2);
|
||||||
|
|
||||||
Console.WriteLine(cmd.ToArray());
|
// Console.WriteLine(cmd.ToArray());
|
||||||
|
|
||||||
APDUResponse response = card.Transmit(cmd);
|
// APDUResponse response = card.Transmit(cmd);
|
||||||
|
|
||||||
if (response.StatusWord == NFC.Mifare_DESFire.APDUStatusWords.OK)
|
// if (response.StatusWord == NFC.Mifare_DESFire.APDUStatusWords.OK)
|
||||||
{
|
// {
|
||||||
transmit_successfully = true;
|
// transmit_successfully = true;
|
||||||
}
|
// }
|
||||||
|
|
||||||
card.Disconnect();
|
// card.Disconnect();
|
||||||
};
|
//};
|
||||||
|
|
||||||
reader.CardDiscovered += handler;
|
//reader.CardDiscovered += handler;
|
||||||
reader.Start();
|
//reader.Start();
|
||||||
|
|
||||||
Assert.AreEqual(true, transmit_successfully);
|
//Assert.AreEqual(true, transmit_successfully);
|
||||||
|
|
||||||
reader.Stop();
|
//reader.Stop();
|
||||||
reader.CardDiscovered -= handler;
|
//reader.CardDiscovered -= handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestCase("ACS ACR122U PICC Interface 0", (UInt32)0xAAFFEE)]
|
[TestCase("ACS ACR122U PICC Interface 0", (UInt32)0xAAFFEE)]
|
||||||
@ -271,7 +272,48 @@ namespace NFC_Test
|
|||||||
|
|
||||||
desfire.SelectApplication(0xAAFFEE);
|
desfire.SelectApplication(0xAAFFEE);
|
||||||
desfire.AuthenticateDES(0x00, GenerateDefaultKey(8));
|
desfire.AuthenticateDES(0x00, GenerateDefaultKey(8));
|
||||||
desfire.ChangeKeyDES(0x01, GenerateDefaultKey(8), new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF });
|
desfire.ChangeKeyDES(0x00, GenerateDefaultKey(8), new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF });
|
||||||
|
|
||||||
|
transmit_successfully = true;
|
||||||
|
|
||||||
|
card.Disconnect();
|
||||||
|
};
|
||||||
|
|
||||||
|
reader.CardDiscovered += handler;
|
||||||
|
reader.Start();
|
||||||
|
|
||||||
|
Assert.AreEqual(true, transmit_successfully);
|
||||||
|
|
||||||
|
reader.Stop();
|
||||||
|
reader.CardDiscovered -= handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestCase("ACS ACR122U PICC Interface 0")]
|
||||||
|
public void ChangeKey_Test(string readerID)
|
||||||
|
{
|
||||||
|
IHardware hardware = new Hardware();
|
||||||
|
IReader reader = hardware.OpenReader(readerID);
|
||||||
|
|
||||||
|
bool transmit_successfully = false;
|
||||||
|
|
||||||
|
ReaderEventHandler handler = (sender, card) =>
|
||||||
|
{
|
||||||
|
card.Connect();
|
||||||
|
|
||||||
|
MIFARE_DESFire desfire = new MIFARE_DESFire(card);
|
||||||
|
|
||||||
|
desfire.SelectApplication(0xAAFFEE);
|
||||||
|
desfire.AuthenticateDES(0x00, GenerateDefaultKey(8));
|
||||||
|
|
||||||
|
APDUCommand cmd = new APDUCommand(IsoCase.Case4Short)
|
||||||
|
{
|
||||||
|
CLA = 0x90,
|
||||||
|
INS = 0xC4,
|
||||||
|
Data = GenerateDefaultKey(1 + 16 + 4 + 4)
|
||||||
|
// KEY_NO, KEY, CRC32, PADDING
|
||||||
|
};
|
||||||
|
|
||||||
|
card.Transmit(cmd);
|
||||||
|
|
||||||
transmit_successfully = true;
|
transmit_successfully = true;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user