using NFC;
using NFC.Mifare_DESFire.Enums;
using NFC.ISO7816_4;
using NFC.Mifare_DESFire;
using NFC.NXP_MIFARE_DESFire.Exceptions;
using NSubstitute;
using NUnit.Framework;
using System;
using System.Text;

namespace NFC_Unit_Test.NXP_MIFARE_DESFire
{
    [TestFixture]
    public class MIFARE_DESFire_Test
    {
        #region Helper Methods
        [Test]
        public void GenerateEmptyArray()
        {
            uint i = 16;

            MIFARE_DESFire desfire = new 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
            };

            MIFARE_DESFire desfire = new MIFARE_DESFire(null);

            Assert.AreEqual(expected_subarray, desfire.GetSubArray(array, 1, 3));
        }

        [Test]
        public void CheckAPDUResponse__NULL()
        {
            MIFARE_DESFire desfire = new MIFARE_DESFire(null);
            Assert.Throws<ArgumentNullException>(
            delegate
            {
                desfire.CheckAPDUResponse(null);
            });
        }

        [Test]
        public void CheckAPDUResponse__UNKNOWN()
        {
            APDUResponse response = new APDUResponse()
            {
                SW1 = 0x00,
                SW2 = 0x00
            };

            MIFARE_DESFire desfire = new MIFARE_DESFire(null);
            Assert.Throws<Exception>(
            delegate
            {
                desfire.CheckAPDUResponse(response);
            });
        }

        [Test]
        public void CheckAPDUResponse__OPERATION_OK()
        {
            APDUResponse response = new APDUResponse()
            {
                SW1 = 0x91,
                SW2 = 0x00
            };

            MIFARE_DESFire desfire = new MIFARE_DESFire(null);
            
            desfire.CheckAPDUResponse(response);
        }

        [Test]
        public void CheckAPDUResponse__NO_CHANGES()
        {
            APDUResponse response = new APDUResponse()
            {
                SW1 = 0x91,
                SW2 = 0x0C
            };

            MIFARE_DESFire desfire = new MIFARE_DESFire(null);
            desfire.CheckAPDUResponse(response);
        }

        [Test]
        public void CheckAPDUResponse__ILLEGAL_COMMAND_CODE()
        {
            APDUResponse response = new APDUResponse()
            {
                SW1 = 0x91,
                SW2 = 0x1C
            };

            MIFARE_DESFire desfire = new MIFARE_DESFire(null);

            Assert.Throws<IllegalCommandCodeException>(
            delegate 
            {
                desfire.CheckAPDUResponse(response);
            });
        }

        [Test]
        public void CheckAPDUResponse__INTEGRITY_ERROR()
        {
            APDUResponse response = new APDUResponse()
            {
                SW1 = 0x91,
                SW2 = 0x1E
            };

            MIFARE_DESFire desfire = new MIFARE_DESFire(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 desfire = new MIFARE_DESFire(null);

            Assert.Throws<NoSuchKeyException>(
            delegate
            {
                desfire.CheckAPDUResponse(response);
            });
        }

        [Test]
        public void CheckAPDUResponse__LENGTH_ERROR()
        {
            APDUResponse response = new APDUResponse()
            {
                SW1 = 0x91,
                SW2 = 0x7E
            };

            MIFARE_DESFire desfire = new MIFARE_DESFire(null);

            Assert.Throws<LengthErrorException>(
            delegate
            {
                desfire.CheckAPDUResponse(response);
            });
        }

        [Test]
        public void CheckAPDUResponse__PERMISSION_DENIED()
        {
            APDUResponse response = new APDUResponse()
            {
                SW1 = 0x91,
                SW2 = 0x9D
            };

            MIFARE_DESFire desfire = new MIFARE_DESFire(null);

            Assert.Throws<PermissionDeniedException>(
            delegate
            {
                desfire.CheckAPDUResponse(response);
            });
        }

        [Test]
        public void CheckAPDUResponse__PARAMETER_ERROR()
        {
            APDUResponse response = new APDUResponse()
            {
                SW1 = 0x91,
                SW2 = 0x9E
            };

            MIFARE_DESFire desfire = new MIFARE_DESFire(null);

            Assert.Throws<ParameterErrorException>(
            delegate
            {
                desfire.CheckAPDUResponse(response);
            });
        }

        [Test]
        public void CheckAPDUResponse__AUTHENTICATION_DELAY()
        {
            APDUResponse response = new APDUResponse()
            {
                SW1 = 0x91,
                SW2 = 0xAD
            };

            MIFARE_DESFire desfire = new MIFARE_DESFire(null);

            Assert.Throws<AuthenticationDelayException>(
            delegate
            {
                desfire.CheckAPDUResponse(response);
            });
        }

        [Test]
        public void CheckAPDUResponse__AUTHENTICATION_ERROR()
        {
            APDUResponse response = new APDUResponse()
            {
                SW1 = 0x91,
                SW2 = 0xAE
            };

            MIFARE_DESFire desfire = new MIFARE_DESFire(null);

            Assert.Throws<AuthenticationErrorException>(
            delegate
            {
                desfire.CheckAPDUResponse(response);
            });
        }

        [Test]
        public void CheckAPDUResponse__ADDITIONAL_FRAME()
        {
            APDUResponse response = new APDUResponse()
            {
                SW1 = 0x91,
                SW2 = 0xAF
            };

            MIFARE_DESFire desfire = new MIFARE_DESFire(null);

            desfire.CheckAPDUResponse(response);
        }

        [Test]
        public void CheckAPDUResponse__BOUNDARY_ERROR()
        {
            APDUResponse response = new APDUResponse()
            {
                SW1 = 0x91,
                SW2 = 0xBE
            };

            MIFARE_DESFire desfire = new MIFARE_DESFire(null);

            Assert.Throws<BoundaryErrorException>(
            delegate
            {
                desfire.CheckAPDUResponse(response);
            });
        }

        [Test]
        public void CheckAPDUResponse__COMMAND_ABORTED()
        {
            APDUResponse response = new APDUResponse()
            {
                SW1 = 0x91,
                SW2 = 0xCA
            };

            MIFARE_DESFire desfire = new MIFARE_DESFire(null);

            Assert.Throws<CommandAbortedException>(
            delegate
            {
                desfire.CheckAPDUResponse(response);
            });
        }

        [Test]
        public void CheckAPDUResponse__DUPLICATE_ERROR()
        {
            APDUResponse response = new APDUResponse()
            {
                SW1 = 0x91,
                SW2 = 0xDE
            };

            MIFARE_DESFire desfire = new MIFARE_DESFire(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 desfire = new MIFARE_DESFire(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 desfire = new 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
            };

            MIFARE_DESFire desfire = new MIFARE_DESFire(null);

            Assert.Throws<ArgumentException>(
            delegate
            {
                byte[] lastblock = desfire.ExtractLastBlock(data, 7);
            });
        }

        [Test]
        public void ExtractLastBlock_Null()
        {
            byte[] data = null;

            MIFARE_DESFire desfire = new MIFARE_DESFire(null);

            Assert.Throws<ArgumentNullException>(
            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
            };

            MIFARE_DESFire desfire = new MIFARE_DESFire(null);

            byte[] lastblock = desfire.ExpandToBlockSize(data, 8);

            Assert.AreEqual(expected_lastblock, lastblock);
        }

        [Test]
        public void ExpandToBlockSize_Null()
        {
            byte[] data = null;

            MIFARE_DESFire desfire = new MIFARE_DESFire(null);

            Assert.Throws<ArgumentNullException>(
            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
            };

            MIFARE_DESFire desfire = new MIFARE_DESFire(null);

            byte[] data_left = desfire.RotateLeft(data);

            Assert.AreEqual(expected_data_left, data_left);
        }

        [Test]
        public void RotateLeft_Null()
        {
            MIFARE_DESFire desfire = new MIFARE_DESFire(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 desfire = new MIFARE_DESFire(null);

            byte[] data_left = desfire.RotateRight(data);

            Assert.AreEqual(expected_data_left, data_left);
        }

        [Test]
        public void RotateRight_Null()
        {
            MIFARE_DESFire desfire = new MIFARE_DESFire(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 desfire = new 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
            };

            MIFARE_DESFire desfire = new 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()
        {
            MIFARE_DESFire desfire = new MIFARE_DESFire(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 desfire = new MIFARE_DESFire(null);

            byte[] data_c = desfire.XOR(data_a, data_b);

            Assert.AreEqual(expected_data_c, data_c);
        }

        [Test]
        public void XOR_null()
        {
            MIFARE_DESFire desfire = new MIFARE_DESFire(null);

            Assert.Throws<ArgumentNullException>(
            delegate
            {
                byte[] lastblock = desfire.XOR(null, null);
            });
        }

        [Test]
        public void GenerateSessionKey_DES()
        {
            MIFARE_DESFire desfire = new MIFARE_DESFire(null);

            byte[] rndA = HexConverter.ConvertFromHexString("a541a9dc9138df07");
            Console.WriteLine(HexConverter.ConvertToHexString(rndA));
            byte[] rndB = HexConverter.ConvertFromHexString("cbe55aa893b2da25");
            Console.WriteLine(HexConverter.ConvertToHexString(rndB));

            byte[] expected_sessionkey = HexConverter.ConvertFromHexString("a541a9dccbe55aa8a541a9dccbe55aa8");
            Console.WriteLine(HexConverter.ConvertToHexString(expected_sessionkey));

            byte[] sessionkey = desfire.GenerateSesionKey_DES(rndA, rndB);
            Console.WriteLine(HexConverter.ConvertToHexString(sessionkey));
            Assert.AreEqual(expected_sessionkey, sessionkey);
        }

        [Test]
        public void GenerateSessionKey_AES()
        {
            MIFARE_DESFire desfire = new MIFARE_DESFire(null);

            byte[] rndA = HexConverter.ConvertFromHexString("bc14dfde20074617e45a8822f06fdd91");
            Console.WriteLine(HexConverter.ConvertToHexString(rndA));
            byte[] rndB = HexConverter.ConvertFromHexString("482ddc54426e6dee560413b8d95471f5");
            Console.WriteLine(HexConverter.ConvertToHexString(rndB));

            byte[] expected_sessionkey = HexConverter.ConvertFromHexString("bc14dfde482ddc54f06fdd91d95471f5");
            Console.WriteLine(HexConverter.ConvertToHexString(expected_sessionkey));

            byte[] sessionkey = desfire.GenerateSesionKey_AES(rndA, rndB);
            Console.WriteLine(HexConverter.ConvertToHexString(sessionkey));
            Assert.AreEqual(expected_sessionkey, sessionkey);
        }
        #endregion

        #region Configuration Generator
        [Test]
        public void GenerateKeySetting1()
        {
            MIFARE_DESFire desfire = new MIFARE_DESFire(null);

            Assert.AreEqual(0x0B, desfire.GenerateKeySetting1(ChangeApplicationKey.MASTERKEY, ChangeMasterKeySettings.WITHMASTERKEY, CreateDeleteFile.ONLYMASTERKEY, FileDirectoryAccess.NOKEY, ChangeMasterKey.CHANGEABLE));
        }

        [Test]
        public void GenerateKeySetting1_ChangeKey()
        {
            MIFARE_DESFire desfire = new MIFARE_DESFire(null);
            Assert.AreEqual(0x1B, desfire.GenerateKeySetting1(0x01, ChangeMasterKeySettings.WITHMASTERKEY, CreateDeleteFile.ONLYMASTERKEY, FileDirectoryAccess.NOKEY, ChangeMasterKey.CHANGEABLE));
        }

        [Test]
        public void GenerateKeySetting1_Wrong_KeyID()
        {
            MIFARE_DESFire desfire = new MIFARE_DESFire(null);

            Assert.Throws<ArgumentOutOfRangeException>(
            delegate
            {
                desfire.GenerateKeySetting1(0x10, ChangeMasterKeySettings.WITHMASTERKEY, CreateDeleteFile.ONLYMASTERKEY, FileDirectoryAccess.NOKEY, ChangeMasterKey.CHANGEABLE);
            });
        }

        [Test]
        public void GenerateKeySetting2()
        {
            MIFARE_DESFire desfire = new MIFARE_DESFire(null);

            Assert.AreEqual(0x82, desfire.GenerateKeySetting2(CryptoOperationsType.AES, FileIdentifies.NOTUSED, 0x02));
        }

        [Test]
        public void GenerateKeySetting2_Wrong_KeyNumbers()
        {
            MIFARE_DESFire desfire = new MIFARE_DESFire(null);

            Assert.Throws<ArgumentOutOfRangeException>(
            delegate
            {
                desfire.GenerateKeySetting2(CryptoOperationsType.AES, FileIdentifies.NOTUSED, 0x10);
            });
        }


        [Test]
        public void GenerateFileAccessRights()
        {
            MIFARE_DESFire desfire = new MIFARE_DESFire(null);

            Assert.AreEqual(0x1234, desfire.GenerateFileAccessRights(0x01, 0x02, 0x03, 0x04));
        }

        [Test]
        public void GenerateFileAccessRights_OutOfRange()
        {
            MIFARE_DESFire desfire = new MIFARE_DESFire(null);

            Assert.Throws<ArgumentOutOfRangeException>(
            delegate
            {
                desfire.GenerateFileAccessRights(0x10, 0x00, 0x00, 0x00);
            });
        }
        #endregion

        #region DESFire Commands
        [Test]
        public void SelectApplication()
        {
            ICard card = Substitute.For<ICard>();

            MIFARE_DESFire desfire = new MIFARE_DESFire(card);

            APDUResponse response = new APDUResponse()
            {
                SW1 = 0x91,
                SW2 = 0x00
            };

            card.Transmit(Arg.Is<APDUCommand>(x => HexConverter.ConvertToHexString(x.ToArray()) == "905a00000333221100")).Returns(response);

            desfire.SelectApplication(0x112233);
        }

        [Test]
        public void SelectApplication_InvalidAID()
        {
            ICard card = Substitute.For<ICard>();

            MIFARE_DESFire desfire = new MIFARE_DESFire(null);

            Assert.Throws<ArgumentOutOfRangeException>(
            delegate
            {
                desfire.SelectApplication(0xFF000000);
            });
        }

        [Test]
        public void AuthenticateISO_DES()
        {
            ICard card = Substitute.For<ICard>();

            MIFARE_DESFire desfire = new MIFARE_DESFire(card);

            APDUResponse response_challenge_request = new APDUResponse()
            {
                SW1 = 0x91,
                SW2 = 0xAF,
                Body = HexConverter.ConvertFromHexString("2bf9a938ecca02e2")
            };

            APDUResponse response_challenge_response = new APDUResponse()
            {
                SW1 = 0x91,
                SW2 = 0x00,
                Body = HexConverter.ConvertFromHexString("07d825607a552e2e")
            };

            byte[] rndA = HexConverter.ConvertFromHexString("5f7d1dd12d979173");
            byte[] key = HexConverter.ConvertFromHexString("00000000000000000000000000000000");

            card.Transmit(Arg.Is<APDUCommand>(x => HexConverter.ConvertToHexString(x.ToArray()) == "901a0000010000")).Returns(response_challenge_request);
            card.Transmit(Arg.Is<APDUCommand>(x => HexConverter.ConvertToHexString(x.ToArray()) == "90af000010f8cdb2eaa42a3167dfcb53852ce267fd00")).Returns(response_challenge_response);

            desfire.AuthenticateISO_DES(0x00, key, rndA);

            byte[] expected_sessionkey = HexConverter.ConvertFromHexString("5f7d1dd1f449db5c5f7d1dd1f449db5c");
            byte[] expected_iv = HexConverter.ConvertFromHexString("0000000000000000");

            Assert.AreEqual(expected_sessionkey, desfire._SessionKey);
            Assert.AreEqual(expected_iv, desfire._IV);
        }

        [Test]
        public void AuthenticateISO_DES_InvalidKeyNo()
        {
            MIFARE_DESFire desfire = new MIFARE_DESFire(null);

            Assert.Throws<ArgumentOutOfRangeException>(
            delegate
            {
                desfire.AuthenticateISO_DES(0x0F, null);
            });
        }

        [Test]
        public void Format()
        {
            ICard card = Substitute.For<ICard>();

            MIFARE_DESFire desfire = new MIFARE_DESFire(card);

            APDUResponse response = new APDUResponse()
            {
                SW1 = 0x91,
                SW2 = 0x00
            };

            card.Transmit(Arg.Is<APDUCommand>(x => HexConverter.ConvertToHexString(x.ToArray()) == "90fc000000")).Returns(response);

            desfire.Format();
        }

        [Test]
        public void CreateApplication()
        {
            ICard card = Substitute.For<ICard>();

            MIFARE_DESFire desfire = new MIFARE_DESFire(card);

            APDUResponse response = new APDUResponse()
            {
                SW1 = 0x91,
                SW2 = 0x00
            };

            card.Transmit(Arg.Is<APDUCommand>(x => HexConverter.ConvertToHexString(x.ToArray()) == "90ca000005eeffaa0b8200")).Returns(response);

            desfire.CreateApplication(0xAAFFEE, 0x0b, 0x82);
        }

        [Test]
        public void CreateApplication_InvalidAID()
        {
            ICard card = Substitute.For<ICard>();

            MIFARE_DESFire desfire = new MIFARE_DESFire(null);

            Assert.Throws<ArgumentOutOfRangeException>(
            delegate
            {
                desfire.CreateApplication(0xFF000000, 0x00, 0x00);
            });
        }

        [Test]
        public void AuthenticateISO_AES()
        {
            ICard card = Substitute.For<ICard>();

            MIFARE_DESFire desfire = new MIFARE_DESFire(card);

            APDUResponse response_challenge_request = new APDUResponse()
            {
                SW1 = 0x91,
                SW2 = 0xAF,
                Body = HexConverter.ConvertFromHexString("a33856932308775cf464610c2b17a558")
            };

            APDUResponse response_challenge_response = new APDUResponse()
            {
                SW1 = 0x91,
                SW2 = 0x00,
                Body = HexConverter.ConvertFromHexString("8fdc476f6bac44fe9150e285abd68d48")
            };

            byte[] rndA = HexConverter.ConvertFromHexString("2176770e7a6eb4bef00d5e4b201d1e57");
            byte[] key = HexConverter.ConvertFromHexString("00000000000000000000000000000000");

            card.Transmit(Arg.Is<APDUCommand>(x => HexConverter.ConvertToHexString(x.ToArray()) == "90aa0000010000")).Returns(response_challenge_request);
            card.Transmit(Arg.Is<APDUCommand>(x => HexConverter.ConvertToHexString(x.ToArray()) == "90af000020cbe9726faf54bc76b2055d0b9700e7dc97ecad5627f1d1702a16e8408d2a0ada00")).Returns(response_challenge_response);

            desfire.AuthenticateISO_AES(0x00, key, rndA);

            byte[] expected_sessionkey = HexConverter.ConvertFromHexString("2176770e11c512ca201d1e57fde6e15a");
            byte[] expected_iv = HexConverter.ConvertFromHexString("00000000000000000000000000000000");

            Assert.AreEqual(expected_sessionkey, desfire._SessionKey);
            Assert.AreEqual(expected_iv, desfire._IV);
        }

        [Test]
        public void AuthenticateISO_AES_InvalidKeyNo()
        {
            MIFARE_DESFire desfire = new MIFARE_DESFire(null);

            Assert.Throws<ArgumentOutOfRangeException>(
            delegate
            {
                desfire.AuthenticateISO_AES(0x0F, null);
            });
        }

        [Test]
        public void ChangeKey_AES()
        {
            ICard card = Substitute.For<ICard>();

            MIFARE_DESFire desfire = new MIFARE_DESFire(card);

            APDUResponse response = new APDUResponse()
            {
                SW1 = 0x91,
                SW2 = 0x00
            };

            card.Transmit(Arg.Is<APDUCommand>(x => HexConverter.ConvertToHexString(x.ToArray()) == "90c400002100c2b54a718d0251845653199909bb32e8e38bd6719e8dc21799c29c922a0984fc00")).Returns(response);

            byte[] new_key = HexConverter.ConvertFromHexString("25432a462d4a614e645267556b587032");

            byte[] sessionkey = HexConverter.ConvertFromHexString("a8514dd0350f3dfbc86e80744bcc9b57");
            byte[] iv = HexConverter.ConvertFromHexString("00000000000000000000000000000000");

            desfire._SessionKey = sessionkey;
            desfire._IV = iv;

            desfire.ChangeKey_AES(0x00, new_key, 0x10);
        }

        [Test]
        public void ChangeKey_AES_InvalidKeyNo()
        {
            MIFARE_DESFire desfire = new MIFARE_DESFire(null);

            Assert.Throws<ArgumentOutOfRangeException>(
            delegate
            {
                desfire.ChangeKey_AES(0x0F, null, 0x10);
            });
        }

        [Test]
        public void ChangeOtherKey_AES()
        {
            ICard card = Substitute.For<ICard>();

            MIFARE_DESFire desfire = new MIFARE_DESFire(card);

            APDUResponse response = new APDUResponse()
            {
                SW1 = 0x91,
                SW2 = 0x00
            };

            card.Transmit(Arg.Is<APDUCommand>(x => HexConverter.ConvertToHexString(x.ToArray()) == "90c400002101a8c5a61a06f56f38dc91266fed2e87dc00a5ad72a634ff0e62c8d6d80707dd6000")).Returns(response);

            byte[] new_key = HexConverter.ConvertFromHexString("25432a462d4a614e645267556b587032");
            byte[] old_key = HexConverter.ConvertFromHexString("00000000000000000000000000000000");

            byte[] sessionkey = HexConverter.ConvertFromHexString("1677623e1e158a62dc3d128db55f947d");
            byte[] iv = HexConverter.ConvertFromHexString("00000000000000000000000000000000");

            desfire._SessionKey = sessionkey;
            desfire._IV = iv;

            desfire.ChangeOtherKey_AES(0x01, new_key, old_key, 0x10);
        }

        [Test]
        public void ChangeOtherKey_AES_InvalidKeyNo()
        {
            MIFARE_DESFire desfire = new MIFARE_DESFire(null);

            Assert.Throws<ArgumentOutOfRangeException>(
            delegate
            {
                desfire.ChangeKey_AES(0x0F, null, 0x10);
            });
        }

        [Test]
        public void CreateFile_Standard()
        {
            ICard card = Substitute.For<ICard>();

            MIFARE_DESFire desfire = new MIFARE_DESFire(card);

            APDUResponse response = new APDUResponse()
            {
                SW1 = 0x91,
                SW2 = 0x00
            };

            card.Transmit(Arg.Is<APDUCommand>(x => HexConverter.ConvertToHexString(x.ToArray()) == "90cd000007010000e0f0000000")).Returns(response);

            UInt16 accesRights = desfire.GenerateFileAccessRights((byte)FileAccessRights.FREE, 0x00, 0x00, 0x00);
            desfire.CreateFile_Standard(0x01, FileCommunication.PLAIN, accesRights, 0xF0);
        }

        [Test]
        public void CreateFile_Standard_InvalidFID()
        {
            ICard card = Substitute.For<ICard>();

            MIFARE_DESFire desfire = new MIFARE_DESFire(card);

            Assert.Throws<ArgumentOutOfRangeException>(
            delegate
            {
                desfire.CreateFile_Standard(0x21, FileCommunication.PLAIN, 0x0000, 0xF0);
            });
        }

        [Test]
        public void WriteData()
        {
            ICard card = Substitute.For<ICard>();

            MIFARE_DESFire desfire = new MIFARE_DESFire(card);

            APDUResponse response = new APDUResponse()
            {
                SW1 = 0x91,
                SW2 = 0x00
            };

            card.Transmit(Arg.Is<APDUCommand>(x => HexConverter.ConvertToHexString(x.ToArray()) == "903d00000f01000000080000546573743132333400")).Returns(response);

            desfire.WriteData(0x01, 0, Encoding.ASCII.GetBytes("Test1234"));
        }

        [Test]
        public void WriteData_Long()
        {
            ICard card = Substitute.For<ICard>();

            MIFARE_DESFire desfire = new MIFARE_DESFire(card);

            APDUResponse response = new APDUResponse()
            {
                SW1 = 0x91,
                SW2 = 0x00
            };

            card.Transmit(Arg.Is<APDUCommand>(x => HexConverter.ConvertToHexString(x.ToArray()) == "903d000036010000002f0000546573743132333454657374313233345465737431323334546573743132333454657374313233345465737431323300")).Returns(response);
            card.Transmit(Arg.Is<APDUCommand>(x => HexConverter.ConvertToHexString(x.ToArray()) == "903d000036012f00002f0000345465737431323334546573743132333454657374313233345465737431323334546573743132333454657374313200")).Returns(response);
            card.Transmit(Arg.Is<APDUCommand>(x => HexConverter.ConvertToHexString(x.ToArray()) == "903d000019015e000012000033345465737431323334546573743132333400")).Returns(response);

            desfire.WriteData(0x01, 0, Encoding.ASCII.GetBytes("Test1234Test1234Test1234Test1234Test1234Test1234Test1234Test1234Test1234Test1234Test1234Test1234Test1234Test1234"));
        }

        [Test]
        public void WriteData_InvalidFileID()
        {
            ICard card = Substitute.For<ICard>();

            MIFARE_DESFire desfire = new MIFARE_DESFire(card);

            Assert.Throws<ArgumentOutOfRangeException>(
            delegate
            {
                desfire.WriteData(0x21, 0x00, Encoding.ASCII.GetBytes("Test1234"));
            });
        }

        [Test]
        public void ReadData()
        {
            ICard card = Substitute.For<ICard>();

            MIFARE_DESFire desfire = new MIFARE_DESFire(card);

            APDUResponse response = new APDUResponse()
            {
                SW1 = 0x91,
                SW2 = 0x00,
                Body = HexConverter.ConvertFromHexString("54657374313233340000000000000000000000000000000000000000000000009100")
            };

            card.Transmit(Arg.Is<APDUCommand>(x => HexConverter.ConvertToHexString(x.ToArray()) == "90bd0000070100000020000000")).Returns(response);

            byte[] data = desfire.ReadData(0x01, 0x00, 0x20);

            Assert.AreEqual("Test1234", Encoding.ASCII.GetString(data).Replace("\u0000", ""));
        }

        [Test]
        public void ReadData_CMAC()
        {
            ICard card = Substitute.For<ICard>();

            MIFARE_DESFire desfire = new MIFARE_DESFire(card);

            APDUResponse response = new APDUResponse()
            {
                SW1 = 0x91,
                SW2 = 0x00,
                Body = HexConverter.ConvertFromHexString("5465737431323334000000000000000000000000000000000000000000000000809a9bedbc559a5b9100")
            };

            card.Transmit(Arg.Is<APDUCommand>(x => HexConverter.ConvertToHexString(x.ToArray()) == "90bd0000070100000020000000")).Returns(response);

            byte[] data = desfire.ReadData(0x01, 0x00, 0x20);

            Assert.AreEqual("Test1234", Encoding.ASCII.GetString(data).Replace("\u0000", ""));
        }

        [Test]
        public void ReadData_Long()
        {
            ICard card = Substitute.For<ICard>();

            MIFARE_DESFire desfire = new MIFARE_DESFire(card);

            APDUResponse response_1 = new APDUResponse()
            {
                SW1 = 0x91,
                SW2 = 0x00,
                Body = HexConverter.ConvertFromHexString("54657374313233340000000000000000000000000000000000000000000000000000000000000000000000000000009100")
            };

            APDUResponse response_2 = new APDUResponse()
            {
                SW1 = 0x91,
                SW2 = 0x00,
                Body = HexConverter.ConvertFromHexString("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009100")
            };

            APDUResponse response_3 = new APDUResponse()
            {
                SW1 = 0x91,
                SW2 = 0x00,
                Body = HexConverter.ConvertFromHexString("00009100")
            };

            card.Transmit(Arg.Is<APDUCommand>(x => HexConverter.ConvertToHexString(x.ToArray()) == "90bd000007010000002f000000")).Returns(response_1);
            card.Transmit(Arg.Is<APDUCommand>(x => HexConverter.ConvertToHexString(x.ToArray()) == "90bd000007012f00002f000000")).Returns(response_2);
            card.Transmit(Arg.Is<APDUCommand>(x => HexConverter.ConvertToHexString(x.ToArray()) == "90bd000007015e000002000000")).Returns(response_3);

            byte[] data = desfire.ReadData(0x01, 0x00, 0x60);

            Assert.AreEqual("Test1234", Encoding.ASCII.GetString(data).Replace("\u0000", ""));
        }

        [Test]
        public void ReadData_InvalidFileID()
        {
            ICard card = Substitute.For<ICard>();

            MIFARE_DESFire desfire = new MIFARE_DESFire(card);

            Assert.Throws<ArgumentOutOfRangeException>(
            delegate
            {
                desfire.ReadData(0x21, 0x00, 0x20);
            });
        }
        #endregion
    }
}