using System;

using NUnit.Framework;

using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Generators;
using Org.BouncyCastle.Crypto.Macs;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Utilities;
using Org.BouncyCastle.Utilities.Encoders;
using Org.BouncyCastle.Utilities.Test;
using Org.BouncyCastle.Security;

namespace Org.BouncyCastle.Crypto.Tests
{
	[TestFixture]
	public class Poly1305Test
		: SimpleTest
	{
		private const int MAXLEN = 1000;

        private class TestCase
		{
			internal byte[] key;
			internal byte[] nonce;
			internal byte[] message;
			internal byte[] expectedMac;

			public TestCase(string key, string nonce, string message, string expectedMac)
			{
				this.key = Hex.Decode(key);
				// nacl test case keys are not pre-Clamped
				Poly1305KeyGenerator.Clamp(this.key);
				this.nonce = (nonce == null) ? null : Hex.Decode(nonce);
				this.message = Hex.Decode(message);
				this.expectedMac = Hex.Decode(expectedMac);
			}
		}

        private static TestCase[] CASES = {
            // Raw Poly1305
            // onetimeauth.c from nacl-20110221
            new TestCase("eea6a7251c1e72916d11c2cb214d3c25" + "2539121d8e234e652d651fa4c8cff880", null,
                "8e993b9f48681273c29650ba32fc76ce48332ea7164d96a4476fb8c531a1186a"
                    + "c0dfc17c98dce87b4da7f011ec48c97271d2c20f9b928fe2270d6fb863d51738"
                    + "b48eeee314a7cc8ab932164548e526ae90224368517acfeabd6bb3732bc0e9da"
                    + "99832b61ca01b6de56244a9e88d5f9b37973f622a43d14a6599b1f654cb45a74e355a5",
                "f3ffc7703f9400e52a7dfb4b3d3305d9"),
            // Poly1305-AES
            // Loop 1 of test-poly1305aes from poly1305aes-20050218
            new TestCase("0000000000000000000000000000000000000000000000000000000000000000",
                "00000000000000000000000000000000", "", "66e94bd4ef8a2c3b884cfa59ca342b2e"),
            new TestCase("f795bd0a50e29e0710d3130a20e98d0c" + "f795bd4a52e29ed713d313fa20e98dbc",
                "917cf69ebd68b2ec9b9fe9a3eadda692", "66f7", "5ca585c75e8f8f025e710cabc9a1508b"),
            new TestCase("3ef49901c8e11c000430d90ad45e7603" + "e69dae0aab9f91c03a325dcc9436fa90",
                "166450152e2394835606a9d1dd2cdc8b", "66f75c0e0c7a406586", "2924f51b9c2eff5df09db61dd03a9ca1"),
            new TestCase("da4afc035087d90e503f8f0ea08c3e0d" + "85a4ea91a7de0b0d96eed0d4bf6ecf1c",
                "0b6ef7a0b8f8c738b0f8d5995415271f",
                "66f75c0e0c7a40658629e3392f7f8e3349a02191ffd49f39879a8d9d1d0e23ea",
                "3c5a13adb18d31c64cc29972030c917d"),
            new TestCase(
                "ca3c6a0da0a864024ca3090628c28e0d" + "25eb69bac5cdf7d6bfcee4d9d5507b82",
                "046772a4f0a8de92e4f0d628cdb04484",
                "66f75c0e0c7a40658629e3392f7f8e3349a02191ffd49f39879a8d9d1d0e23ea3caa4d240bd2ab8a8c4a6bb8d3288d9de4b793f05e97646dd4d98055de",
                "fc5fb58dc65daf19b14d1d05da1064e8"),
            // Specific test cases generated from test-poly1305aes from poly1305aes-20050218 that
            // expose Java unsigned integer problems
            new TestCase(
                "01bcb20bfc8b6e03609ddd09f44b060f" + "95cc0e44d0b79a8856afcae1bec4fe3c",
                null,
                "66f75c0e0c7a40658629e3392f7f8e3349a02191ffd49f39879a8d9d1d0e23ea3caa4d240bd2ab8a8c4a6bb8d3288d9de4b793f05e97646dd4d98055de"
                    + "fc3e0677d956b4c62664bac15962ab15d93ccbbc03aafdbde779162ed93b55361f0f8acaa41d50ef5175927fe79ea316186516eef15001cd04d3524a55"
                    + "e4fa3c5ca479d3aaa8a897c21807f721b6270ffc68b6889d81a116799f6aaa35d8e04c7a7dd5e6da2519e8759f54e906696f5772fee093283bcef7b930"
                    + "aed50323bcbc8c820c67422c1e16bdc022a9c0277c9d95fef0ea4ee11e2b27276da811523c5acb80154989f8a67ee9e3fa30b73b0c1c34bf46e3464d97"
                    + "7cd7fcd0ac3b82721080bb0d9b982ee2c77feee983d7ba35da88ce86955002940652ab63bc56fb16f994da2b01d74356509d7d1b6d7956b0e5a557757b"
                    + "d1ced2eef8650bc5b6d426108c1518abcbd0befb6a0d5fd57a3e2dbf31458eab63df66613653d4beae73f5c40eb438fbcfdcf4a4ba46320184b9ca0da4"
                    + "dfae77de7ccc910356caea3243f33a3c81b064b3b7cedc7435c223f664227215715980e6e0bb570d459ba80d7512dbe458c8f0f3f52d659b6e8eef19ee"
                    + "71aea2ced85c7a42ffca6522a62db49a2a46eff72bd7f7e0883acd087183f0627f3537a4d558754ed63358e8182bee196735b361dc9bd64d5e34e1074a"
                    + "855655d2974cc6fa1653754cf40f561d8c7dc526aab2908ec2d2b977cde1a1fb1071e32f40e049ea20f30368ba1592b4fe57fb51595d23acbdace324cd"
                    + "d78060a17187c662368854e915402d9b52fb21e984663e41c26a109437e162cfaf071b53f77e50000a5388ff183b82ce7a1af476c416d7d204157b3633"
                    + "b2f4ec077b699b032816997e37bceded8d4a04976fd7d0c0b029f290794c3be504c5242287ea2f831f11ed5690d92775cd6e863d7731fd4da687ebfb13"
                    + "df4c41dc0fb8", "ae345d555eb04d6947bb95c0965237e2"),
            new TestCase(
                "cd07fd0ef8c0be0afcbdb30af4af0009" + "76fb3635a2dc92a1f768163ab12f2187",
                null,
                "f05204a74f0f88a7fa1a95b84ec3d8ffb36fcdc7723ea65dfe7cd464e86e0abf6b9d51db3220cfd8496ad6e6d36ebee8d990f9ce0d3bb7f72b7ab5b3ab0a73240d11efe772c857021ae859db4933cdde4387b471d2ce700fef4b81087f8f47c307881fd83017afcd15b8d21edf9b704677f46df97b07e5b83f87c8abd90af9b1d0f9e2710e8ebd0d4d1c6a055abea861f42368bed94d9373e909c1d3715b221c16bc524c55c31ec3eab204850bb2474a84f9917038eff9d921130951391b5c54f09b5e1de833ea2cd7d3b306740abb7096d1e173da83427da2adddd3631eda30b54dbf487f2b082e8646f07d6e0a87e97522ca38d4ace4954bf3db6dd3a93b06fa18eb56856627ed6cffcd7ae26374554ca18ab8905f26331d323fe10e6e70624c7bc07a70f06ecd804b48f8f7e75e910165e1beb554f1f0ec7949c9c8d429a206b4d5c0653102249b6098e6b45fac2a07ff0220b0b8ae8f4c6bcc0c813a7cd141fa8b398b42575fc395747c5a0257ac41d6c1f434cfbf5dfe8349f5347ef6b60e611f5d6c3cbc20ca2555274d1934325824cef4809da293ea13f181929e2af025bbd1c9abdc3af93afd4c50a2854ade3887f4d2c8c225168052c16e74d76d2dd3e9467a2c5b8e15c06ffbffa42b8536384139f07e195a8c9f70f514f31dca4eb2cf262c0dcbde53654b6250a29efe21d54e83c80e005a1cad36d5934ff01c32e4bc5fe06d03064ff4a268517df4a94c759289f323734318cfa5d859d4ce9c16e63d02dff0896976f521607638535d2ee8dd3312e1ddc80a55d34fe829ab954c1ebd54d929954770f1be9d32b4c05003c5c9e97943b6431e2afe820b1e967b19843e5985a131b1100517cdc363799104af91e2cf3f53cb8fd003653a6dd8a31a3f9d566a7124b0ffe9695bcb87c482eb60106f88198f766a40bc0f4873c23653c5f9e7a8e446f770beb8034cf01d21028ba15ccee21a8db918c4829d61c88bfa927bc5def831501796c5b401a60a6b1b433c9fb905c8cd40412fffee81ab",
                "045be28cc52009f506bdbfabedacf0b4"),
            // Test case from JIRA issue BJA-620
            new TestCase(
                "ffffffffffffffffffffffffffffffff" + "ffffffffffffffffffffffffffffffff",
                null,
                  "ffffffffffffffffffffffffffffffff" + "ffffffffffffffffffffffffffffffff" + "ffffffffffffffffffffffffffffffff" + "ffffffffffffffffffffffffffffffff"
                + "ffffffffffffffffffffffffffffffff" + "ffffffffffffffffffffffffffffffff" + "ffffffffffffffffffffffffffffffff" + "ffffffffffffffffffffffffffffffff"
                + "ffffffffffffffffffffffffffffffff" + "ffffffffffffffffffffffffffffffff" + "ffffffffffffffffffffffffffffffff" + "ffffffffffffffffffffffffffffffff"
                + "ffffffffffffffffffffffffffffffff" + "ffffffffffffffffffffffffffffffff" + "ffffffffffffffffffffffffffffffff" + "ffffffffffffffffffffffffffffff",
                "c80cb43844f387946e5aa6085bdf67da")
        };

		public override string Name
		{
			get { return "Poly1305"; }
		}

		public override void PerformTest()
		{
			testKeyGenerator();
			testInit();
			for (int i = 0; i < CASES.Length; i++)
			{
				testCase(i);
			}
			testSequential();
			testReset();
            rfc7539Test();
		}

		private void testCase(int i)
		{
			byte[] output = new byte[16];
			TestCase tc = CASES[i];

			IMac mac;
			if (tc.nonce == null)
			{
				// Raw Poly1305 test - don't do any transform on AES key part
                mac = new Poly1305();
                mac.Init(new KeyParameter(tc.key));
			}
			else
			{
				mac = new Poly1305(new AesEngine());
				mac.Init(new ParametersWithIV(new KeyParameter(tc.key), tc.nonce));
			}
			mac.BlockUpdate(tc.message, 0, tc.message.Length);
			mac.DoFinal(output, 0);

			if (!Arrays.AreEqual(output, tc.expectedMac))
			{
				Fail("Mismatched output " + i, Hex.ToHexString(tc.expectedMac), Hex.ToHexString(output));
			}
		}

		private void testSequential()
		{
			// Sequential test, adapted from test-poly1305aes
			int len;
			byte[] kr = new byte[32];
			byte[] m = new byte[MAXLEN];
			byte[] n = new byte[16];
			byte[] output = new byte[16];

			int c = 0;
			IMac mac = new Poly1305(new AesEngine());
			for (int loop = 0; loop < 13; loop++)
			{
				len = 0;
				for (;;)
				{
					c++;
					mac.Init(new ParametersWithIV(new KeyParameter(kr), n));
					mac.BlockUpdate(m, 0, len);
					mac.DoFinal(output, 0);

					// if (c == 678)
					// {
					// TestCase tc = CASES[0];
					//
					// if (!Arrays.AreEqual(tc.key, kr))
					// {
					// System.err.println("Key bad");
					// System.err.println(Hex.ToHexString(tc.key)));
					// System.err.println(Hex.ToHexString(kr)));
					// System.exit(1);
					// }
					// if (!Arrays.AreEqual(tc.nonce, n))
					// {
					// System.err.println("Nonce bad");
					// System.exit(1);
					// }
					// System.out.printf("[%d] m: %s\n", c, Hex.ToHexString(m, 0, len)));
					// System.out.printf("[%d] K: %s\n", c, new string(Hex.encodje(kr)));
					// System.out.printf("[%d] N: %s\n", c, Hex.ToHexString(n)));
					// System.out.printf("[%d] M: ", c);
					// }
					// System.out.printf("%d/%s\n", c, Hex.ToHexString(out)));

					if (len >= MAXLEN)
						break;
					n[0] = (byte)(n[0] ^ loop);
                    for (int i = 0; i < 16; ++i)
                    {
                        n[i] ^= output[i];
                    }
                    if (len % 2 != 0)
                    {
                        for (int i = 0; i < 16; ++i)
                        {
                            kr[i] ^= output[i];
                        }
                    }
					if (len % 3 != 0)
                    {
						for (int i = 0; i < 16; ++i)
                        {
							kr[i + 16] ^= output[i];
                        }
                    }
                    Poly1305KeyGenerator.Clamp(kr);
					m[len++] ^= output[0];
				}
			}
			// Output after 13 loops as generated by poly1305 ref
            if (c != 13013 || !Arrays.AreEqual(output, Hex.Decode("89824ddf0816481051f4a82731cd56d5")))
            {
                Fail("Sequential Poly1305 " + c, "89824ddf0816481051f4a82731cd56d5", Hex.ToHexString(output));
            }
		}

		private void testReset()
		{
			CipherKeyGenerator gen = new Poly1305KeyGenerator();
			gen.Init(new KeyGenerationParameters(new SecureRandom(), 256));
			byte[] k = gen.GenerateKey();

			byte[] m = new byte[10000];
			byte[] check = new byte[16];
			byte[] output = new byte[16];

			// Generate baseline
			IMac poly = new Poly1305(new AesEngine());
			poly.Init(new ParametersWithIV(new KeyParameter(k), new byte[16]));

			poly.BlockUpdate(m, 0, m.Length);
			poly.DoFinal(check, 0);

			// Check reset after doFinal
			poly.BlockUpdate(m, 0, m.Length);
			poly.DoFinal(output, 0);

			if (!Arrays.AreEqual(check, output))
			{
				Fail("Mac not reset after doFinal");
			}

			// Check reset
			poly.Update((byte)1);
			poly.Update((byte)2);
			poly.Reset();
			poly.BlockUpdate(m, 0, m.Length);
			poly.DoFinal(output, 0);

			if (!Arrays.AreEqual(check, output))
			{
				Fail("Mac not reset after doFinal");
			}

			// Check init resets
			poly.Update((byte)1);
			poly.Update((byte)2);
			poly.Init(new ParametersWithIV(new KeyParameter(k), new byte[16]));
			poly.BlockUpdate(m, 0, m.Length);
			poly.DoFinal(output, 0);

			if (!Arrays.AreEqual(check, output))
			{
				Fail("Mac not reset after doFinal");
			}
		}

		private void testInit()
		{
			CipherKeyGenerator gen = new Poly1305KeyGenerator();
			gen.Init(new KeyGenerationParameters(new SecureRandom(), 256));
			byte[] k = gen.GenerateKey();

			IMac poly = new Poly1305(new AesEngine());
			poly.Init(new ParametersWithIV(new KeyParameter(k), new byte[16]));

			try
			{
				poly.Init(new ParametersWithIV(new KeyParameter(k), new byte[15]));
				Fail("16 byte nonce required");
			}
            catch (ArgumentException)
			{
				// Expected
			}

			try
			{
				byte[] k2 = new byte[k.Length - 1];
				Array.Copy(k, 0, k2, 0, k2.Length);
				poly.Init(new ParametersWithIV(new KeyParameter(k2), new byte[16]));
				Fail("32 byte key required");
			}
            catch (ArgumentException)
			{
				// Expected
			}

            /*
			try
			{
				k[19] = (byte)0xFF;
				poly.Init(new ParametersWithIV(new KeyParameter(k), new byte[16]));
				Fail("UnClamped key should not be accepted.");
			}
            catch (ArgumentException)
			{
				// Expected
			}
            */
		}

		private void testKeyGenerator()
		{
			CipherKeyGenerator gen = new Poly1305KeyGenerator();
			gen.Init(new KeyGenerationParameters(new SecureRandom(), 256));
			byte[] k = gen.GenerateKey();

			if (k.Length != 32)
			{
				Fail("Poly1305 key should be 256 bits.");
			}

			try
			{
				Poly1305KeyGenerator.CheckKey(k);
			}
            catch (ArgumentException)
			{
				Fail("Poly1305 key should be Clamped on generation.");
			}

			byte[] k2 = new byte[k.Length];
			Array.Copy(k, 0, k2, 0, k2.Length);
			Poly1305KeyGenerator.Clamp(k);
			if (!Arrays.AreEqual(k, k2))
			{
				Fail("Poly1305 key should be Clamped on generation.");
			}

            /*
			try
			{
				k2[19] = (byte)0xff;
				Poly1305KeyGenerator.CheckKey(k2);
				Fail("UnClamped key should fail check.");
			}
            catch (ArgumentException)
			{
				// Expected
			}
            */
		}

        public void rfc7539Test()
        {
            // From RFC 7539
            byte[] keyMaterial = Hex.Decode("85d6be7857556d337f4452fe42d506a80103808afb0db2fd4abff6af4149f51b");
            byte[] data = Hex.Decode("43727970746f677261706869 63 20 46 6f 72 75 6d 20 52 65 73 65 61 72 63 68 20 47 72 6f7570");
            byte[] expected = Hex.Decode("a8061dc1305136c6c22b8baf0c0127a9");

            CheckVector(keyMaterial, data, expected);

            data = Hex.Decode("48656c6c6f20776f726c6421");
            keyMaterial = Hex.Decode(
                "746869732069732033322d6279746520" +
                    "6b657920666f7220506f6c7931333035");

            CheckVector(keyMaterial, data, Hex.Decode("a6f745008f81c916a20dcc74eef2b2f0"));

            // A.3 #1
            keyMaterial = Hex.Decode("00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00");

            data = Hex.Decode(
                "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00"
                    + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00"
                    + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00"
                    + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00");

            CheckVector(keyMaterial, data, Hex.Decode("00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00"));

            // A.3 #2
            keyMaterial = Hex.Decode("00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0036 e5 f6 b5 c5 e0 60 70 f0 ef ca 96 22 7a 86 3e");

            data = Hex.Decode(
                "41 6e 79 20 73 75 62 6d 69 73 73 69 6f 6e 20 74"
                    + "6f 20 74 68 65 20 49 45 54 46 20 69 6e 74 65 6e"
                    + "64 65 64 20 62 79 20 74 68 65 20 43 6f 6e 74 72"
                    + "69 62 75 74 6f 72 20 66 6f 72 20 70 75 62 6c 69"
                    + "63 61 74 69 6f 6e 20 61 73 20 61 6c 6c 20 6f 72"
                    + "20 70 61 72 74 20 6f 66 20 61 6e 20 49 45 54 46"
                    + "20 49 6e 74 65 72 6e 65 74 2d 44 72 61 66 74 20"
                    + "6f 72 20 52 46 43 20 61 6e 64 20 61 6e 79 20 73"
                    + "74 61 74 65 6d 65 6e 74 20 6d 61 64 65 20 77 69"
                    + "74 68 69 6e 20 74 68 65 20 63 6f 6e 74 65 78 74"
                    + "20 6f 66 20 61 6e 20 49 45 54 46 20 61 63 74 69"
                    + "76 69 74 79 20 69 73 20 63 6f 6e 73 69 64 65 72"
                    + "65 64 20 61 6e 20 22 49 45 54 46 20 43 6f 6e 74"
                    + "72 69 62 75 74 69 6f 6e 22 2e 20 53 75 63 68 20"
                    + "73 74 61 74 65 6d 65 6e 74 73 20 69 6e 63 6c 75"
                    + "64 65 20 6f 72 61 6c 20 73 74 61 74 65 6d 65 6e"
                    + "74 73 20 69 6e 20 49 45 54 46 20 73 65 73 73 69"
                    + "6f 6e 73 2c 20 61 73 20 77 65 6c 6c 20 61 73 20"
                    + "77 72 69 74 74 65 6e 20 61 6e 64 20 65 6c 65 63"
                    + "74 72 6f 6e 69 63 20 63 6f 6d 6d 75 6e 69 63 61"
                    + "74 69 6f 6e 73 20 6d 61 64 65 20 61 74 20 61 6e"
                    + "79 20 74 69 6d 65 20 6f 72 20 70 6c 61 63 65 2c"
                    + "20 77 68 69 63 68 20 61 72 65 20 61 64 64 72 65"
                    + "73 73 65 64 20 74 6f");

            CheckVector(keyMaterial, data, Hex.Decode("36 e5 f6 b5 c5 e0 60 70 f0 ef ca 96 22 7a 86 3e"));

            // A.3 #3
            keyMaterial = Hex.Decode("36 e5 f6 b5 c5 e0 60 70 f0 ef ca 96 22 7a 86 3e00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00");

            CheckVector(keyMaterial, data, Hex.Decode("f3 47 7e 7c d9 54 17 af 89 a6 b8 79 4c 31 0c f0"));

            // A.3 #4

            keyMaterial = Hex.Decode("1c 92 40 a5 eb 55 d3 8a f3 33 88 86 04 f6 b5 f0 47 39 17 c1 40 2b 80 09 9d ca 5c bc 20 70 75 c0");

            data = Hex.Decode(
                "27 54 77 61 73 20 62 72 69 6c 6c 69 67 2c 20 61"
                    + "6e 64 20 74 68 65 20 73 6c 69 74 68 79 20 74 6f"
                    + "76 65 73 0a 44 69 64 20 67 79 72 65 20 61 6e 64"
                    + "20 67 69 6d 62 6c 65 20 69 6e 20 74 68 65 20 77"
                    + "61 62 65 3a 0a 41 6c 6c 20 6d 69 6d 73 79 20 77"
                    + "65 72 65 20 74 68 65 20 62 6f 72 6f 67 6f 76 65"
                    + "73 2c 0a 41 6e 64 20 74 68 65 20 6d 6f 6d 65 20"
                    + "72 61 74 68 73 20 6f 75 74 67 72 61 62 65 2e");

            CheckVector(keyMaterial, data, Hex.Decode("45 41 66 9a 7e aa ee 61 e7 08 dc 7c bc c5 eb 62"));

            // A.3 #5
            keyMaterial = Hex.Decode("02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00");
            data = Hex.Decode("FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF");

            CheckVector(keyMaterial, data, Hex.Decode("03 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00"));

            // A.3 #6
            keyMaterial = Hex.Decode("02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF");
            data = Hex.Decode("02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00");

            CheckVector(keyMaterial, data, Hex.Decode("03 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00"));

            // A.3 #7
            keyMaterial = Hex.Decode("01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00");
            data = Hex.Decode("FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FFF0 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF11 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00");

            CheckVector(keyMaterial, data, Hex.Decode("05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00"));

            // A.3 #8
            keyMaterial = Hex.Decode("01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00");
            data = Hex.Decode("FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FFFB FE FE FE FE FE FE FE FE FE FE FE FE FE FE FE01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01");

            CheckVector(keyMaterial, data, Hex.Decode("00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00"));

            // A.3 #9
            keyMaterial = Hex.Decode("02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00");
            data = Hex.Decode("FD FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF");

            CheckVector(keyMaterial, data, Hex.Decode("FA FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF"));

            // A.3 #10
            keyMaterial = Hex.Decode("01 00 00 00 00 00 00 00 04 00 00 00 00 00 00 0000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00");
            data = Hex.Decode(
                "E3 35 94 D7 50 5E 43 B9 00 00 00 00 00 00 00 00"
                    + "33 94 D7 50 5E 43 79 CD 01 00 00 00 00 00 00 00"
                    + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00"
                    + "01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00");

            CheckVector(keyMaterial, data, Hex.Decode("14 00 00 00 00 00 00 00 55 00 00 00 00 00 00 00"));

            // A.3 #11
            keyMaterial = Hex.Decode("01 00 00 00 00 00 00 00 04 00 00 00 00 00 00 0000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00");
            data = Hex.Decode(
                "E3 35 94 D7 50 5E 43 B9 00 00 00 00 00 00 00 00"
                    + "33 94 D7 50 5E 43 79 CD 01 00 00 00 00 00 00 00"
                    + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00");

            CheckVector(keyMaterial, data, Hex.Decode("13 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00"));
        }

        private void CheckVector(byte[] keyMaterial, byte[] input, byte[] tag)
        {
            Poly1305 poly1305 = new Poly1305();

            poly1305.Init(new KeyParameter(keyMaterial));

            poly1305.BlockUpdate(input, 0, input.Length);

            byte[] mac = new byte[poly1305.GetMacSize()];

            poly1305.DoFinal(mac, 0);

            if (!Arrays.AreEqual(tag, mac))
            {
                Fail("rfc7539", Hex.ToHexString(tag), Hex.ToHexString(mac));
            }
       }

		public static void Main(
			string[] args)
		{
			RunTest(new Poly1305Test());
		}

		[Test]
		public void TestFunction()
		{
			string resultText = Perform().ToString();

			Assert.AreEqual(Name + ": Okay", resultText);
		}

	}
}