summary refs log tree commit diff
path: root/crypto/test
diff options
context:
space:
mode:
authorPeter Dettman <peter.dettman@bouncycastle.org>2019-09-09 21:01:14 +0700
committerPeter Dettman <peter.dettman@bouncycastle.org>2019-09-09 21:01:14 +0700
commitb4b389a9e08375c78a25b63aca2468abe75e5428 (patch)
treeed2f1d614f0109c375f5b6dccfc6c005080af994 /crypto/test
parentTampering and reuse test cases for GCM (diff)
downloadBouncyCastle.NET-ed25519-b4b389a9e08375c78a25b63aca2468abe75e5428.tar.xz
Port ChaCha20Poly1305 from bc-java
Diffstat (limited to 'crypto/test')
-rw-r--r--crypto/test/UnitTests.csproj1
-rw-r--r--crypto/test/src/crypto/test/ChaCha20Poly1305Test.cs443
-rw-r--r--crypto/test/src/crypto/test/RegressionTest.cs1
3 files changed, 445 insertions, 0 deletions
diff --git a/crypto/test/UnitTests.csproj b/crypto/test/UnitTests.csproj
index 0ab02f02e..5b39de6ad 100644
--- a/crypto/test/UnitTests.csproj
+++ b/crypto/test/UnitTests.csproj
@@ -168,6 +168,7 @@
     <Compile Include="src\crypto\test\BlowfishTest.cs" />
     <Compile Include="src\crypto\test\CAST6Test.cs" />
     <Compile Include="src\crypto\test\CCMTest.cs" />
+    <Compile Include="src\crypto\test\ChaCha20Poly1305Test.cs" />
     <Compile Include="src\crypto\test\ChaChaTest.cs" />
     <Compile Include="src\crypto\test\CMacTest.cs" />
     <Compile Include="src\crypto\test\CTSTest.cs" />
diff --git a/crypto/test/src/crypto/test/ChaCha20Poly1305Test.cs b/crypto/test/src/crypto/test/ChaCha20Poly1305Test.cs
new file mode 100644
index 000000000..3f74669dc
--- /dev/null
+++ b/crypto/test/src/crypto/test/ChaCha20Poly1305Test.cs
@@ -0,0 +1,443 @@
+using System;
+
+using NUnit.Framework;
+
+using Org.BouncyCastle.Crypto.Engines;
+using Org.BouncyCastle.Crypto.Macs;
+using Org.BouncyCastle.Crypto.Modes;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Date;
+using Org.BouncyCastle.Utilities.Encoders;
+using Org.BouncyCastle.Utilities.Test;
+
+namespace Org.BouncyCastle.Crypto.Tests
+{
+    [TestFixture]
+    public class ChaCha20Poly1305Test
+        : SimpleTest
+    {
+        private static readonly string[][] TestVectors = new string[][]
+        {
+            new string[]
+            {
+                "Test Case 1",
+                "808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f",
+                "4c616469657320616e642047656e746c"
+                + "656d656e206f662074686520636c6173"
+                + "73206f66202739393a20496620492063"
+                + "6f756c64206f6666657220796f75206f"
+                + "6e6c79206f6e652074697020666f7220"
+                + "746865206675747572652c2073756e73"
+                + "637265656e20776f756c642062652069"
+                + "742e",
+                "50515253c0c1c2c3c4c5c6c7",
+                "070000004041424344454647",
+                "d31a8d34648e60db7b86afbc53ef7ec2"
+                + "a4aded51296e08fea9e2b5a736ee62d6"
+                + "3dbea45e8ca9671282fafb69da92728b"
+                + "1a71de0a9e060b2905d6a5b67ecd3b36"
+                + "92ddbd7f2d778b8c9803aee328091b58"
+                + "fab324e4fad675945585808b4831d7bc"
+                + "3ff4def08e4b7a9de576d26586cec64b"
+                + "6116",
+                "1ae10b594f09e26a7e902ecbd0600691",
+            },
+        };
+
+        public override string Name
+        {
+            get { return "ChaCha20Poly1305"; }
+        }
+
+        public override void PerformTest()
+        {
+            for (int i = 0; i < TestVectors.Length; ++i)
+            {
+                RunTestCase(TestVectors[i]);
+            }
+
+            OutputSizeTests();
+            RandomTests();
+            TestExceptions();
+        }
+
+        private void CheckTestCase(
+            ChaCha20Poly1305    encCipher,
+            ChaCha20Poly1305    decCipher,
+            string              testName,
+            byte[]              SA,
+            byte[]              P,
+            byte[]              C,
+            byte[]              T)
+        {
+            byte[] enc = new byte[encCipher.GetOutputSize(P.Length)];
+            if (SA != null)
+            {
+                encCipher.ProcessAadBytes(SA, 0, SA.Length);
+            }
+            int len = encCipher.ProcessBytes(P, 0, P.Length, enc, 0);
+            len += encCipher.DoFinal(enc, len);
+
+            if (enc.Length != len)
+            {
+                Fail("encryption reported incorrect length: " + testName);
+            }
+
+            byte[] mac = encCipher.GetMac();
+
+            byte[] data = new byte[P.Length];
+            Array.Copy(enc, 0, data, 0, data.Length);
+            byte[] tail = new byte[enc.Length - P.Length];
+            Array.Copy(enc, P.Length, tail, 0, tail.Length);
+
+            if (!AreEqual(C, data))
+            {
+                Fail("incorrect encrypt in: " + testName);
+            }
+
+            if (!AreEqual(T, mac))
+            {
+                Fail("getMac() returned wrong mac in: " + testName);
+            }
+
+            if (!AreEqual(T, tail))
+            {
+                Fail("stream contained wrong mac in: " + testName);
+            }
+
+            byte[] dec = new byte[decCipher.GetOutputSize(enc.Length)];
+            if (SA != null)
+            {
+                decCipher.ProcessAadBytes(SA, 0, SA.Length);
+            }
+            len = decCipher.ProcessBytes(enc, 0, enc.Length, dec, 0);
+            len += decCipher.DoFinal(dec, len);
+            mac = decCipher.GetMac();
+
+            data = new byte[C.Length];
+            Array.Copy(dec, 0, data, 0, data.Length);
+
+            if (!AreEqual(P, data))
+            {
+                Fail("incorrect decrypt in: " + testName);
+            }
+        }
+
+        private ChaCha20Poly1305 InitCipher(bool forEncryption, AeadParameters parameters)
+        {
+            ChaCha20Poly1305 c = new ChaCha20Poly1305();
+            c.Init(forEncryption, parameters);
+            return c;
+        }
+
+        private static int NextInt(SecureRandom rand, int n)
+        {
+            if ((n & -n) == n)  // i.e., n is a power of 2
+            {
+                return (int)(((uint)n * (ulong)((uint)rand.NextInt() >> 1)) >> 31);
+            }
+
+            int bits, value;
+            do
+            {
+                bits = (int)((uint)rand.NextInt() >> 1);
+                value = bits % n;
+            }
+            while (bits - value + (n - 1) < 0);
+
+            return value;
+        }
+
+        private void OutputSizeTests()
+        {
+            byte[] K = new byte[32];
+            byte[] A = null;
+            byte[] N = new byte[12];
+
+            AeadParameters parameters = new AeadParameters(new KeyParameter(K), 16 * 8, N, A);
+            ChaCha20Poly1305 cipher = InitCipher(true, parameters);
+
+            if (cipher.GetUpdateOutputSize(0) != 0)
+            {
+                Fail("incorrect getUpdateOutputSize for initial 0 bytes encryption");
+            }
+
+            if (cipher.GetOutputSize(0) != 16)
+            {
+                Fail("incorrect getOutputSize for initial 0 bytes encryption");
+            }
+
+            cipher.Init(false, parameters);
+
+            if (cipher.GetUpdateOutputSize(0) != 0)
+            {
+                Fail("incorrect getUpdateOutputSize for initial 0 bytes decryption");
+            }
+
+            // NOTE: 0 bytes would be truncated data, but we want it to fail in the doFinal, not here
+            if (cipher.GetOutputSize(0) != 0)
+            {
+                Fail("fragile getOutputSize for initial 0 bytes decryption");
+            }
+
+            if (cipher.GetOutputSize(16) != 0)
+            {
+                Fail("incorrect getOutputSize for initial MAC-size bytes decryption");
+            }
+        }
+
+        private void RandomTests()
+        {
+            SecureRandom random = new SecureRandom();
+            random.SetSeed(DateTimeUtilities.CurrentUnixMs());
+
+            for (int i = 0; i < 10; ++i)
+            {
+                RandomTest(random);
+            }
+        }
+
+        private void RandomTest(SecureRandom random)
+        {
+            int kLength = 32;
+            byte[] K = new byte[kLength];
+            random.NextBytes(K);
+
+            int pLength = random.Next(65536);
+            byte[] P = new byte[pLength];
+            random.NextBytes(P);
+
+            int aLength = random.Next(256);
+            byte[] A = new byte[aLength];
+            random.NextBytes(A);
+
+            int saLength = random.Next(256);
+            byte[] SA = new byte[saLength];
+            random.NextBytes(SA);
+
+            int nonceLength = 12;
+            byte[] nonce = new byte[nonceLength];
+            random.NextBytes(nonce);
+
+            AeadParameters parameters = new AeadParameters(new KeyParameter(K), 16 * 8, nonce, A);
+            ChaCha20Poly1305 cipher = InitCipher(true, parameters);
+            byte[] C = new byte[cipher.GetOutputSize(P.Length)];
+            int predicted = cipher.GetUpdateOutputSize(P.Length);
+
+            int split = NextInt(random, SA.Length + 1);
+            cipher.ProcessAadBytes(SA, 0, split);
+            cipher.ProcessAadBytes(SA, split, SA.Length - split);
+
+            int len = cipher.ProcessBytes(P, 0, P.Length, C, 0);
+            if (predicted != len)
+            {
+                Fail("encryption reported incorrect update length in randomised test");
+            }
+
+            len += cipher.DoFinal(C, len);
+            if (C.Length != len)
+            {
+                Fail("encryption reported incorrect length in randomised test");
+            }
+
+            byte[] encT = cipher.GetMac();
+            byte[] tail = new byte[C.Length - P.Length];
+            Array.Copy(C, P.Length, tail, 0, tail.Length);
+
+            if (!AreEqual(encT, tail))
+            {
+                Fail("stream contained wrong mac in randomised test");
+            }
+
+            cipher.Init(false, parameters);
+            byte[] decP = new byte[cipher.GetOutputSize(C.Length)];
+            predicted = cipher.GetUpdateOutputSize(C.Length);
+
+            split = NextInt(random, SA.Length + 1);
+            cipher.ProcessAadBytes(SA, 0, split);
+            cipher.ProcessAadBytes(SA, split, SA.Length - split);
+
+            len = cipher.ProcessBytes(C, 0, C.Length, decP, 0);
+            if (predicted != len)
+            {
+                Fail("decryption reported incorrect update length in randomised test");
+            }
+
+            len += cipher.DoFinal(decP, len);
+
+            if (!AreEqual(P, decP))
+            {
+                Fail("incorrect decrypt in randomised test");
+            }
+
+            byte[] decT = cipher.GetMac();
+            if (!AreEqual(encT, decT))
+            {
+                Fail("decryption produced different mac from encryption");
+            }
+
+            //
+            // key reuse test
+            //
+            cipher.Init(false, AeadTestUtilities.ReuseKey(parameters));
+            decP = new byte[cipher.GetOutputSize(C.Length)];
+
+            split = NextInt(random, SA.Length + 1);
+            cipher.ProcessAadBytes(SA, 0, split);
+            cipher.ProcessAadBytes(SA, split, SA.Length - split);
+
+            len = cipher.ProcessBytes(C, 0, C.Length, decP, 0);
+            len += cipher.DoFinal(decP, len);
+
+            if (!AreEqual(P, decP))
+            {
+                Fail("incorrect decrypt in randomised test");
+            }
+
+            decT = cipher.GetMac();
+            if (!AreEqual(encT, decT))
+            {
+                Fail("decryption produced different mac from encryption");
+            }
+        }
+
+        private void RunTestCase(string[] testVector)
+        {
+            int pos = 0;
+            string testName = testVector[pos++];
+            byte[] K = Hex.DecodeStrict(testVector[pos++]);
+            byte[] P = Hex.DecodeStrict(testVector[pos++]);
+            byte[] A = Hex.DecodeStrict(testVector[pos++]);
+            byte[] N = Hex.DecodeStrict(testVector[pos++]);
+            byte[] C = Hex.DecodeStrict(testVector[pos++]);
+            byte[] T = Hex.DecodeStrict(testVector[pos++]);
+
+            RunTestCase(testName, K, N, A, P, C, T);
+        }
+
+        private void RunTestCase(
+            string  testName,
+            byte[]  K,
+            byte[]  N,
+            byte[]  A,
+            byte[]  P,
+            byte[]  C,
+            byte[]  T)
+        {
+            byte[] fa = new byte[A.Length / 2];
+            byte[] la = new byte[A.Length - (A.Length / 2)];
+            Array.Copy(A, 0, fa, 0, fa.Length);
+            Array.Copy(A, fa.Length, la, 0, la.Length);
+
+            RunTestCase(testName + " all initial associated data", K, N, A, null, P, C, T);
+            RunTestCase(testName + " all subsequent associated data", K, N, null, A, P, C, T);
+            RunTestCase(testName + " split associated data", K, N, fa, la, P, C, T);
+        }
+
+        private void RunTestCase(
+            string  testName,
+            byte[]  K,
+            byte[]  N,
+            byte[]  A,
+            byte[]  SA,
+            byte[]  P,
+            byte[]  C,
+            byte[]  T)
+        {
+            AeadParameters parameters = new AeadParameters(new KeyParameter(K), T.Length * 8, N, A);
+            ChaCha20Poly1305 encCipher = InitCipher(true, parameters);
+            ChaCha20Poly1305 decCipher = InitCipher(false, parameters);
+            CheckTestCase(encCipher, decCipher, testName, SA, P, C, T);
+            encCipher = InitCipher(true, parameters);
+            CheckTestCase(encCipher, decCipher, testName + " (reused)", SA, P, C, T);
+
+            // Key reuse
+            AeadParameters keyReuseParams = AeadTestUtilities.ReuseKey(parameters);
+
+            try
+            {
+                encCipher.Init(true, keyReuseParams);
+                Fail("no exception");
+            }
+            catch (ArgumentException e)
+            {
+                IsTrue("wrong message", "cannot reuse nonce for ChaCha20Poly1305 encryption".Equals(e.Message));
+            }
+        }
+
+        private void TestExceptions()
+        {
+            ChaCha20Poly1305 c = new ChaCha20Poly1305();
+
+            try
+            {
+                c = new ChaCha20Poly1305(new SipHash());
+
+                Fail("incorrect mac size not picked up");
+            }
+            catch (ArgumentException e)
+            {
+                // expected
+            }
+
+            try
+            {
+                c.Init(false, new KeyParameter(new byte[32]));
+
+                Fail("illegal argument not picked up");
+            }
+            catch (ArgumentException e)
+            {
+                // expected
+            }
+
+            AeadTestUtilities.TestTampering(this, c, new AeadParameters(new KeyParameter(new byte[32]), 128, new byte[12]));
+
+            byte[] P = Strings.ToByteArray("Hello world!");
+            byte[] buf = new byte[100];
+
+            c = new ChaCha20Poly1305();
+            AeadParameters aeadParameters = new AeadParameters(new KeyParameter(new byte[32]), 128, new byte[12]);
+            c.Init(true, aeadParameters);
+
+            c.ProcessBytes(P, 0, P.Length, buf, 0);
+
+            c.DoFinal(buf, 0);
+
+            try
+            {
+                c.DoFinal(buf, 0);
+                Fail("no exception on reuse");
+            }
+            catch (InvalidOperationException e)
+            {
+                IsTrue("wrong message", e.Message.Equals("ChaCha20Poly1305 cannot be reused for encryption"));
+            }
+
+            try
+            {
+                c.Init(true, aeadParameters);
+                Fail("no exception on reuse");
+            }
+            catch (ArgumentException e)
+            {
+                IsTrue("wrong message", e.Message.Equals("cannot reuse nonce for ChaCha20Poly1305 encryption"));
+            }
+        }
+
+        public static void Main(string[] args)
+        {
+            RunTest(new ChaCha20Poly1305Test());
+        }
+
+        [Test]
+        public void TestFunction()
+        {
+            string resultText = Perform().ToString();
+
+            Assert.AreEqual(Name + ": Okay", resultText);
+        }
+    }
+}
diff --git a/crypto/test/src/crypto/test/RegressionTest.cs b/crypto/test/src/crypto/test/RegressionTest.cs
index 2bc55e8a2..aa9cd0483 100644
--- a/crypto/test/src/crypto/test/RegressionTest.cs
+++ b/crypto/test/src/crypto/test/RegressionTest.cs
@@ -105,6 +105,7 @@ namespace Org.BouncyCastle.Crypto.Tests
             new Salsa20Test(),
             new XSalsa20Test(),
             new ChaChaTest(),
+            new ChaCha20Poly1305Test(),
             new CMacTest(),
             new EaxTest(),
             new GcmTest(),