2020-10-01 19:06:09 +02:00
|
|
|
|
using NFC.Crypto;
|
2020-10-05 18:39:45 +02:00
|
|
|
|
using NFC.ISO7816_4;
|
2020-10-01 19:06:09 +02:00
|
|
|
|
using NFC.Mifare_DESFire.Enums;
|
2020-09-26 18:02:44 +02:00
|
|
|
|
using PCSC.Iso7816;
|
2020-09-17 16:07:58 +02:00
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
2020-10-01 23:47:47 +02:00
|
|
|
|
using System.Linq;
|
2020-09-17 16:07:58 +02:00
|
|
|
|
|
|
|
|
|
namespace NFC.Mifare_DESFire
|
|
|
|
|
{
|
2020-10-05 18:39:45 +02:00
|
|
|
|
public class MIFARE_DESFire
|
2020-09-17 16:07:58 +02:00
|
|
|
|
{
|
2020-10-05 18:39:45 +02:00
|
|
|
|
// Docs https://hackmd.io/qATu8uYdRnOC40aFrB9afg
|
|
|
|
|
|
|
|
|
|
#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(ICard card)
|
|
|
|
|
{
|
|
|
|
|
_Card = card;
|
|
|
|
|
}
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region Properties
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// ICard Implementation used to transmit APDUCommands and recive APDUResponses
|
|
|
|
|
/// </summary>
|
|
|
|
|
private ICard _Card;
|
2020-10-06 15:59:53 +02:00
|
|
|
|
|
|
|
|
|
public byte[] _SessionKey;
|
|
|
|
|
public byte[] _IV;
|
2020-10-05 18:39:45 +02:00
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region Methods
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Generate Byte Array filled with 0
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="size">Size of Array</param>
|
|
|
|
|
public byte[] GenerateDefaultKey(uint size)
|
2020-10-01 19:06:09 +02:00
|
|
|
|
{
|
2020-10-05 18:39:45 +02:00
|
|
|
|
byte[] key = new byte[size];
|
2020-10-01 19:06:09 +02:00
|
|
|
|
for (int i = 0; i < size; i++)
|
|
|
|
|
{
|
2020-10-06 15:59:53 +02:00
|
|
|
|
key[i] = 0;
|
2020-10-01 19:06:09 +02:00
|
|
|
|
}
|
|
|
|
|
|
2020-10-05 18:39:45 +02:00
|
|
|
|
return key;
|
2020-10-01 19:06:09 +02:00
|
|
|
|
}
|
|
|
|
|
|
2020-10-05 18:39:45 +02:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Converts byte[] to string with HEX Code
|
|
|
|
|
/// No 0x is created
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="data">Data</param>
|
|
|
|
|
public string ConvertToHexString(byte[] data)
|
|
|
|
|
{
|
2020-10-07 19:25:51 +02:00
|
|
|
|
return BitConverter.ToString(data).Replace("-", " ");
|
2020-10-05 18:39:45 +02:00
|
|
|
|
}
|
2020-09-26 18:02:44 +02:00
|
|
|
|
|
2020-10-07 19:25:51 +02:00
|
|
|
|
|
2020-10-05 18:39:45 +02:00
|
|
|
|
public void CheckAPDUResponse(APDUResponse response)
|
2020-09-26 18:02:44 +02:00
|
|
|
|
{
|
2020-10-05 18:39:45 +02:00
|
|
|
|
|
2020-09-26 18:02:44 +02:00
|
|
|
|
}
|
|
|
|
|
|
2020-10-06 15:59:53 +02:00
|
|
|
|
public bool CheckKey(byte[] key)
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
GetKeyTypeDES(key);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
catch
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Key Types used for DESFire Cards
|
|
|
|
|
/// </summary>
|
|
|
|
|
public enum KeyType
|
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// DES Key - 8 Byte - 64 Bit
|
|
|
|
|
/// </summary>
|
|
|
|
|
DES,
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Triple DES Key with two DES Keys - 16 Byte - 128 Bit
|
|
|
|
|
/// </summary>
|
|
|
|
|
TDES_2K,
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Triple DES Key with three DES Keys - 24 Byte - 192 Bit
|
|
|
|
|
/// </summary>
|
|
|
|
|
TDES_3K,
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// AES Key - 16 Byte - 128 Bit
|
|
|
|
|
/// </summary>
|
|
|
|
|
AES
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Check the Key Length to get Type of DES/3DES Key
|
|
|
|
|
/// </summary>
|
|
|
|
|
public KeyType GetKeyTypeDES(byte[] key)
|
|
|
|
|
{
|
|
|
|
|
switch(key.Length)
|
|
|
|
|
{
|
|
|
|
|
case 8:
|
|
|
|
|
return KeyType.DES;
|
|
|
|
|
case 16:
|
|
|
|
|
return KeyType.TDES_2K;
|
|
|
|
|
case 24:
|
|
|
|
|
return KeyType.TDES_3K;
|
|
|
|
|
default:
|
|
|
|
|
throw new ArgumentException(string.Format("No valid DES/3DES Key Size({0})", key.Length));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-05 18:39:45 +02:00
|
|
|
|
#region Methods for Crypto Operation
|
2020-09-17 16:07:58 +02:00
|
|
|
|
/// <summary>
|
2020-10-05 18:39:45 +02:00
|
|
|
|
/// Return a copy of the last Block of data
|
2020-09-17 16:07:58 +02:00
|
|
|
|
/// </summary>
|
2020-10-05 18:39:45 +02:00
|
|
|
|
/// <param name="data">Data compatible to blocksize</param>
|
|
|
|
|
/// <param name="blocksize">Size of Block, default is 8</param>
|
|
|
|
|
public byte[] GetLastBlock(byte[] data, uint blocksize = 8)
|
2020-09-17 16:07:58 +02:00
|
|
|
|
{
|
2020-10-05 18:39:45 +02:00
|
|
|
|
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[] block = new byte[blocksize];
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < blocksize; i++)
|
|
|
|
|
{
|
|
|
|
|
block[i] = data[data.Length - blocksize + i];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return block;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Rotates Array to the left
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="data">Data</param>
|
|
|
|
|
/// <returns>Copy of data</returns>
|
|
|
|
|
public byte[] rotateLeft(byte[] data)
|
|
|
|
|
{
|
|
|
|
|
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;
|
2020-09-17 16:07:58 +02:00
|
|
|
|
}
|
|
|
|
|
|
2020-10-05 18:39:45 +02:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Rotates Array to the right
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="data">Data</param>
|
|
|
|
|
/// <returns>Copy of data</returns>
|
|
|
|
|
public byte[] rotateRight(byte[] data)
|
|
|
|
|
{
|
|
|
|
|
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</returns>
|
|
|
|
|
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;
|
|
|
|
|
}
|
2020-10-06 15:59:53 +02:00
|
|
|
|
|
|
|
|
|
public byte[] xor(byte[] a, byte[] b)
|
|
|
|
|
{
|
|
|
|
|
if(a.Length != b.Length)
|
|
|
|
|
{
|
|
|
|
|
throw new ArgumentException("Array are not same Length");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
byte[] c = new byte[a.Length];
|
|
|
|
|
|
|
|
|
|
for(int i = 0; i < a.Length; i++)
|
|
|
|
|
{
|
|
|
|
|
c[i] = (byte)(a[i] ^ b[i]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return c;
|
|
|
|
|
}
|
2020-10-05 18:39:45 +02:00
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region DESFire Commands
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Get Application IDs from Card
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <returns>AIDs as Array</returns>
|
|
|
|
|
public UInt32[] GetApplicationIDs()
|
2020-09-17 16:07:58 +02:00
|
|
|
|
{
|
|
|
|
|
APDUCommand cmd = new APDUCommand(IsoCase.Case2Short)
|
|
|
|
|
{
|
|
|
|
|
CLA = 0x90,
|
|
|
|
|
INS = (byte)APDUInstructions.GET_APPLICATION_IDS
|
|
|
|
|
};
|
|
|
|
|
|
2020-10-05 18:39:45 +02:00
|
|
|
|
APDUResponse response = _Card.Transmit(cmd);
|
2020-09-17 16:07:58 +02:00
|
|
|
|
|
2020-10-05 18:39:45 +02:00
|
|
|
|
CheckAPDUResponse(response);
|
|
|
|
|
|
|
|
|
|
if (response.Body.Length % 3 != 0)
|
|
|
|
|
{
|
|
|
|
|
throw new Exception(string.Format("Invalid body length (was: {0}).", response.Body.Length));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(response.Body.Length == 0)
|
2020-09-17 16:07:58 +02:00
|
|
|
|
{
|
2020-10-05 18:39:45 +02:00
|
|
|
|
throw new Exception("Missing PICC Entry 0x000000.");
|
2020-09-17 16:07:58 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
List<UInt32> applicationIDs = new List<UInt32>();
|
|
|
|
|
|
2020-10-05 18:39:45 +02:00
|
|
|
|
for (int i = 0; i < response.Body.Length; i += 3)
|
2020-09-17 16:07:58 +02:00
|
|
|
|
{
|
|
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-05 18:39:45 +02:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Select Application by AID
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="aid">3 Byte AID</param>
|
|
|
|
|
public void SelectApplication(UInt32 aid)
|
|
|
|
|
{
|
|
|
|
|
byte[] id_byte = BitConverter.GetBytes(aid);
|
|
|
|
|
|
2020-10-06 15:59:53 +02:00
|
|
|
|
APDUCommand cmd = new APDUCommand(IsoCase.Case4Short)
|
2020-10-05 18:39:45 +02:00
|
|
|
|
{
|
|
|
|
|
CLA = 0x90,
|
|
|
|
|
INS = (byte)APDUInstructions.SELECT_APPLICATION,
|
|
|
|
|
Data = new byte[]
|
|
|
|
|
{
|
|
|
|
|
id_byte[0],
|
|
|
|
|
id_byte[1],
|
|
|
|
|
id_byte[2]
|
2020-10-06 15:59:53 +02:00
|
|
|
|
}
|
2020-10-05 18:39:45 +02:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
APDUResponse response = _Card.Transmit(cmd);
|
|
|
|
|
|
|
|
|
|
CheckAPDUResponse(response);
|
|
|
|
|
}
|
|
|
|
|
#endregion
|
|
|
|
|
|
2020-10-06 15:59:53 +02:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Expand Array to Block Size
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="data"></param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
public byte[] expandToBlockSize(byte[] data, int bocksize)
|
|
|
|
|
{
|
|
|
|
|
int diff = data.Length % bocksize;
|
|
|
|
|
if (diff == 0)
|
|
|
|
|
{
|
|
|
|
|
return data;
|
|
|
|
|
}
|
2020-10-05 18:39:45 +02:00
|
|
|
|
|
2020-10-07 19:25:51 +02:00
|
|
|
|
byte[] expand = new byte[data.Length + bocksize - diff];
|
2020-10-06 15:59:53 +02:00
|
|
|
|
|
|
|
|
|
data.CopyTo(expand, 0);
|
|
|
|
|
|
|
|
|
|
for(int i = expand.Length - 1; i > data.Length - 1; i--)
|
|
|
|
|
{
|
|
|
|
|
expand[i] = 0x00;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return expand;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void ChangeKeyDES(byte key_no, byte[] key_new, byte[] key_current)
|
|
|
|
|
{
|
2020-10-07 19:25:51 +02:00
|
|
|
|
Console.WriteLine("key_new: {0}", ConvertToHexString(key_new));
|
|
|
|
|
key_new = concatenate(key_new, key_new);
|
|
|
|
|
Console.WriteLine("key_new_double: {0}", ConvertToHexString(key_new));
|
2020-10-06 15:59:53 +02:00
|
|
|
|
|
2020-10-07 19:25:51 +02:00
|
|
|
|
byte[] header = new byte[]
|
2020-10-06 15:59:53 +02:00
|
|
|
|
{
|
2020-10-07 19:25:51 +02:00
|
|
|
|
0xC4, key_no
|
|
|
|
|
};
|
|
|
|
|
Console.WriteLine("header: {0}", ConvertToHexString(header));
|
2020-10-06 15:59:53 +02:00
|
|
|
|
|
2020-10-07 19:25:51 +02:00
|
|
|
|
byte[] cmd_ = concatenate(header, key_new);
|
|
|
|
|
Console.WriteLine("cmd_: {0}", ConvertToHexString(cmd_));
|
2020-10-06 15:59:53 +02:00
|
|
|
|
|
2020-10-07 19:25:51 +02:00
|
|
|
|
CRC32 crc = new CRC32();
|
|
|
|
|
byte[] cmd_crc = crc.Calculate(cmd_);
|
|
|
|
|
Console.WriteLine("cmd_crc: {0}", ConvertToHexString(cmd_crc));
|
|
|
|
|
byte[] cryptogram = concatenate(key_new, cmd_crc);
|
|
|
|
|
Console.WriteLine("cryptogram: {0}", ConvertToHexString(cryptogram));
|
2020-10-06 15:59:53 +02:00
|
|
|
|
|
2020-10-07 19:25:51 +02:00
|
|
|
|
byte[] cryptogram_block = expandToBlockSize(cryptogram, 8);
|
|
|
|
|
Console.WriteLine("cryptogram_block: {0}", ConvertToHexString(cryptogram_block));
|
2020-10-06 15:59:53 +02:00
|
|
|
|
|
|
|
|
|
DES des = new DES();
|
|
|
|
|
|
2020-10-07 19:25:51 +02:00
|
|
|
|
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));
|
2020-10-06 15:59:53 +02:00
|
|
|
|
|
|
|
|
|
APDUCommand cmd = new APDUCommand(IsoCase.Case4Short)
|
|
|
|
|
{
|
|
|
|
|
CLA = 0x90,
|
2020-10-07 19:25:51 +02:00
|
|
|
|
INS = 0xC4,
|
|
|
|
|
Data = data
|
2020-10-06 15:59:53 +02:00
|
|
|
|
};
|
2020-10-07 19:25:51 +02:00
|
|
|
|
Console.WriteLine("cmd: {0}", ConvertToHexString(cmd.ToArray()));
|
2020-10-06 15:59:53 +02:00
|
|
|
|
|
|
|
|
|
APDUResponse response = _Card.Transmit(cmd);
|
|
|
|
|
|
|
|
|
|
CheckAPDUResponse(response);
|
|
|
|
|
}
|
2020-10-05 18:39:45 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Create new Application with AID
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="aid">Appilication ID</param>
|
|
|
|
|
public APDUCommand CreateApplication(UInt64 aid)
|
|
|
|
|
{
|
|
|
|
|
throw new NotImplementedException();
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-26 18:02:44 +02:00
|
|
|
|
public void Format()
|
|
|
|
|
{
|
|
|
|
|
throw new NotImplementedException();
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-01 19:06:09 +02:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Authenticate to Card
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="key_id">0x01 - 0x0D</param>
|
|
|
|
|
/// <param name="key"></param>
|
2020-10-05 18:39:45 +02:00
|
|
|
|
|
|
|
|
|
// TODO
|
|
|
|
|
public void AuthenticateDES(byte key_id, byte[] key)
|
2020-09-26 18:02:44 +02:00
|
|
|
|
{
|
2020-10-01 23:47:47 +02:00
|
|
|
|
APDUCommand cmd_challange_request = new APDUCommand(IsoCase.Case4Short)
|
2020-10-01 19:06:09 +02:00
|
|
|
|
{
|
|
|
|
|
CLA = 0x90,
|
|
|
|
|
INS = (byte)0x1A,
|
|
|
|
|
Data = new byte[]
|
|
|
|
|
{
|
|
|
|
|
key_id
|
|
|
|
|
}
|
|
|
|
|
};
|
2020-10-01 23:47:47 +02:00
|
|
|
|
APDUResponse response = _Card.Transmit(cmd_challange_request);
|
2020-10-01 19:06:09 +02:00
|
|
|
|
|
2020-10-01 23:47:47 +02:00
|
|
|
|
byte[] rndB_enc = response.Body;
|
2020-10-05 18:39:45 +02:00
|
|
|
|
Console.WriteLine("rndB_enc: {0}", ConvertToHexString(rndB_enc));
|
2020-10-01 23:47:47 +02:00
|
|
|
|
|
|
|
|
|
DES des = new DES();
|
|
|
|
|
byte[] rndB = des.Decrypt(rndB_enc, key, GenerateDefaultKey(8));
|
2020-10-05 18:39:45 +02:00
|
|
|
|
Console.WriteLine("rndB: {0}", ConvertToHexString(rndB));
|
2020-10-01 23:47:47 +02:00
|
|
|
|
|
|
|
|
|
byte[] iv = new byte[8];
|
|
|
|
|
rndB.CopyTo(iv, 0);
|
|
|
|
|
|
|
|
|
|
byte[] rndB_rl = rotateLeft(rndB);
|
2020-10-05 18:39:45 +02:00
|
|
|
|
Console.WriteLine("rndB_enc: {0}", ConvertToHexString(rndB_rl));
|
2020-10-01 19:06:09 +02:00
|
|
|
|
|
|
|
|
|
byte[] rndA = new byte[]
|
|
|
|
|
{
|
2020-10-01 23:47:47 +02:00
|
|
|
|
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08
|
2020-10-01 19:06:09 +02:00
|
|
|
|
};
|
2020-10-05 18:39:45 +02:00
|
|
|
|
Console.WriteLine("rndA: {0}", ConvertToHexString(rndA));
|
2020-10-01 19:06:09 +02:00
|
|
|
|
|
2020-10-01 23:47:47 +02:00
|
|
|
|
byte[] rndAB = concatenate(rndA, rndB_rl);
|
2020-10-05 18:39:45 +02:00
|
|
|
|
Console.WriteLine("rndAB: {0}", ConvertToHexString(rndAB));
|
2020-10-01 19:06:09 +02:00
|
|
|
|
|
2020-10-01 23:47:47 +02:00
|
|
|
|
byte[] rndAB_enc = des.Encrypt(rndAB, key, rndB_enc);
|
2020-10-05 18:39:45 +02:00
|
|
|
|
Console.WriteLine("rndA_rndB_enc: {0}", ConvertToHexString(rndAB_enc));
|
|
|
|
|
iv = GetLastBlock(rndAB_enc);
|
2020-10-01 19:06:09 +02:00
|
|
|
|
|
2020-10-01 23:47:47 +02:00
|
|
|
|
APDUCommand cmd_challange_response = new APDUCommand(IsoCase.Case4Short)
|
2020-10-01 19:06:09 +02:00
|
|
|
|
{
|
|
|
|
|
CLA = 0x90,
|
|
|
|
|
INS = (byte)0xAF,
|
2020-10-01 23:47:47 +02:00
|
|
|
|
Data = rndAB_enc
|
2020-10-01 19:06:09 +02:00
|
|
|
|
};
|
2020-10-05 18:39:45 +02:00
|
|
|
|
Console.WriteLine("cmd_challange_response: {0}", ConvertToHexString(cmd_challange_response.ToArray()));
|
2020-10-01 19:06:09 +02:00
|
|
|
|
|
2020-10-01 23:47:47 +02:00
|
|
|
|
response = _Card.Transmit(cmd_challange_response);
|
2020-10-01 19:06:09 +02:00
|
|
|
|
|
|
|
|
|
byte[] encryptedRndAFromCard = response.Body;
|
2020-10-05 18:39:45 +02:00
|
|
|
|
Console.WriteLine("encryptedRndAFromCard: {0}", ConvertToHexString(encryptedRndAFromCard));
|
2020-10-01 19:06:09 +02:00
|
|
|
|
|
2020-10-01 23:47:47 +02:00
|
|
|
|
byte[] rotatedRndAFromCard = des.Decrypt(encryptedRndAFromCard, key, iv);
|
2020-10-05 18:39:45 +02:00
|
|
|
|
Console.WriteLine("rotatedRndAFromCard: {0}", ConvertToHexString(rotatedRndAFromCard));
|
2020-10-01 19:06:09 +02:00
|
|
|
|
|
|
|
|
|
byte[] rndAFromCard = rotateRight(rotatedRndAFromCard);
|
2020-10-05 18:39:45 +02:00
|
|
|
|
Console.WriteLine("rndAFromCard: {0}", ConvertToHexString(rndAFromCard));
|
2020-10-01 19:06:09 +02:00
|
|
|
|
|
2020-10-01 23:47:47 +02:00
|
|
|
|
if (!rndA.SequenceEqual(rndAFromCard))
|
2020-10-01 19:06:09 +02:00
|
|
|
|
{
|
2020-10-05 18:39:45 +02:00
|
|
|
|
throw new Exception("PICC Challenge is not correct answered.");
|
2020-10-01 19:06:09 +02:00
|
|
|
|
}
|
2020-10-06 15:59:53 +02:00
|
|
|
|
|
|
|
|
|
_SessionKey = GenerateDESSesionKey(rndA, rndB);
|
|
|
|
|
_IV = GenerateDefaultKey(8);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private byte[] GenerateDESSesionKey(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];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Set Key Verion of Key to 0x55 to LSB
|
|
|
|
|
// TODO
|
|
|
|
|
byte[] key_version = SetKeyVersion(sesssionkey, 0x55);
|
|
|
|
|
|
|
|
|
|
return concatenate(key_version, key_version);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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;
|
2020-09-17 16:07:58 +02:00
|
|
|
|
}
|
|
|
|
|
|
2020-09-26 18:02:44 +02:00
|
|
|
|
public void ChangeApplicationMasterKey(byte[] aPP_MasterKey)
|
2020-09-25 20:23:33 +02:00
|
|
|
|
{
|
2020-09-26 18:02:44 +02:00
|
|
|
|
throw new NotImplementedException();
|
2020-09-25 20:23:33 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Delete Application by ID
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="id">3 Byte ID</param>
|
2020-10-06 15:59:53 +02:00
|
|
|
|
public void DeleteApplication(UInt32 id)
|
2020-09-25 20:23:33 +02:00
|
|
|
|
{
|
|
|
|
|
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
|
|
|
|
|
};
|
|
|
|
|
|
2020-10-06 15:59:53 +02:00
|
|
|
|
APDUResponse response = _Card.Transmit(cmd);
|
|
|
|
|
CheckAPDUResponse(response);
|
2020-09-25 20:23:33 +02:00
|
|
|
|
}
|
|
|
|
|
|
2020-09-26 18:02:44 +02:00
|
|
|
|
public void ChangeApplicationKey(int v, byte[] aPP_Key_1)
|
|
|
|
|
{
|
|
|
|
|
throw new NotImplementedException();
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-17 16:07:58 +02:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Select Application by ID
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="id">3 Byte ID</param>
|
2020-09-25 20:23:33 +02:00
|
|
|
|
public APDUCommand CreateApplication(UInt32 id, byte keysetting1, byte keysetting2)
|
2020-09-17 16:07:58 +02:00
|
|
|
|
{
|
|
|
|
|
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],
|
2020-09-25 20:23:33 +02:00
|
|
|
|
id_byte[2],
|
|
|
|
|
keysetting1,
|
|
|
|
|
keysetting2
|
2020-09-17 16:07:58 +02:00
|
|
|
|
},
|
|
|
|
|
Le = 0x00
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return cmd;
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-01 19:06:09 +02:00
|
|
|
|
public void CreateFile(byte fabAccessIdentFileID, FileCommunication pLAIN, ushort fileAccessRight, UInt32 v)
|
2020-09-26 18:02:44 +02:00
|
|
|
|
{
|
|
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-25 20:23:33 +02:00
|
|
|
|
/// <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);
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-07 19:25:51 +02:00
|
|
|
|
|
2020-09-17 16:07:58 +02:00
|
|
|
|
}
|
|
|
|
|
}
|