diff options
author | Peter Dettman <peter.dettman@bouncycastle.org> | 2019-09-09 21:01:14 +0700 |
---|---|---|
committer | Peter Dettman <peter.dettman@bouncycastle.org> | 2019-09-09 21:01:14 +0700 |
commit | b4b389a9e08375c78a25b63aca2468abe75e5428 (patch) | |
tree | ed2f1d614f0109c375f5b6dccfc6c005080af994 /crypto/test | |
parent | Tampering and reuse test cases for GCM (diff) | |
download | BouncyCastle.NET-ed25519-b4b389a9e08375c78a25b63aca2468abe75e5428.tar.xz |
Port ChaCha20Poly1305 from bc-java
Diffstat (limited to 'crypto/test')
-rw-r--r-- | crypto/test/UnitTests.csproj | 1 | ||||
-rw-r--r-- | crypto/test/src/crypto/test/ChaCha20Poly1305Test.cs | 443 | ||||
-rw-r--r-- | crypto/test/src/crypto/test/RegressionTest.cs | 1 |
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(), |