diff --git a/NFC/APDUCommand.cs b/NFC/APDUCommand.cs index b504df2..aac61ec 100644 --- a/NFC/APDUCommand.cs +++ b/NFC/APDUCommand.cs @@ -1,22 +1,149 @@ -using System; +using NFC.Helper; +using System; +using System.Collections.Generic; +using System.Linq; namespace NFC { + /// + /// Application Protocol Data Unit + /// https://de.wikipedia.org/wiki/Application_Protocol_Data_Unit + /// https://github.com/danm-de/pcsc-sharp/blob/246fc4303190184d6acd98a2d66f48cb7ffd7094/src/PCSC.Iso7816/CommandApdu.cs + /// public class APDUCommand { - public APDUCommand(IsoCase isoCase) + #region Constructors + public APDUCommand(IsoCase isoCase, SCardProtocol sCardProtocol = SCardProtocol.ANY) { Case = isoCase; + Protocol = sCardProtocol; + Data = new byte[0]; } + #endregion + #region Properties public IsoCase Case { get; set; } - public byte CLA { get; set; } - public byte INS { get; set; } - public byte[] Data { get; set; } + public SCardProtocol Protocol { get; set; } + public byte CLA { get; set; } = 0x00; + public byte INS { get; set; } = 0x00; + public byte P1 { get; set; } = 0x00; + public byte P2 { get; set; } = 0x00; + public byte LC + { + get + { + return (byte)Data.Length; + } + } + public byte[] Data { get; set; } + public byte LE { get; set; } = 0x00; + #endregion + + #region Methods public byte[] ToArray() { - throw new NotImplementedException(); + byte[] header = ByteOperation.Concatenate(new byte[] { CLA }, new byte[] { INS }, new byte[] { P1 }, new byte[] { P2 }); + switch (Case) + { + case IsoCase.Case1: + /* Regarding to OpenSC: T0 needs one additional + * byte containing 0x00. */ + if (Protocol == SCardProtocol.T0) + { + return ByteOperation.Concatenate(header, new byte[] { 0x00 }); + } + else + { + return header; + } + + case IsoCase.Case2Short: + return ByteOperation.Concatenate(header, new byte[] { LE }); + + case IsoCase.Case3Short: + return ByteOperation.Concatenate(header, new byte[] { LC }, Data); + + case IsoCase.Case4Short: + /* Regarding to OpenSC: T0 has no Le */ + if (Protocol == SCardProtocol.T0) + { + return ByteOperation.Concatenate(header, new byte[] { LC }, Data, new byte[] { 0x00 }); + } + else + { + return ByteOperation.Concatenate(header, new byte[] { LC }, Data, new byte[] { LE }); + } + + default: + throw new NotSupportedException(string.Format("IsoCase {0} is not supported.", Case)); + } } + + public override bool Equals(object obj) + { + return obj is APDUCommand command && + Case == command.Case && + Protocol == command.Protocol && + CLA == command.CLA && + INS == command.INS && + P1 == command.P1 && + P2 == command.P2 && + Data.SequenceEqual(command.Data) && + LE == command.LE; + } + + public override int GetHashCode() + { + int hashCode = -98047210; + hashCode = hashCode * -1521134295 + Case.GetHashCode(); + hashCode = hashCode * -1521134295 + Protocol.GetHashCode(); + hashCode = hashCode * -1521134295 + CLA.GetHashCode(); + hashCode = hashCode * -1521134295 + INS.GetHashCode(); + hashCode = hashCode * -1521134295 + P1.GetHashCode(); + hashCode = hashCode * -1521134295 + P2.GetHashCode(); + hashCode = hashCode * -1521134295 + LC.GetHashCode(); + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(Data); + hashCode = hashCode * -1521134295 + LE.GetHashCode(); + return hashCode; + } + + public override string ToString() + { + string pattern_case1 = "(CASE: 1) CLA: 0x{0:x} | INS: 0x{1:x} | P1: 0x{2:x} | P2: 0x{3:x}"; + string pattern_case2 = "(CASE: 2) CLA: 0x{0:x} | INS: 0x{1:x} | P1: 0x{2:x} | P2: 0x{3:x} | LE: 0x{4:x} |"; + string pattern_case3 = "(CASE: 3) CLA: 0x{0:x} | INS: 0x{1:x} | P1: 0x{2:x} | P2: 0x{3:x} | LC: 0x{4:x} | Data: {5:x}"; + string pattern_case4 = "(CASE: 4) CLA: 0x{0:x} | INS: 0x{1:x} | P1: 0x{2:x} | P2: 0x{3:x} | LC: 0x{4:x} | Data: {5:x} | LE: 0x{6:x} |"; + + switch (Case) + { + case IsoCase.Case1: + return string.Format(pattern_case1, CLA, INS, P1, P2); + case IsoCase.Case2Short: + case IsoCase.Case2Extended: + return string.Format(pattern_case2, CLA, INS, P1, P2, LE); + case IsoCase.Case3Short: + case IsoCase.Case3Extended: + return string.Format(pattern_case3, CLA, INS, P1, P2, LC, BitConverter.ToString(Data).Replace("-", "").ToLower()); + case IsoCase.Case4Short: + case IsoCase.Case4Extended: + return string.Format(pattern_case4, CLA, INS, P1, P2, LC, BitConverter.ToString(Data).Replace("-", "").ToLower(), LE); + default: + throw new Exception("Unknown IsoCase"); + } + } + #endregion + + #region Operator Overloading + public static bool operator ==(APDUCommand obj1, APDUCommand obj2) + { + return obj1.Equals(obj2); + } + + public static bool operator !=(APDUCommand obj1, APDUCommand obj2) + { + return !(obj1.Equals(obj2)); + } + #endregion } } diff --git a/NFC/Cards/NXP MIFARE DESFire/NXP_MIFARE_DESFire.cs b/NFC/Cards/NXP MIFARE DESFire/NXP_MIFARE_DESFire.cs index 2a15df3..35a15dd 100644 --- a/NFC/Cards/NXP MIFARE DESFire/NXP_MIFARE_DESFire.cs +++ b/NFC/Cards/NXP MIFARE DESFire/NXP_MIFARE_DESFire.cs @@ -34,7 +34,7 @@ namespace NFC.Cards.NXP_MIFARE_DESFire /// /// ICard Implementation used to transmit APDUCommands and recive APDUResponses /// - private ICard _Card; + private readonly ICard _Card; /// /// SessionKey, is set after Successfull Authentication @@ -50,38 +50,6 @@ namespace NFC.Cards.NXP_MIFARE_DESFire #region Methods #region Helper Methods - /// - /// Generate Byte Array filled with 0 - /// - /// Size of Array - public byte[] GenerateEmptyArray(uint size) - { - byte[] key = new byte[size]; - for (int i = 0; i < size; i++) - { - key[i] = 0; - } - - return key; - } - - /// - /// Get Range of Array Elements - /// - /// Array - /// Offset in Byte - /// Lenght to read in Byte - /// new Array with Range of Array Elements - public byte[] GetSubArray(byte[] array, long offset, long length) - { - byte[] subarray = new byte[length]; - for (long i = offset; i < offset + length; i++) - { - subarray[i - offset] = array[i]; - } - return subarray; - } - /// /// Check APDU Response for DESFire Error Codes /// https://www.nxp.com/docs/en/data-sheet/MF2DLHX0.pdf @@ -138,167 +106,6 @@ namespace NFC.Cards.NXP_MIFARE_DESFire #endregion #region Crypto Operation - /// - /// Return a copy of the last Block of data - /// - /// Data compatible to blocksize - /// in byte - 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; - } - - /// - /// Expand Array to Block Size, fill with 0x00 - /// - /// - public byte[] ExpandToBlockSize(byte[] data, uint bocksize) - { - if (data == null) - { - throw new ArgumentNullException("Data cannot be null."); - } - - 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; - } - - /// - /// Rotates Array to the left - /// - /// Data - /// Copy of data - 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; - } - - /// - /// Rotates Array to the right - /// - /// Data - /// Copy of data - 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; - } - - /// - /// Concatenates Arrays - /// - /// List of Byte Array - public byte[] Concatenate(params byte[][] data) - { - if (data == null) - { - throw new ArgumentNullException("Data cannot be null."); - } - - List cat = new List(); - - foreach (byte[] d in data) - { - cat.AddRange(d); - } - - return cat.ToArray(); - } - - /// - /// Boolean Operation XOR on all Bytes - /// - /// Array A - /// Array B - /// Copy of Data - 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; - } - - /// /// Generates SessionKey for DES Authentification /// @@ -320,7 +127,7 @@ namespace NFC.Cards.NXP_MIFARE_DESFire } // DES SessionKey is a double DES Key - return Concatenate(sesssionkey, sesssionkey); + return ByteOperation.Concatenate(sesssionkey, sesssionkey); } /// @@ -484,12 +291,12 @@ namespace NFC.Cards.NXP_MIFARE_DESFire _Log.DebugFormat("rndB_enc: {0}", HexConverter.ConvertToHexString(rndB_enc)); TDES des = new TDES(); - byte[] rndB = des.Decrypt(rndB_enc, key, GenerateEmptyArray(8)); + byte[] rndB = des.Decrypt(rndB_enc, key, ByteOperation.GenerateEmptyArray(8)); _Log.DebugFormat("rndB: {0}", HexConverter.ConvertToHexString(rndB)); rndB.CopyTo(iv, 0); - byte[] rndB_rl = RotateLeft(rndB); + byte[] rndB_rl = ByteOperation.RotateLeft(rndB); _Log.DebugFormat("rndB_enc: {0}", HexConverter.ConvertToHexString(rndB_rl)); if (rndA == null) @@ -500,12 +307,12 @@ namespace NFC.Cards.NXP_MIFARE_DESFire } _Log.DebugFormat("rndA: {0}", HexConverter.ConvertToHexString(rndA)); - byte[] rndAB = Concatenate(rndA, rndB_rl); + byte[] rndAB = ByteOperation.Concatenate(rndA, rndB_rl); _Log.DebugFormat("rndAB: {0}", HexConverter.ConvertToHexString(rndAB)); byte[] rndAB_enc = des.Encrypt(rndAB, key, rndB_enc); _Log.DebugFormat("rndAB_enc: {0}", HexConverter.ConvertToHexString(rndAB_enc)); - iv = ExtractLastBlock(rndAB_enc, 8); + iv = ByteOperation.ExtractLastBlock(rndAB_enc, 8); APDUCommand cmd_challange_response = new APDUCommand(IsoCase.Case4Short) { @@ -526,7 +333,7 @@ namespace NFC.Cards.NXP_MIFARE_DESFire byte[] rotatedRndAFromCard = des.Decrypt(encryptedRndAFromCard, key, iv); _Log.DebugFormat("rotatedRndAFromCard: {0}", HexConverter.ConvertToHexString(rotatedRndAFromCard)); - byte[] rndAFromCard = RotateRight(rotatedRndAFromCard); + byte[] rndAFromCard = ByteOperation.RotateRight(rotatedRndAFromCard); _Log.DebugFormat("rndAFromCard: {0}", HexConverter.ConvertToHexString(rndAFromCard)); if (!rndA.SequenceEqual(rndAFromCard)) @@ -539,7 +346,7 @@ namespace NFC.Cards.NXP_MIFARE_DESFire _SessionKey = GenerateSesionKey_DES(rndA, rndB); _Log.DebugFormat("SessionKey: {0}", HexConverter.ConvertToHexString(_SessionKey)); - _IV = GenerateEmptyArray(8); + _IV = ByteOperation.GenerateEmptyArray(8); _Log.DebugFormat("IV: {0}", HexConverter.ConvertToHexString(_IV)); } @@ -637,12 +444,12 @@ namespace NFC.Cards.NXP_MIFARE_DESFire _Log.DebugFormat("rndB_enc: {0}", HexConverter.ConvertToHexString(rndB_enc)); AES aes = new AES(); - byte[] rndB = aes.Decrypt(rndB_enc, key, GenerateEmptyArray(16)); + byte[] rndB = aes.Decrypt(rndB_enc, key, ByteOperation.GenerateEmptyArray(16)); _Log.DebugFormat("rndB: {0}", HexConverter.ConvertToHexString(rndB)); rndB.CopyTo(iv, 0); - byte[] rndB_rl = RotateLeft(rndB); + byte[] rndB_rl = ByteOperation.RotateLeft(rndB); _Log.DebugFormat("rndB_enc: {0}", HexConverter.ConvertToHexString(rndB_rl)); if (rndA == null) @@ -653,12 +460,12 @@ namespace NFC.Cards.NXP_MIFARE_DESFire } _Log.DebugFormat("rndA: {0}", HexConverter.ConvertToHexString(rndA)); - byte[] rndAB = Concatenate(rndA, rndB_rl); + byte[] rndAB = ByteOperation.Concatenate(rndA, rndB_rl); _Log.DebugFormat("rndAB: {0}", HexConverter.ConvertToHexString(rndAB)); byte[] rndAB_enc = aes.Encrypt(rndAB, key, rndB_enc); _Log.DebugFormat("rndAB_enc: {0}", HexConverter.ConvertToHexString(rndAB_enc)); - iv = ExtractLastBlock(rndAB_enc, 16); + iv = ByteOperation.ExtractLastBlock(rndAB_enc, 16); APDUCommand cmd_challange_response = new APDUCommand(IsoCase.Case4Short) { @@ -679,7 +486,7 @@ namespace NFC.Cards.NXP_MIFARE_DESFire byte[] rotatedRndAFromCard = aes.Decrypt(encryptedRndAFromCard, key, iv); _Log.DebugFormat("rotatedRndAFromCard: {0}", HexConverter.ConvertToHexString(rotatedRndAFromCard)); - byte[] rndAFromCard = RotateRight(rotatedRndAFromCard); + byte[] rndAFromCard = ByteOperation.RotateRight(rotatedRndAFromCard); _Log.DebugFormat("rndAFromCard: {0}", HexConverter.ConvertToHexString(rndAFromCard)); if (!rndA.SequenceEqual(rndAFromCard)) @@ -690,7 +497,7 @@ namespace NFC.Cards.NXP_MIFARE_DESFire _SessionKey = GenerateSesionKey_AES(rndA, rndB); _Log.DebugFormat("SessionKey: {0}", HexConverter.ConvertToHexString(_SessionKey)); - _IV = GenerateEmptyArray(16); + _IV = ByteOperation.GenerateEmptyArray(16); _Log.DebugFormat("IV: {0}", HexConverter.ConvertToHexString(_IV)); } @@ -716,28 +523,28 @@ namespace NFC.Cards.NXP_MIFARE_DESFire _Log.DebugFormat("header: {0}", HexConverter.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); + byte[] key_and_version = ByteOperation.Concatenate(new_key, new byte[] { key_version }); + byte[] command = ByteOperation.Concatenate(header, key_and_version); _Log.DebugFormat("command: {0}", HexConverter.ConvertToHexString(command)); CRC32 crc32 = new CRC32(); byte[] crc = crc32.Calculate(command); _Log.DebugFormat("crc: {0}", HexConverter.ConvertToHexString(crc)); - byte[] cryptogram = Concatenate(key_and_version, crc); + byte[] cryptogram = ByteOperation.Concatenate(key_and_version, crc); _Log.DebugFormat("cryptogram: {0}", HexConverter.ConvertToHexString(cryptogram)); - byte[] cryptogram_block = ExpandToBlockSize(cryptogram, 16); + byte[] cryptogram_block = ByteOperation.ExpandToBlockSize(cryptogram, 16); _Log.DebugFormat("cryptogram_block: {0}", HexConverter.ConvertToHexString(cryptogram_block)); AES aes = new AES(); byte[] cryptogram_enc = aes.Encrypt(cryptogram_block, _SessionKey, _IV); _Log.DebugFormat("cryptogram_enc: {0}", HexConverter.ConvertToHexString(cryptogram_enc)); - _IV = ExtractLastBlock(cryptogram_enc, 16); + _IV = ByteOperation.ExtractLastBlock(cryptogram_enc, 16); _Log.DebugFormat("_IV: {0}", HexConverter.ConvertToHexString(_IV)); - byte[] data = Concatenate(new byte[] { key_id }, cryptogram_enc); + byte[] data = ByteOperation.Concatenate(new byte[] { key_id }, cryptogram_enc); _Log.DebugFormat("data: {0}", HexConverter.ConvertToHexString(data)); APDUCommand cmd_ChangeKey = new APDUCommand(IsoCase.Case4Short) @@ -775,11 +582,11 @@ namespace NFC.Cards.NXP_MIFARE_DESFire }; _Log.DebugFormat("header: {0}", HexConverter.ConvertToHexString(header)); - byte[] key_xor = XOR(new_key, old_key); + byte[] key_xor = ByteOperation.XOR(new_key, old_key); // AES Key Version is Append to Key - byte[] key_and_version = Concatenate(key_xor, new byte[] { key_version }); - byte[] command = Concatenate(header, key_and_version); + byte[] key_and_version = ByteOperation.Concatenate(key_xor, new byte[] { key_version }); + byte[] command = ByteOperation.Concatenate(header, key_and_version); _Log.DebugFormat("command: {0}", HexConverter.ConvertToHexString(command)); CRC32 crc32 = new CRC32(); @@ -788,21 +595,21 @@ namespace NFC.Cards.NXP_MIFARE_DESFire byte[] crc_key = crc32.Calculate(new_key); _Log.DebugFormat("crc_key: {0}", HexConverter.ConvertToHexString(crc_key)); - byte[] cryptogram = Concatenate(key_and_version, crc_cmd); - cryptogram = Concatenate(cryptogram, crc_key); + byte[] cryptogram = ByteOperation.Concatenate(key_and_version, crc_cmd); + cryptogram = ByteOperation.Concatenate(cryptogram, crc_key); _Log.DebugFormat("cryptogram: {0}", HexConverter.ConvertToHexString(cryptogram)); - byte[] cryptogram_block = ExpandToBlockSize(cryptogram, 16); + byte[] cryptogram_block = ByteOperation.ExpandToBlockSize(cryptogram, 16); _Log.DebugFormat("cryptogram_block: {0}", HexConverter.ConvertToHexString(cryptogram_block)); AES aes = new AES(); byte[] cryptogram_enc = aes.Encrypt(cryptogram_block, _SessionKey, _IV); _Log.DebugFormat("cryptogram_enc: {0}", HexConverter.ConvertToHexString(cryptogram_enc)); - _IV = ExtractLastBlock(cryptogram_enc, 16); + _IV = ByteOperation.ExtractLastBlock(cryptogram_enc, 16); _Log.DebugFormat("_IV: {0}", HexConverter.ConvertToHexString(_IV)); - byte[] data = Concatenate(new byte[] { key_id }, cryptogram_enc); + byte[] data = ByteOperation.Concatenate(new byte[] { key_id }, cryptogram_enc); _Log.DebugFormat("data: {0}", HexConverter.ConvertToHexString(data)); APDUCommand cmd_ChangeKey = new APDUCommand(IsoCase.Case4Short) @@ -854,7 +661,7 @@ namespace NFC.Cards.NXP_MIFARE_DESFire { CLA = 0x90, INS = 0xCD, - Data = Concatenate(data, accessRights_byte, size_byte) + Data = ByteOperation.Concatenate(data, accessRights_byte, size_byte) }; _Log.Debug(cmd_CreateFile_Standard.ToString()); @@ -900,8 +707,7 @@ namespace NFC.Cards.NXP_MIFARE_DESFire offset_byte_tolong[2], }; - long bytes_towrite = 0; - + long bytes_towrite; if (length - bytes_writed < max_write_bytes_pre_transaction) { bytes_towrite = length - bytes_writed; @@ -913,7 +719,7 @@ namespace NFC.Cards.NXP_MIFARE_DESFire byte[] length_byte_tolong = BitConverter.GetBytes(bytes_towrite); - write_buffer = GetSubArray(data, bytes_writed, bytes_towrite); + write_buffer = ByteOperation.GetSubArray(data, bytes_writed, bytes_towrite); bytes_writed += bytes_towrite; // Use only 3 Bytes @@ -928,7 +734,7 @@ namespace NFC.Cards.NXP_MIFARE_DESFire { CLA = 0x90, INS = 0x3D, - Data = Concatenate(file_id_array, offset_byte, length_byte, write_buffer) + Data = ByteOperation.Concatenate(file_id_array, offset_byte, length_byte, write_buffer) }; _Log.Debug(cmd_WriteData.ToString()); @@ -955,7 +761,6 @@ namespace NFC.Cards.NXP_MIFARE_DESFire int max_read_bytes_pre_transaction = 47; long bytes_readed = 0; - byte[] readbuffer = new byte[47]; List read_data = new List(); @@ -975,8 +780,7 @@ namespace NFC.Cards.NXP_MIFARE_DESFire offset_byte_tolong[2], }; - long bytes_toread = 0; - + long bytes_toread; if (length - bytes_readed < max_read_bytes_pre_transaction) { bytes_toread = length - bytes_readed; @@ -1001,7 +805,7 @@ namespace NFC.Cards.NXP_MIFARE_DESFire { CLA = 0x90, INS = 0xBD, - Data = Concatenate(data, offset_byte, length_byte) + Data = ByteOperation.Concatenate(data, offset_byte, length_byte) }; _Log.Debug(cmd_ReadData.ToString()); @@ -1011,7 +815,7 @@ namespace NFC.Cards.NXP_MIFARE_DESFire CheckAPDUResponse(response); // Remove CMAC from Body - read_data.AddRange(GetSubArray(response.Body, 0, bytes_toread)); + read_data.AddRange(ByteOperation.GetSubArray(response.Body, 0, bytes_toread)); } return read_data.ToArray(); diff --git a/NFC/Helper/ByteOperation.cs b/NFC/Helper/ByteOperation.cs new file mode 100644 index 0000000..9f4096b --- /dev/null +++ b/NFC/Helper/ByteOperation.cs @@ -0,0 +1,200 @@ +using System; +using System.Collections.Generic; + +namespace NFC.Helper +{ + public static class ByteOperation + { + /// + /// Generate Byte Array filled with 0 + /// + /// Size of Array + public static byte[] GenerateEmptyArray(uint size) + { + byte[] key = new byte[size]; + for (int i = 0; i < size; i++) + { + key[i] = 0; + } + + return key; + } + + /// + /// Get Range of Array Elements + /// + /// Array + /// Offset in Byte + /// Lenght to read in Byte + /// new Array with Range of Array Elements + public static byte[] GetSubArray(byte[] array, long offset, long length) + { + byte[] subarray = new byte[length]; + for (long i = offset; i < offset + length; i++) + { + subarray[i - offset] = array[i]; + } + return subarray; + } + + /// + /// Return a copy of the last Block of data + /// + /// Data compatible to blocksize + /// in byte + public static 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; + } + + /// + /// Expand Array to Block Size, fill with 0x00 + /// + /// + public static byte[] ExpandToBlockSize(byte[] data, uint bocksize) + { + if (data == null) + { + throw new ArgumentNullException("Data cannot be null."); + } + + 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; + } + + /// + /// Rotates Array to the left + /// + /// Data + /// Copy of data + public static 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; + } + + /// + /// Rotates Array to the right + /// + /// Data + /// Copy of data + public static 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; + } + + /// + /// Concatenates Arrays + /// + /// List of Byte Array + public static byte[] Concatenate(params byte[][] data) + { + if (data == null) + { + throw new ArgumentNullException("Data cannot be null."); + } + + List cat = new List(); + + foreach (byte[] d in data) + { + cat.AddRange(d); + } + + return cat.ToArray(); + } + + /// + /// Boolean Operation XOR on all Bytes + /// + /// Array A + /// Array B + /// Copy of Data + public static 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; + } + } +} diff --git a/NFC/IsoCase.cs b/NFC/IsoCase.cs index caa61de..eca4ef4 100644 --- a/NFC/IsoCase.cs +++ b/NFC/IsoCase.cs @@ -1,10 +1,16 @@ namespace NFC { + /// + /// https://github.com/danm-de/pcsc-sharp/blob/246fc4303190184d6acd98a2d66f48cb7ffd7094/src/PCSC.Iso7816/IsoCase.cs + /// public enum IsoCase { - Case4Short, + Case1, Case2Short, Case3Short, - Case1 + Case4Short, + Case2Extended, + Case3Extended, + Case4Extended } } diff --git a/NFC/SCardProtocol.cs b/NFC/SCardProtocol.cs new file mode 100644 index 0000000..2b789b2 --- /dev/null +++ b/NFC/SCardProtocol.cs @@ -0,0 +1,15 @@ +namespace NFC +{ + /// + /// https://github.com/danm-de/pcsc-sharp/blob/246fc4303190184d6acd98a2d66f48cb7ffd7094/src/PCSC/SCardProtocol.cs + /// + public enum SCardProtocol + { + UNSET, + T0, + T1, + RAW, + T15, + ANY + } +} diff --git a/NFC_PCSC/Card_PCSC.cs b/NFC_PCSC/Card_PCSC.cs index f45ac9a..6514af5 100644 --- a/NFC_PCSC/Card_PCSC.cs +++ b/NFC_PCSC/Card_PCSC.cs @@ -8,8 +8,8 @@ namespace NFC_PCSC { public class Card_PCSC : ICard { - private IsoReader _ISOReader; - private string _ReaderID; + private readonly IsoReader _ISOReader; + private readonly string _ReaderID; public Card_PCSC(IsoReader isoreader, string readerID) { @@ -18,7 +18,7 @@ namespace NFC_PCSC } public void Connect() { - _ISOReader.Connect(_ReaderID, SCardShareMode.Shared, SCardProtocol.Any); + _ISOReader.Connect(_ReaderID, SCardShareMode.Shared, PCSC.SCardProtocol.Any); } public void Disconnect() @@ -28,17 +28,53 @@ namespace NFC_PCSC public APDUResponse Transmit(APDUCommand apdu_cmd) { - Response response = _ISOReader.Transmit(Convert(apdu_cmd)); + Response response = _ISOReader.Transmit(ConvertAPDUCommand(apdu_cmd)); return Convert(response); } - public CommandApdu Convert(APDUCommand apdu_cmd) + public CommandApdu ConvertAPDUCommand(APDUCommand apdu_cmd) { - throw new NotImplementedException(); - return new CommandApdu(ConvertISOCase(apdu_cmd.Case), SCardProtocol.Any) + switch(apdu_cmd.Case) { - - }; + case NFC.IsoCase.Case1: + return new CommandApdu(ConvertISOCase(apdu_cmd.Case), ConvertSCardProtocol(apdu_cmd.Protocol)) + { + CLA = apdu_cmd.CLA, + INS = apdu_cmd.INS, + P1 = apdu_cmd.P1, + P2 = apdu_cmd.P2 + }; + case NFC.IsoCase.Case2Short: + return new CommandApdu(ConvertISOCase(apdu_cmd.Case), ConvertSCardProtocol(apdu_cmd.Protocol)) + { + CLA = apdu_cmd.CLA, + INS = apdu_cmd.INS, + P1 = apdu_cmd.P1, + P2 = apdu_cmd.P2, + Le = apdu_cmd.LE + }; + case NFC.IsoCase.Case3Short: + return new CommandApdu(ConvertISOCase(apdu_cmd.Case), ConvertSCardProtocol(apdu_cmd.Protocol)) + { + CLA = apdu_cmd.CLA, + INS = apdu_cmd.INS, + P1 = apdu_cmd.P1, + P2 = apdu_cmd.P2, + Data = apdu_cmd.Data + }; + case NFC.IsoCase.Case4Short: + return new CommandApdu(ConvertISOCase(apdu_cmd.Case), ConvertSCardProtocol(apdu_cmd.Protocol)) + { + CLA = apdu_cmd.CLA, + INS = apdu_cmd.INS, + P1 = apdu_cmd.P1, + P2 = apdu_cmd.P2, + Data = apdu_cmd.Data, + Le = apdu_cmd.LE + }; + default: + throw new Exception("Unknown IsoCase"); + } } public PCSC.Iso7816.IsoCase ConvertISOCase(NFC.IsoCase isoCase) @@ -58,6 +94,27 @@ namespace NFC_PCSC } } + public PCSC.SCardProtocol ConvertSCardProtocol(NFC.SCardProtocol sCardProtocol) + { + switch (sCardProtocol) + { + case NFC.SCardProtocol.UNSET: + return PCSC.SCardProtocol.Unset; + case NFC.SCardProtocol.T0: + return PCSC.SCardProtocol.T0; + case NFC.SCardProtocol.T1: + return PCSC.SCardProtocol.T1; + case NFC.SCardProtocol.RAW: + return PCSC.SCardProtocol.Raw; + case NFC.SCardProtocol.T15: + return PCSC.SCardProtocol.T15; + case NFC.SCardProtocol.ANY: + return PCSC.SCardProtocol.Any; + default: + throw new NotSupportedException("Unknown SCardProtocol"); + } + } + public APDUResponse Convert(Response response) { ResponseApdu responseApdu = response.Get(0); diff --git a/NFC_PCSC/Hardware_PCSC.cs b/NFC_PCSC/Hardware_PCSC.cs index b5f184d..6588e61 100644 --- a/NFC_PCSC/Hardware_PCSC.cs +++ b/NFC_PCSC/Hardware_PCSC.cs @@ -8,10 +8,8 @@ namespace NFC_PCSC public string[] GetReaders() { var contextFactory = ContextFactory.Instance; - using (var context = contextFactory.Establish(SCardScope.System)) - { - return context.GetReaders(); - } + using var context = contextFactory.Establish(SCardScope.System); + return context.GetReaders(); } public bool IsAvailable() diff --git a/NFC_Test/Cards/MIFARE_DESFire_Test.cs b/NFC_Test/Cards/MIFARE_DESFire_Test.cs index bf4d6fa..8777815 100644 --- a/NFC_Test/Cards/MIFARE_DESFire_Test.cs +++ b/NFC_Test/Cards/MIFARE_DESFire_Test.cs @@ -15,42 +15,6 @@ namespace NFC_Test.Cards public class MIFARE_DESFire_Test { #region Helper Methods - [Test] - public void GenerateEmptyArray() - { - uint i = 16; - - NXP_MIFARE_DESFire desfire = new NXP_MIFARE_DESFire(null); - - byte[] data = desfire.GenerateEmptyArray(i); - - for (int e = 0; e < i; e++) - { - if (data[e] != 0x00) - { - Assert.Fail("Data is not 0x00"); - } - } - } - - [Test] - public void GetSubArray() - { - byte[] array = new byte[] - { - 0x01, 0x02, 0x03, 0x04, 0x05 - }; - - byte[] expected_subarray = new byte[] - { - 0x02, 0x03, 0x04 - }; - - NXP_MIFARE_DESFire desfire = new NXP_MIFARE_DESFire(null); - - Assert.AreEqual(expected_subarray, desfire.GetSubArray(array, 1, 3)); - } - [Test] public void CheckAPDUResponse__NULL() { @@ -338,259 +302,6 @@ namespace NFC_Test.Cards #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 - }; - - NXP_MIFARE_DESFire desfire = new NXP_MIFARE_DESFire(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 - }; - - NXP_MIFARE_DESFire desfire = new NXP_MIFARE_DESFire(null); - - Assert.Throws( - delegate - { - byte[] lastblock = desfire.ExtractLastBlock(data, 7); - }); - } - - [Test] - public void ExtractLastBlock_Null() - { - byte[] data = null; - - NXP_MIFARE_DESFire desfire = new NXP_MIFARE_DESFire(null); - - Assert.Throws( - delegate - { - byte[] lastblock = desfire.ExtractLastBlock(data, 7); - }); - } - - [Test] - public void ExpandToBlockSize() - { - byte[] data = new byte[] - { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 - }; - - byte[] expected_lastblock = new byte[] - { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00 - }; - - NXP_MIFARE_DESFire desfire = new NXP_MIFARE_DESFire(null); - - byte[] lastblock = desfire.ExpandToBlockSize(data, 8); - - Assert.AreEqual(expected_lastblock, lastblock); - } - - [Test] - public void ExpandToBlockSize_Null() - { - byte[] data = null; - - NXP_MIFARE_DESFire desfire = new NXP_MIFARE_DESFire(null); - - Assert.Throws( - delegate - { - byte[] lastblock = desfire.ExpandToBlockSize(data, 8); - }); - } - - [Test] - public void RotateLeft() - { - byte[] data = new byte[] - { - 0x01, 0x02, 0x03, 0x04 - }; - - byte[] expected_data_left = new byte[] - { - 0x02, 0x03, 0x04, 0x01 - }; - - NXP_MIFARE_DESFire desfire = new NXP_MIFARE_DESFire(null); - - byte[] data_left = desfire.RotateLeft(data); - - Assert.AreEqual(expected_data_left, data_left); - } - - [Test] - public void RotateLeft_Null() - { - NXP_MIFARE_DESFire desfire = new NXP_MIFARE_DESFire(null); - - Assert.Throws( - 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 - }; - - NXP_MIFARE_DESFire desfire = new NXP_MIFARE_DESFire(null); - - byte[] data_left = desfire.RotateRight(data); - - Assert.AreEqual(expected_data_left, data_left); - } - - [Test] - public void RotateRight_Null() - { - NXP_MIFARE_DESFire desfire = new NXP_MIFARE_DESFire(null); - - Assert.Throws( - 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 - }; - - NXP_MIFARE_DESFire desfire = new NXP_MIFARE_DESFire(null); - - byte[] data_c = desfire.Concatenate(data_a, data_b); - - Assert.AreEqual(expected_data_c, data_c); - } - - [Test] - public void Concatenate_ABC() - { - byte[] data_a = new byte[] - { - 0x01, 0x02, 0x03, 0x04 - }; - - byte[] data_b = new byte[] - { - 0x05, 0x06, 0x07, 0x08 - }; - - byte[] data_c = new byte[] - { - 0x09, 0xA0, 0xB0, 0xC0 - }; - - byte[] expected_data_d = new byte[] - { - 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0xA0, 0xB0, 0xC0 - }; - - NXP_MIFARE_DESFire desfire = new NXP_MIFARE_DESFire(null); - - byte[] data_d = desfire.Concatenate(data_a, data_b, data_c); - - Assert.AreEqual(expected_data_d, data_d); - } - - [Test] - public void Concatenate_Null() - { - NXP_MIFARE_DESFire desfire = new NXP_MIFARE_DESFire(null); - - Assert.Throws( - 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 - }; - - NXP_MIFARE_DESFire desfire = new NXP_MIFARE_DESFire(null); - - byte[] data_c = desfire.XOR(data_a, data_b); - - Assert.AreEqual(expected_data_c, data_c); - } - - [Test] - public void XOR_null() - { - NXP_MIFARE_DESFire desfire = new NXP_MIFARE_DESFire(null); - - Assert.Throws( - delegate - { - byte[] lastblock = desfire.XOR(null, null); - }); - } - [Test] public void GenerateSessionKey_DES() { diff --git a/NFC_Test/Helper/ByteOperation_Test.cs b/NFC_Test/Helper/ByteOperation_Test.cs new file mode 100644 index 0000000..9a55b16 --- /dev/null +++ b/NFC_Test/Helper/ByteOperation_Test.cs @@ -0,0 +1,266 @@ +using NFC.Helper; +using NUnit.Framework; +using System; + +namespace NFC_Test.Helper +{ + public class ByteOperation_Test + { + [Test] + public void GenerateEmptyArray() + { + uint i = 16; + + byte[] data = ByteOperation.GenerateEmptyArray(i); + + for (int e = 0; e < i; e++) + { + if (data[e] != 0x00) + { + Assert.Fail("Data is not 0x00"); + } + } + } + + [Test] + public void GetSubArray() + { + byte[] array = new byte[] + { + 0x01, 0x02, 0x03, 0x04, 0x05 + }; + + byte[] expected_subarray = new byte[] + { + 0x02, 0x03, 0x04 + }; + + Assert.AreEqual(expected_subarray, ByteOperation.GetSubArray(array, 1, 3)); + } + + [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 + }; + + byte[] lastblock = ByteOperation.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 + }; + + Assert.Throws( + delegate + { + byte[] lastblock = ByteOperation.ExtractLastBlock(data, 7); + }); + } + + [Test] + public void ExtractLastBlock_Null() + { + byte[] data = null; + + Assert.Throws( + delegate + { + byte[] lastblock = ByteOperation.ExtractLastBlock(data, 7); + }); + } + + [Test] + public void ExpandToBlockSize() + { + byte[] data = new byte[] + { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 + }; + + byte[] expected_lastblock = new byte[] + { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00 + }; + + byte[] lastblock = ByteOperation.ExpandToBlockSize(data, 8); + + Assert.AreEqual(expected_lastblock, lastblock); + } + + [Test] + public void ExpandToBlockSize_Null() + { + byte[] data = null; + + Assert.Throws( + delegate + { + byte[] lastblock = ByteOperation.ExpandToBlockSize(data, 8); + }); + } + + [Test] + public void RotateLeft() + { + byte[] data = new byte[] + { + 0x01, 0x02, 0x03, 0x04 + }; + + byte[] expected_data_left = new byte[] + { + 0x02, 0x03, 0x04, 0x01 + }; + + byte[] data_left = ByteOperation.RotateLeft(data); + + Assert.AreEqual(expected_data_left, data_left); + } + + [Test] + public void RotateLeft_Null() + { + Assert.Throws( + delegate + { + byte[] lastblock = ByteOperation.RotateLeft(null); + }); + } + + [Test] + public void RotateRight() + { + byte[] data = new byte[] + { + 0x01, 0x02, 0x03, 0x04 + }; + + byte[] expected_data_left = new byte[] + { + 0x04, 0x01, 0x02, 0x03 + }; + + byte[] data_left = ByteOperation.RotateRight(data); + + Assert.AreEqual(expected_data_left, data_left); + } + + [Test] + public void RotateRight_Null() + { + Assert.Throws( + delegate + { + byte[] lastblock = ByteOperation.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 + }; + + byte[] data_c = ByteOperation.Concatenate(data_a, data_b); + + Assert.AreEqual(expected_data_c, data_c); + } + + [Test] + public void Concatenate_ABC() + { + byte[] data_a = new byte[] + { + 0x01, 0x02, 0x03, 0x04 + }; + + byte[] data_b = new byte[] + { + 0x05, 0x06, 0x07, 0x08 + }; + + byte[] data_c = new byte[] + { + 0x09, 0xA0, 0xB0, 0xC0 + }; + + byte[] expected_data_d = new byte[] + { + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0xA0, 0xB0, 0xC0 + }; + + byte[] data_d = ByteOperation.Concatenate(data_a, data_b, data_c); + + Assert.AreEqual(expected_data_d, data_d); + } + + [Test] + public void Concatenate_Null() + { + Assert.Throws( + delegate + { + byte[] lastblock = ByteOperation.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 + }; + + byte[] data_c = ByteOperation.XOR(data_a, data_b); + + Assert.AreEqual(expected_data_c, data_c); + } + + [Test] + public void XOR_null() + { + Assert.Throws( + delegate + { + byte[] lastblock = ByteOperation.XOR(null, null); + }); + } + } +} diff --git a/NFC_Test/NFC_Test.csproj b/NFC_Test/NFC_Test.csproj index 78f0b6d..807f203 100644 --- a/NFC_Test/NFC_Test.csproj +++ b/NFC_Test/NFC_Test.csproj @@ -5,6 +5,7 @@ +