diff --git a/crypto/test/UnitTests.csproj b/crypto/test/UnitTests.csproj
index 0c0184d42..c5fdecd54 100644
--- a/crypto/test/UnitTests.csproj
+++ b/crypto/test/UnitTests.csproj
@@ -467,6 +467,48 @@
<Compile Include="src\test\nist\NistCertPathTest.cs" />
<Compile Include="src\test\nist\NistCertPathTest2.cs" />
<Compile Include="src\test\rsa3\RSA3CertTest.cs" />
+ <Compile Include="src\tls\crypto\test\BcTlsCryptoTest.cs" />
+ <Compile Include="src\tls\test\ByteQueueInputStreamTest.cs" />
+ <Compile Include="src\tls\test\CertChainUtilities.cs" />
+ <Compile Include="src\tls\test\DtlsProtocolTest.cs" />
+ <Compile Include="src\tls\test\DtlsPskProtocolTest.cs" />
+ <Compile Include="src\tls\test\DtlsTestCase.cs" />
+ <Compile Include="src\tls\test\DtlsTestClientProtocol.cs" />
+ <Compile Include="src\tls\test\DtlsTestServerProtocol.cs" />
+ <Compile Include="src\tls\test\DtlsTestSuite.cs" />
+ <Compile Include="src\tls\test\LoggingDatagramTransport.cs" />
+ <Compile Include="src\tls\test\MockDatagramAssociation.cs" />
+ <Compile Include="src\tls\test\MockDtlsClient.cs" />
+ <Compile Include="src\tls\test\MockDtlsServer.cs" />
+ <Compile Include="src\tls\test\MockPskDtlsClient.cs" />
+ <Compile Include="src\tls\test\MockPskDtlsServer.cs" />
+ <Compile Include="src\tls\test\MockPskTlsClient.cs" />
+ <Compile Include="src\tls\test\MockPskTlsServer.cs" />
+ <Compile Include="src\tls\test\MockSrpTlsClient.cs" />
+ <Compile Include="src\tls\test\MockSrpTlsServer.cs" />
+ <Compile Include="src\tls\test\MockTlsClient.cs" />
+ <Compile Include="src\tls\test\MockTlsServer.cs" />
+ <Compile Include="src\tls\test\NetworkStream.cs" />
+ <Compile Include="src\tls\test\PipedStream.cs" />
+ <Compile Include="src\tls\test\PrfTest.cs" />
+ <Compile Include="src\tls\test\PskTlsClientTest.cs" />
+ <Compile Include="src\tls\test\PskTlsServerTest.cs" />
+ <Compile Include="src\tls\test\TlsClientTest.cs" />
+ <Compile Include="src\tls\test\TlsProtocolNonBlockingTest.cs" />
+ <Compile Include="src\tls\test\TlsProtocolTest.cs" />
+ <Compile Include="src\tls\test\TlsPskProtocolTest.cs" />
+ <Compile Include="src\tls\test\TlsServerTest.cs" />
+ <Compile Include="src\tls\test\TlsSrpProtocolTest.cs" />
+ <Compile Include="src\tls\test\TlsTestCase.cs" />
+ <Compile Include="src\tls\test\TlsTestClientImpl.cs" />
+ <Compile Include="src\tls\test\TlsTestClientProtocol.cs" />
+ <Compile Include="src\tls\test\TlsTestConfig.cs" />
+ <Compile Include="src\tls\test\TlsTestServerImpl.cs" />
+ <Compile Include="src\tls\test\TlsTestServerProtocol.cs" />
+ <Compile Include="src\tls\test\TlsTestSuite.cs" />
+ <Compile Include="src\tls\test\TlsTestUtilities.cs" />
+ <Compile Include="src\tls\test\TlsUtilitiesTest.cs" />
+ <Compile Include="src\tls\test\UnreliableDatagramTransport.cs" />
<Compile Include="src\tsp\test\AllTests.cs" />
<Compile Include="src\tsp\test\GenTimeAccuracyTest.cs" />
<Compile Include="src\tsp\test\ParseTest.cs" />
diff --git a/crypto/test/src/tls/crypto/test/BcTlsCryptoTest.cs b/crypto/test/src/tls/crypto/test/BcTlsCryptoTest.cs
new file mode 100644
index 000000000..a57212c73
--- /dev/null
+++ b/crypto/test/src/tls/crypto/test/BcTlsCryptoTest.cs
@@ -0,0 +1,820 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using NUnit.Framework;
+
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Tls.Crypto.Impl.BC;
+using Org.BouncyCastle.Tls.Tests;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Tls.Crypto.Tests
+{
+ [TestFixture]
+ public class BcTlsCryptoTest
+ {
+ private static readonly byte[] ClientHello = Hex("01 00 00 c0 03 03 cb 34 ec b1 e7 81 63"
+ + "ba 1c 38 c6 da cb 19 6a 6d ff a2 1a 8d 99 12 ec 18 a2 ef 62 83"
+ + "02 4d ec e7 00 00 06 13 01 13 03 13 02 01 00 00 91 00 00 00 0b"
+ + "00 09 00 00 06 73 65 72 76 65 72 ff 01 00 01 00 00 0a 00 14 00"
+ + "12 00 1d 00 17 00 18 00 19 01 00 01 01 01 02 01 03 01 04 00 23"
+ + "00 00 00 33 00 26 00 24 00 1d 00 20 99 38 1d e5 60 e4 bd 43 d2"
+ + "3d 8e 43 5a 7d ba fe b3 c0 6e 51 c1 3c ae 4d 54 13 69 1e 52 9a"
+ + "af 2c 00 2b 00 03 02 03 04 00 0d 00 20 00 1e 04 03 05 03 06 03"
+ + "02 03 08 04 08 05 08 06 04 01 05 01 06 01 02 01 04 02 05 02 06"
+ + "02 02 02 00 2d 00 02 01 01 00 1c 00 02 40 01");
+ private static readonly byte[] ServerHello = Hex("02 00 00 56 03 03 a6 af 06 a4 12 18 60"
+ + "dc 5e 6e 60 24 9c d3 4c 95 93 0c 8a c5 cb 14 34 da c1 55 77 2e"
+ + "d3 e2 69 28 00 13 01 00 00 2e 00 33 00 24 00 1d 00 20 c9 82 88"
+ + "76 11 20 95 fe 66 76 2b db f7 c6 72 e1 56 d6 cc 25 3b 83 3d f1"
+ + "dd 69 b1 b0 4e 75 1f 0f 00 2b 00 02 03 04");
+ private static readonly byte[] EncryptedExtensions = Hex("08 00 00 24 00 22 00 0a 00 14 00"
+ + "12 00 1d 00 17 00 18 00 19 01 00 01 01 01 02 01 03 01 04 00 1c"
+ + "00 02 40 01 00 00 00 00");
+ private static readonly byte[] Certificate = Hex("0b 00 01 b9 00 00 01 b5 00 01 b0 30 82"
+ + "01 ac 30 82 01 15 a0 03 02 01 02 02 01 02 30 0d 06 09 2a 86 48"
+ + "86 f7 0d 01 01 0b 05 00 30 0e 31 0c 30 0a 06 03 55 04 03 13 03"
+ + "72 73 61 30 1e 17 0d 31 36 30 37 33 30 30 31 32 33 35 39 5a 17"
+ + "0d 32 36 30 37 33 30 30 31 32 33 35 39 5a 30 0e 31 0c 30 0a 06"
+ + "03 55 04 03 13 03 72 73 61 30 81 9f 30 0d 06 09 2a 86 48 86 f7"
+ + "0d 01 01 01 05 00 03 81 8d 00 30 81 89 02 81 81 00 b4 bb 49 8f"
+ + "82 79 30 3d 98 08 36 39 9b 36 c6 98 8c 0c 68 de 55 e1 bd b8 26"
+ + "d3 90 1a 24 61 ea fd 2d e4 9a 91 d0 15 ab bc 9a 95 13 7a ce 6c"
+ + "1a f1 9e aa 6a f9 8c 7c ed 43 12 09 98 e1 87 a8 0e e0 cc b0 52"
+ + "4b 1b 01 8c 3e 0b 63 26 4d 44 9a 6d 38 e2 2a 5f da 43 08 46 74"
+ + "80 30 53 0e f0 46 1c 8c a9 d9 ef bf ae 8e a6 d1 d0 3e 2b d1 93"
+ + "ef f0 ab 9a 80 02 c4 74 28 a6 d3 5a 8d 88 d7 9f 7f 1e 3f 02 03"
+ + "01 00 01 a3 1a 30 18 30 09 06 03 55 1d 13 04 02 30 00 30 0b 06"
+ + "03 55 1d 0f 04 04 03 02 05 a0 30 0d 06 09 2a 86 48 86 f7 0d 01"
+ + "01 0b 05 00 03 81 81 00 85 aa d2 a0 e5 b9 27 6b 90 8c 65 f7 3a"
+ + "72 67 17 06 18 a5 4c 5f 8a 7b 33 7d 2d f7 a5 94 36 54 17 f2 ea"
+ + "e8 f8 a5 8c 8f 81 72 f9 31 9c f3 6b 7f d6 c5 5b 80 f2 1a 03 01"
+ + "51 56 72 60 96 fd 33 5e 5e 67 f2 db f1 02 70 2e 60 8c ca e6 be"
+ + "c1 fc 63 a4 2a 99 be 5c 3e b7 10 7c 3c 54 e9 b9 eb 2b d5 20 3b"
+ + "1c 3b 84 e0 a8 b2 f7 59 40 9b a3 ea c9 d9 1d 40 2d cc 0c c8 f8"
+ + "96 12 29 ac 91 87 b4 2b 4d e1 00 00");
+ private static readonly byte[] CertificateVerify = Hex("0f 00 00 84 08 04 00 80 5a 74 7c"
+ + "5d 88 fa 9b d2 e5 5a b0 85 a6 10 15 b7 21 1f 82 4c d4 84 14 5a"
+ + "b3 ff 52 f1 fd a8 47 7b 0b 7a bc 90 db 78 e2 d3 3a 5c 14 1a 07"
+ + "86 53 fa 6b ef 78 0c 5e a2 48 ee aa a7 85 c4 f3 94 ca b6 d3 0b"
+ + "be 8d 48 59 ee 51 1f 60 29 57 b1 54 11 ac 02 76 71 45 9e 46 44"
+ + "5c 9e a5 8c 18 1e 81 8e 95 b8 c3 fb 0b f3 27 84 09 d3 be 15 2a"
+ + "3d a5 04 3e 06 3d da 65 cd f5 ae a2 0d 53 df ac d4 2f 74 f3");
+ private static readonly byte[] ServerFinished = Hex("14 00 00 20 9b 9b 14 1d 90 63 37 fb d2 cb"
+ + "dc e7 1d f4 de da 4a b4 2c 30 95 72 cb 7f ff ee 54 54 b7 8f 07"
+ + "18");
+ private static readonly byte[] ClientFinished = Hex("14 00 00 20 a8 ec 43 6d 67 76 34 ae 52 5a"
+ + "c1 fc eb e1 1a 03 9e c1 76 94 fa c6 e9 85 27 b6 42 f2 ed d5 ce"
+ + "61");
+
+ private readonly TlsCrypto m_crypto = new BcTlsCrypto(new SecureRandom());
+
+ protected TlsCredentialedSigner LoadCredentialedSigner(TlsCryptoParameters cryptoParams, string resource,
+ SignatureAndHashAlgorithm signatureAndHashAlgorithm)
+ {
+ return TlsTestUtilities.LoadSignerCredentials(cryptoParams, m_crypto,
+ new string[]{ "x509-server-" + resource + ".pem" }, "x509-server-key-" + resource + ".pem",
+ signatureAndHashAlgorithm);
+ }
+
+ protected TlsCredentialedSigner LoadCredentialedSignerLegacy(TlsCryptoParameters cryptoParams,
+ short signatureAlgorithm)
+ {
+ switch (signatureAlgorithm)
+ {
+ case SignatureAlgorithm.dsa:
+ return LoadCredentialedSigner(cryptoParams, "dsa", null);
+ case SignatureAlgorithm.ecdsa:
+ return LoadCredentialedSigner(cryptoParams, "ecdsa", null);
+ case SignatureAlgorithm.rsa:
+ return LoadCredentialedSigner(cryptoParams, "rsa-sign", null);
+ default:
+ return null;
+ }
+ }
+
+ protected TlsCredentialedSigner LoadCredentialedSigner12(TlsCryptoParameters cryptoParams,
+ SignatureAndHashAlgorithm signatureAndHashAlgorithm)
+ {
+ switch (signatureAndHashAlgorithm.Signature)
+ {
+ case SignatureAlgorithm.dsa:
+ return LoadCredentialedSigner(cryptoParams, "dsa", signatureAndHashAlgorithm);
+ case SignatureAlgorithm.ecdsa:
+ return LoadCredentialedSigner(cryptoParams, "ecdsa", signatureAndHashAlgorithm);
+ case SignatureAlgorithm.ed25519:
+ return LoadCredentialedSigner(cryptoParams, "ed25519", signatureAndHashAlgorithm);
+ case SignatureAlgorithm.ed448:
+ return LoadCredentialedSigner(cryptoParams, "ed448", signatureAndHashAlgorithm);
+ case SignatureAlgorithm.rsa_pss_pss_sha256:
+ return LoadCredentialedSigner(cryptoParams, "rsa_pss_256", signatureAndHashAlgorithm);
+ case SignatureAlgorithm.rsa_pss_pss_sha384:
+ return LoadCredentialedSigner(cryptoParams, "rsa_pss_384", signatureAndHashAlgorithm);
+ case SignatureAlgorithm.rsa_pss_pss_sha512:
+ return LoadCredentialedSigner(cryptoParams, "rsa_pss_512", signatureAndHashAlgorithm);
+ case SignatureAlgorithm.rsa:
+ case SignatureAlgorithm.rsa_pss_rsae_sha256:
+ case SignatureAlgorithm.rsa_pss_rsae_sha384:
+ case SignatureAlgorithm.rsa_pss_rsae_sha512:
+ return LoadCredentialedSigner(cryptoParams, "rsa-sign", signatureAndHashAlgorithm);
+
+ // TODO[draft-smyshlyaev-tls12-gost-suites-10] Add test resources for these
+ case SignatureAlgorithm.gostr34102012_256:
+ case SignatureAlgorithm.gostr34102012_512:
+
+ default:
+ return null;
+ }
+ }
+
+ protected TlsCredentialedSigner LoadCredentialedSigner13(TlsCryptoParameters cryptoParams, int signatureScheme)
+ {
+ SignatureAndHashAlgorithm signatureAndHashAlgorithm = SignatureScheme.GetSignatureAndHashAlgorithm(
+ signatureScheme);
+
+ switch (signatureScheme)
+ {
+ case SignatureScheme.ecdsa_secp256r1_sha256:
+ return LoadCredentialedSigner(cryptoParams, "ecdsa", signatureAndHashAlgorithm);
+ case SignatureScheme.ed25519:
+ return LoadCredentialedSigner(cryptoParams, "ed25519", signatureAndHashAlgorithm);
+ case SignatureScheme.ed448:
+ return LoadCredentialedSigner(cryptoParams, "ed448", signatureAndHashAlgorithm);
+ case SignatureScheme.rsa_pss_pss_sha256:
+ return LoadCredentialedSigner(cryptoParams, "rsa_pss_256", signatureAndHashAlgorithm);
+ case SignatureScheme.rsa_pss_pss_sha384:
+ return LoadCredentialedSigner(cryptoParams, "rsa_pss_384", signatureAndHashAlgorithm);
+ case SignatureScheme.rsa_pss_pss_sha512:
+ return LoadCredentialedSigner(cryptoParams, "rsa_pss_512", signatureAndHashAlgorithm);
+ case SignatureScheme.rsa_pss_rsae_sha256:
+ case SignatureScheme.rsa_pss_rsae_sha384:
+ case SignatureScheme.rsa_pss_rsae_sha512:
+ return LoadCredentialedSigner(cryptoParams, "rsa-sign", signatureAndHashAlgorithm);
+
+ // TODO[tls] Add test resources for these
+ case SignatureScheme.ecdsa_brainpoolP256r1tls13_sha256:
+ case SignatureScheme.ecdsa_brainpoolP384r1tls13_sha384:
+ case SignatureScheme.ecdsa_brainpoolP512r1tls13_sha512:
+ case SignatureScheme.ecdsa_secp384r1_sha384:
+ case SignatureScheme.ecdsa_secp521r1_sha512:
+ case SignatureScheme.sm2sig_sm3:
+
+ default:
+ return null;
+ }
+ }
+
+ [Test]
+ public void TestDHDomain()
+ {
+ if (!m_crypto.HasDHAgreement())
+ {
+ return;
+ }
+
+ for (int namedGroup = 256; namedGroup < 512; ++namedGroup)
+ {
+ if (!NamedGroup.RefersToASpecificFiniteField(namedGroup) || !m_crypto.HasNamedGroup(namedGroup))
+ {
+ continue;
+ }
+
+ ImplTestDHDomain(new TlsDHConfig(namedGroup, false));
+ ImplTestDHDomain(new TlsDHConfig(namedGroup, true));
+ }
+
+ IList groups = new TestTlsDHGroupVerifier().Groups;
+ foreach (DHGroup dhGroup in groups)
+ {
+ int namedGroup = TlsDHUtilities.GetNamedGroupForDHParameters(dhGroup.P, dhGroup.G);
+ if (NamedGroup.RefersToASpecificFiniteField(namedGroup))
+ {
+ // Already tested the named groups
+ continue;
+ }
+
+ ImplTestDHDomain(new TlsDHConfig(dhGroup));
+ }
+ }
+
+ [Test]
+ public void TestECDomain()
+ {
+ if (!m_crypto.HasECDHAgreement())
+ {
+ return;
+ }
+
+ for (int namedGroup = 0; namedGroup < 256; ++namedGroup)
+ {
+ if (!NamedGroup.RefersToAnECDHCurve(namedGroup) || !m_crypto.HasNamedGroup(namedGroup))
+ {
+ continue;
+ }
+
+ ImplTestECDomain(new TlsECConfig(namedGroup));
+ }
+ }
+
+ [Test]
+ public void TestHkdf()
+ {
+ /*
+ * Test vectors drawn from the server-side calculations of example handshake trace in RFC 8448, section 3.
+ */
+
+ int hash = CryptoHashAlgorithm.sha256;
+ int hashLen = TlsCryptoUtilities.GetHashOutputSize(hash);
+
+ TlsSecret init = m_crypto.HkdfInit(hash), early, handshake, master, c_hs_t, s_hs_t, c_ap_t, s_ap_t, exp_master, res_master;
+
+ TlsHash prfHash = m_crypto.CreateHash(hash);
+
+ byte[] emptyTranscriptHash = GetCurrentHash(prfHash);
+ Expect(emptyTranscriptHash, "e3 b0 c4 42 98 fc 1c 14 9a fb f4 c8 99 6f b9 24 27 ae 41 e4 64 9b 93 4c a4 95 99 1b 78 52 b8 55");
+
+ // {server} extract secret "early":
+ {
+ byte[] ikm = new byte[32];
+ early = init.HkdfExtract(hash, ikm);
+ Expect(early, "33 ad 0a 1c 60 7e c0 3b 09 e6 cd 98 93 68 0c e2 10 ad f3 00 aa 1f 26 60 e1 b2 2e 10 f1 70 f9 2a");
+ }
+
+ // {server} derive secret for handshake "tls13 derived":
+ {
+ string label = "derived";
+ handshake = TlsCryptoUtilities.HkdfExpandLabel(early, hash, label, emptyTranscriptHash, hashLen);
+ Expect(handshake, "6f 26 15 a1 08 c7 02 c5 67 8f 54 fc 9d ba b6 97 16 c0 76 18 9c 48 25 0c eb ea c3 57 6c 36 11 ba");
+ }
+
+ // {server} extract secret "handshake":
+ {
+ byte[] ikm = Hex("8b d4 05 4f b5 5b 9d 63 fd fb ac f9 f0 4b 9f 0d 35 e6 d6 3f 53 75 63 ef d4 62 72 90 0f 89 49 2d");
+ handshake = handshake.HkdfExtract(hash, ikm);
+ Expect(handshake, "1d c8 26 e9 36 06 aa 6f dc 0a ad c1 2f 74 1b 01 04 6a a6 b9 9f 69 1e d2 21 a9 f0 ca 04 3f be ac");
+ }
+
+ prfHash.Update(ClientHello, 0, ClientHello.Length);
+ prfHash.Update(ServerHello, 0, ServerHello.Length);
+
+ byte[] serverHelloTranscriptHash = GetCurrentHash(prfHash);
+ Expect(serverHelloTranscriptHash, "86 0c 06 ed c0 78 58 ee 8e 78 f0 e7 42 8c 58 ed d6 b4 3f 2c a3 e6 e9 5f 02 ed 06 3c f0 e1 ca d8");
+
+ // {server} derive secret "tls13 c hs traffic":
+ {
+ string label = "c hs traffic";
+ c_hs_t = TlsCryptoUtilities.HkdfExpandLabel(handshake, hash, label, serverHelloTranscriptHash, hashLen);
+ Expect(c_hs_t, "b3 ed db 12 6e 06 7f 35 a7 80 b3 ab f4 5e 2d 8f 3b 1a 95 07 38 f5 2e 96 00 74 6a 0e 27 a5 5a 21");
+ }
+
+ // {server} derive secret "tls13 s hs traffic":
+ {
+ string label = "s hs traffic";
+ s_hs_t = TlsCryptoUtilities.HkdfExpandLabel(handshake, hash, label, serverHelloTranscriptHash, hashLen);
+ Expect(s_hs_t, "b6 7b 7d 69 0c c1 6c 4e 75 e5 42 13 cb 2d 37 b4 e9 c9 12 bc de d9 10 5d 42 be fd 59 d3 91 ad 38");
+ }
+
+ // {server} derive secret for master "tls13 derived":
+ {
+ string label = "derived";
+ master = TlsCryptoUtilities.HkdfExpandLabel(handshake, hash, label, emptyTranscriptHash, hashLen);
+ Expect(master, "43 de 77 e0 c7 77 13 85 9a 94 4d b9 db 25 90 b5 31 90 a6 5b 3e e2 e4 f1 2d d7 a0 bb 7c e2 54 b4");
+ }
+
+ // {server} extract secret "master":
+ {
+ byte[] ikm = new byte[32];
+ master = master.HkdfExtract(hash, ikm);
+ Expect(master, "18 df 06 84 3d 13 a0 8b f2 a4 49 84 4c 5f 8a 47 80 01 bc 4d 4c 62 79 84 d5 a4 1d a8 d0 40 29 19");
+ }
+
+ // {server} derive write traffic keys for handshake data:
+ {
+ TlsSecret key = TlsCryptoUtilities.HkdfExpandLabel(s_hs_t, hash, "key", TlsUtilities.EmptyBytes, 16);
+ Expect(key, "3f ce 51 60 09 c2 17 27 d0 f2 e4 e8 6e e4 03 bc");
+
+ TlsSecret iv = TlsCryptoUtilities.HkdfExpandLabel(s_hs_t, hash, "iv", TlsUtilities.EmptyBytes, 12);
+ Expect(iv, "5d 31 3e b2 67 12 76 ee 13 00 0b 30");
+ }
+
+ prfHash.Update(EncryptedExtensions, 0, EncryptedExtensions.Length);
+ prfHash.Update(Certificate, 0, Certificate.Length);
+ prfHash.Update(CertificateVerify, 0, CertificateVerify.Length);
+
+ // {server} calculate (server) finished "tls13 finished":
+ {
+ TlsSecret expanded = TlsCryptoUtilities.HkdfExpandLabel(s_hs_t, hash, "finished", TlsUtilities.EmptyBytes, hashLen);
+ Expect(expanded, "00 8d 3b 66 f8 16 ea 55 9f 96 b5 37 e8 85 c3 1f c0 68 bf 49 2c 65 2f 01 f2 88 a1 d8 cd c1 9f c8");
+
+ // TODO Mention this transcript hash in RFC 8448 data?
+ byte[] transcriptHash = GetCurrentHash(prfHash);
+ Expect(transcriptHash, "ed b7 72 5f a7 a3 47 3b 03 1e c8 ef 65 a2 48 54 93 90 01 38 a2 b9 12 91 40 7d 79 51 a0 61 10 ed");
+
+ byte[] finished = CalculateHmac(hash, expanded, transcriptHash);
+ Expect(finished, Hex("9b 9b 14 1d 90 63 37 fb d2 cb dc e7 1d f4 de da 4a b4 2c 30 95 72 cb 7f ff ee 54 54 b7 8f 07 18"));
+ }
+
+ prfHash.Update(ServerFinished, 0, ServerFinished.Length);
+
+ byte[] serverFinishedTranscriptHash = GetCurrentHash(prfHash);
+ Expect(serverFinishedTranscriptHash, "96 08 10 2a 0f 1c cc 6d b6 25 0b 7b 7e 41 7b 1a 00 0e aa da 3d aa e4 77 7a 76 86 c9 ff 83 df 13");
+
+ // {server} derive secret "tls13 c ap traffic":
+ {
+ string label = "c ap traffic";
+ c_ap_t = TlsCryptoUtilities.HkdfExpandLabel(master, hash, label, serverFinishedTranscriptHash, hashLen);
+ Expect(c_ap_t, "9e 40 64 6c e7 9a 7f 9d c0 5a f8 88 9b ce 65 52 87 5a fa 0b 06 df 00 87 f7 92 eb b7 c1 75 04 a5");
+ }
+
+ // {server} derive secret "tls13 s ap traffic":
+ {
+ string label = "s ap traffic";
+ s_ap_t = TlsCryptoUtilities.HkdfExpandLabel(master, hash, label, serverFinishedTranscriptHash, hashLen);
+ Expect(s_ap_t, "a1 1a f9 f0 55 31 f8 56 ad 47 11 6b 45 a9 50 32 82 04 b4 f4 4b fb 6b 3a 4b 4f 1f 3f cb 63 16 43");
+ }
+
+ // {server} derive secret "tls13 exp master":
+ {
+ string label = "exp master";
+ exp_master = TlsCryptoUtilities.HkdfExpandLabel(master, hash, label, serverFinishedTranscriptHash, hashLen);
+ Expect(exp_master, "fe 22 f8 81 17 6e da 18 eb 8f 44 52 9e 67 92 c5 0c 9a 3f 89 45 2f 68 d8 ae 31 1b 43 09 d3 cf 50");
+ }
+
+ // {server} derive write traffic keys for application data:
+ {
+ TlsSecret key = TlsCryptoUtilities.HkdfExpandLabel(s_ap_t, hash, "key", TlsUtilities.EmptyBytes, 16);
+ Expect(key, "9f 02 28 3b 6c 9c 07 ef c2 6b b9 f2 ac 92 e3 56");
+
+ TlsSecret iv = TlsCryptoUtilities.HkdfExpandLabel(s_ap_t, hash, "iv", TlsUtilities.EmptyBytes, 12);
+ Expect(iv, "cf 78 2b 88 dd 83 54 9a ad f1 e9 84");
+ }
+
+ // {server} derive read traffic keys for handshake data:
+ {
+ TlsSecret key = TlsCryptoUtilities.HkdfExpandLabel(c_hs_t, hash, "key", TlsUtilities.EmptyBytes, 16);
+ Expect(key, "db fa a6 93 d1 76 2c 5b 66 6a f5 d9 50 25 8d 01");
+
+ TlsSecret iv = TlsCryptoUtilities.HkdfExpandLabel(c_hs_t, hash, "iv", TlsUtilities.EmptyBytes, 12);
+ Expect(iv, "5b d3 c7 1b 83 6e 0b 76 bb 73 26 5f");
+ }
+
+ // {server} calculate (client) finished "tls13 finished":
+ {
+ TlsSecret expanded = TlsCryptoUtilities.HkdfExpandLabel(c_hs_t, hash, "finished", TlsUtilities.EmptyBytes, hashLen);
+ Expect(expanded, "b8 0a d0 10 15 fb 2f 0b d6 5f f7 d4 da 5d 6b f8 3f 84 82 1d 1f 87 fd c7 d3 c7 5b 5a 7b 42 d9 c4");
+
+ // TODO Mention this transcript hash in RFC 8448 data?
+ byte[] finished = CalculateHmac(hash, expanded, serverFinishedTranscriptHash);
+ Expect(finished, Hex("a8 ec 43 6d 67 76 34 ae 52 5a c1 fc eb e1 1a 03 9e c1 76 94 fa c6 e9 85 27 b6 42 f2 ed d5 ce 61"));
+ }
+
+ prfHash.Update(ClientFinished, 0, ClientFinished.Length);
+
+ byte[] clientFinishedTranscriptHash = GetCurrentHash(prfHash);
+ Expect(clientFinishedTranscriptHash, "20 91 45 a9 6e e8 e2 a1 22 ff 81 00 47 cc 95 26 84 65 8d 60 49 e8 64 29 42 6d b8 7c 54 ad 14 3d");
+
+ // {server} derive read traffic keys for application data:
+ {
+ TlsSecret key = TlsCryptoUtilities.HkdfExpandLabel(c_ap_t, hash, "key", TlsUtilities.EmptyBytes, 16);
+ Expect(key, "17 42 2d da 59 6e d5 d9 ac d8 90 e3 c6 3f 50 51");
+
+ TlsSecret iv = TlsCryptoUtilities.HkdfExpandLabel(c_ap_t, hash, "iv", TlsUtilities.EmptyBytes, 12);
+ Expect(iv, "5b 78 92 3d ee 08 57 90 33 e5 23 d9");
+ }
+
+ // {server} derive secret "tls13 res master":
+ {
+ res_master = TlsCryptoUtilities.HkdfExpandLabel(master, hash, "res master", clientFinishedTranscriptHash, hashLen);
+ Expect(res_master, "7d f2 35 f2 03 1d 2a 05 12 87 d0 2b 02 41 b0 bf da f8 6c c8 56 23 1f 2d 5a ba 46 c4 34 ec 19 6c");
+ }
+
+ // {server} generate resumption secret "tls13 resumption":
+ {
+ byte[] context = Hex("00 00");
+ TlsSecret expanded = TlsCryptoUtilities.HkdfExpandLabel(res_master, hash, "resumption", context, hashLen);
+ Expect(expanded, "4e cd 0e b6 ec 3b 4d 87 f5 d6 02 8f 92 2c a4 c5 85 1a 27 7f d4 13 11 c9 e6 2d 2c 94 92 e1 c4 f3");
+ }
+ }
+
+ [Test]
+ public void TestHkdfExpandLimit()
+ {
+ int[] hashes = new int[] { CryptoHashAlgorithm.md5, CryptoHashAlgorithm.sha1, CryptoHashAlgorithm.sha224,
+ CryptoHashAlgorithm.sha256, CryptoHashAlgorithm.sha384, CryptoHashAlgorithm.sha512,
+ CryptoHashAlgorithm.sm3 };
+
+ for (int i = 0; i < hashes.Length; ++i)
+ {
+ int hash = hashes[i];
+ int hashLen = TlsCryptoUtilities.GetHashOutputSize(hash);
+ byte[] zeroes = new byte[hashLen];
+
+ int limit = 255 * hashLen;
+
+ TlsSecret secret = m_crypto.HkdfInit(hash).HkdfExtract(hash, zeroes);
+
+ try
+ {
+ secret.HkdfExpand(hash, TlsUtilities.EmptyBytes, limit);
+ }
+ catch (Exception e)
+ {
+ Assert.Fail("Unexpected exception: " + e.Message);
+ }
+
+ try
+ {
+ secret.HkdfExpand(hash, TlsUtilities.EmptyBytes, limit + 1);
+ Assert.Fail("Expected an exception!");
+ }
+ catch (ArgumentException)
+ {
+ // Expected
+ }
+ catch (Exception e)
+ {
+ Assert.Fail("Unexpected exception: " + e.Message);
+ }
+ }
+ }
+
+ [Test]
+ public void TestSignaturesLegacy()
+ {
+ short[] signatureAlgorithms = new short[]{ SignatureAlgorithm.dsa, SignatureAlgorithm.ecdsa,
+ SignatureAlgorithm.rsa };
+
+ TlsCryptoParameters cryptoParams = new TestTlsCryptoParameters(ProtocolVersion.TLSv11);
+
+ for (int i = 0; i < signatureAlgorithms.Length; ++i)
+ {
+ short signatureAlgorithm = signatureAlgorithms[i];
+ if (!m_crypto.HasSignatureAlgorithm(signatureAlgorithm))
+ continue;
+
+ TlsCredentialedSigner credentialedSigner = LoadCredentialedSignerLegacy(cryptoParams,
+ signatureAlgorithm);
+ if (null != credentialedSigner)
+ {
+ ImplTestSignatureLegacy(credentialedSigner);
+ }
+ }
+ }
+
+ [Test]
+ public void TestSignatures12()
+ {
+ short[] hashAlgorithms = new short[]{ HashAlgorithm.md5, HashAlgorithm.sha1, HashAlgorithm.sha224,
+ HashAlgorithm.sha256, HashAlgorithm.sha384, HashAlgorithm.sha512 };
+ short[] signatureAlgorithms = new short[]{ SignatureAlgorithm.dsa, SignatureAlgorithm.ecdsa,
+ SignatureAlgorithm.rsa };
+
+ TlsCryptoParameters cryptoParams = new TestTlsCryptoParameters(ProtocolVersion.TLSv12);
+
+ for (int i = 0; i < signatureAlgorithms.Length; ++i)
+ {
+ short signatureAlgorithm = signatureAlgorithms[i];
+
+ for (int j = 0; j < hashAlgorithms.Length; ++j)
+ {
+ SignatureAndHashAlgorithm signatureAndHashAlgorithm = SignatureAndHashAlgorithm.GetInstance(
+ hashAlgorithms[j], signatureAlgorithm);
+ if (!m_crypto.HasSignatureAndHashAlgorithm(signatureAndHashAlgorithm))
+ continue;
+
+ TlsCredentialedSigner credentialedSigner = LoadCredentialedSigner12(cryptoParams,
+ signatureAndHashAlgorithm);
+ if (null != credentialedSigner)
+ {
+ ImplTestSignature12(credentialedSigner, signatureAndHashAlgorithm);
+ }
+ }
+ }
+
+ // Signature algorithms usable with HashAlgorithm.Intrinsic in TLS 1.2
+ short[] intrinsicSignatureAlgorithms = new short[] { SignatureAlgorithm.ed25519, SignatureAlgorithm.ed448,
+ SignatureAlgorithm.gostr34102012_256, SignatureAlgorithm.gostr34102012_512,
+ SignatureAlgorithm.rsa_pss_pss_sha256, SignatureAlgorithm.rsa_pss_pss_sha384,
+ SignatureAlgorithm.rsa_pss_pss_sha512, SignatureAlgorithm.rsa_pss_rsae_sha256,
+ SignatureAlgorithm.rsa_pss_rsae_sha384, SignatureAlgorithm.rsa_pss_rsae_sha512, };
+
+ for (int i = 0; i < intrinsicSignatureAlgorithms.Length; ++i)
+ {
+ SignatureAndHashAlgorithm signatureAndHashAlgorithm = SignatureAndHashAlgorithm.GetInstance(
+ HashAlgorithm.Intrinsic, intrinsicSignatureAlgorithms[i]);
+ if (!m_crypto.HasSignatureAndHashAlgorithm(signatureAndHashAlgorithm))
+ continue;
+
+ TlsCredentialedSigner credentialedSigner = LoadCredentialedSigner12(cryptoParams,
+ signatureAndHashAlgorithm);
+ if (null != credentialedSigner)
+ {
+ ImplTestSignature12(credentialedSigner, signatureAndHashAlgorithm);
+ }
+ }
+ }
+
+ [Test]
+ public void TestSignatures13()
+ {
+ int[] signatureSchemes = new int[] { SignatureScheme.ecdsa_brainpoolP256r1tls13_sha256,
+ SignatureScheme.ecdsa_brainpoolP384r1tls13_sha384, SignatureScheme.ecdsa_brainpoolP512r1tls13_sha512,
+ SignatureScheme.ecdsa_secp256r1_sha256, SignatureScheme.ecdsa_secp384r1_sha384,
+ SignatureScheme.ecdsa_secp521r1_sha512, SignatureScheme.ed25519, SignatureScheme.ed448,
+ SignatureScheme.rsa_pss_pss_sha256, SignatureScheme.rsa_pss_pss_sha384, SignatureScheme.rsa_pss_pss_sha512,
+ SignatureScheme.rsa_pss_rsae_sha256, SignatureScheme.rsa_pss_rsae_sha384,
+ SignatureScheme.rsa_pss_rsae_sha512, SignatureScheme.sm2sig_sm3,
+ // These are only used for certs in 1.3 (cert verification is not done by TlsCrypto)
+ //SignatureScheme.ecdsa_sha1, SignatureScheme.rsa_pkcs1_sha1, SignatureScheme.rsa_pkcs1_sha256,
+ //SignatureScheme.rsa_pkcs1_sha384, SignatureScheme.rsa_pkcs1_sha512,
+ };
+
+ TlsCryptoParameters cryptoParams = new TestTlsCryptoParameters(ProtocolVersion.TLSv13);
+
+ for (int i = 0; i < signatureSchemes.Length; ++i)
+ {
+ int signatureScheme = signatureSchemes[i];
+ if (!m_crypto.HasSignatureScheme(signatureScheme))
+ continue;
+
+ TlsCredentialedSigner credentialedSigner = LoadCredentialedSigner13(cryptoParams, signatureScheme);
+ if (null != credentialedSigner)
+ {
+ ImplTestSignature13(credentialedSigner, signatureScheme);
+ }
+ }
+ }
+
+ private byte[] CalculateHmac(int cryptoHashAlgorithm, TlsSecret hmacKey, byte[] hmacInput)
+ {
+ byte[] keyBytes = Extract(hmacKey);
+
+ TlsHmac hmac = m_crypto.CreateHmacForHash(cryptoHashAlgorithm);
+ hmac.SetKey(keyBytes, 0, keyBytes.Length);
+ hmac.Update(hmacInput, 0, hmacInput.Length);
+ return hmac.CalculateMac();
+ }
+
+ private void Expect(TlsSecret secret, string expectedHex)
+ {
+ Expect(Extract(secret), Hex(expectedHex));
+ }
+
+ private void Expect(byte[] actualOctets, string expectedHex)
+ {
+ Expect(actualOctets, Hex(expectedHex));
+ }
+
+ private void Expect(byte[] actualOctets, byte[] expectedOctets)
+ {
+ AssertArrayEquals(actualOctets, expectedOctets);
+ }
+
+ private byte[] Extract(TlsSecret secret)
+ {
+ return m_crypto.AdoptSecret(secret).Extract();
+ }
+
+ private byte[] GetCurrentHash(TlsHash hash)
+ {
+ return hash.CloneHash().CalculateHash();
+ }
+
+ private static void AssertArrayEquals(byte[] a, byte[] b)
+ {
+ Assert.IsTrue(Arrays.AreEqual(a, b));
+ }
+
+ private static byte[] Hex(string s)
+ {
+ return Utilities.Encoders.Hex.Decode(s.Replace(" ", ""));
+ }
+
+ private void ImplTestAgreement(TlsAgreement aA, TlsAgreement aB)
+ {
+ byte[] pA = aA.GenerateEphemeral();
+ byte[] pB = aB.GenerateEphemeral();
+
+ aA.ReceivePeerValue(pB);
+ aB.ReceivePeerValue(pA);
+
+ TlsSecret sA = aA.CalculateSecret();
+ TlsSecret sB = aB.CalculateSecret();
+
+ AssertArrayEquals(Extract(sA), Extract(sB));
+ }
+
+ private void ImplTestDHDomain(TlsDHConfig dhConfig)
+ {
+ int namedGroup = dhConfig.NamedGroup;
+ int bits = namedGroup >= 0
+ ? NamedGroup.GetFiniteFieldBits(namedGroup)
+ : dhConfig.ExplicitGroup.P.BitLength;
+
+ int rounds = System.Math.Max(2, 11 - (bits >> 10));
+
+ TlsDHDomain d = m_crypto.CreateDHDomain(dhConfig);
+
+ for (int i = 0; i < rounds; ++i)
+ {
+ TlsAgreement aA = d.CreateDH();
+ TlsAgreement aB = d.CreateDH();
+
+ ImplTestAgreement(aA, aB);
+ }
+ }
+
+ private void ImplTestECDomain(TlsECConfig ecConfig)
+ {
+ int bits = NamedGroup.GetCurveBits(ecConfig.NamedGroup);
+ int rounds = System.Math.Max(2, 12 - (bits >> 6));
+
+ TlsECDomain d = m_crypto.CreateECDomain(ecConfig);
+
+ for (int i = 0; i < rounds; ++i)
+ {
+ TlsAgreement aA = d.CreateECDH();
+ TlsAgreement aB = d.CreateECDH();
+
+ ImplTestAgreement(aA, aB);
+ }
+ }
+
+ private void ImplTestSignatureLegacy(TlsCredentialedSigner credentialedSigner)
+ {
+ byte[] message = m_crypto.CreateNonceGenerator(TlsUtilities.EmptyBytes).GenerateNonce(100);
+
+ byte[] signature;
+ TlsStreamSigner tlsStreamSigner = credentialedSigner.GetStreamSigner();
+ if (null != tlsStreamSigner)
+ {
+ Stream output = tlsStreamSigner.GetOutputStream();
+ output.Write(message, 0, message.Length);
+ signature = tlsStreamSigner.GetSignature();
+ }
+ else
+ {
+ TlsHash tlsHash = new CombinedHash(m_crypto);
+ tlsHash.Update(message, 0, message.Length);
+ byte[] hash = tlsHash.CalculateHash();
+ signature = credentialedSigner.GenerateRawSignature(hash);
+ }
+
+ DigitallySigned digitallySigned = new DigitallySigned(null, signature);
+
+ TlsCertificate tlsCertificate = credentialedSigner.Certificate.GetCertificateAt(0);
+ TlsVerifier tlsVerifier = tlsCertificate.CreateVerifier(tlsCertificate.GetLegacySignatureAlgorithm());
+
+ bool verified;
+ TlsStreamVerifier tlsStreamVerifier = tlsVerifier.GetStreamVerifier(digitallySigned);
+ if (null != tlsStreamVerifier)
+ {
+ Stream output = tlsStreamVerifier.GetOutputStream();
+ output.Write(message, 0, message.Length);
+ verified = tlsStreamVerifier.IsVerified();
+ }
+ else
+ {
+ TlsHash tlsHash = new CombinedHash(m_crypto);
+ tlsHash.Update(message, 0, message.Length);
+ byte[] hash = tlsHash.CalculateHash();
+ verified = tlsVerifier.VerifyRawSignature(digitallySigned, hash);
+ }
+
+ Assert.IsTrue(verified);
+ }
+
+ private void ImplTestSignature12(TlsCredentialedSigner credentialedSigner,
+ SignatureAndHashAlgorithm signatureAndHashAlgorithm)
+ {
+ short hashAlgorithm = signatureAndHashAlgorithm.Hash;
+
+ byte[] message = m_crypto.CreateNonceGenerator(TlsUtilities.EmptyBytes).GenerateNonce(100);
+
+ byte[] signature;
+ TlsStreamSigner tlsStreamSigner = credentialedSigner.GetStreamSigner();
+ if (null != tlsStreamSigner)
+ {
+ Stream output = tlsStreamSigner.GetOutputStream();
+ output.Write(message, 0, message.Length);
+ signature = tlsStreamSigner.GetSignature();
+ }
+ else
+ {
+ // Currently 1.2 relies on these being handled by stream signers
+ Assert.IsTrue(HashAlgorithm.Intrinsic != hashAlgorithm);
+
+ int cryptoHashAlgorithm = TlsCryptoUtilities.GetHash(hashAlgorithm);
+
+ TlsHash tlsHash = m_crypto.CreateHash(cryptoHashAlgorithm);
+ tlsHash.Update(message, 0, message.Length);
+ byte[] hash = tlsHash.CalculateHash();
+ signature = credentialedSigner.GenerateRawSignature(hash);
+ }
+
+ DigitallySigned digitallySigned = new DigitallySigned(signatureAndHashAlgorithm, signature);
+
+ TlsCertificate tlsCertificate = credentialedSigner.Certificate.GetCertificateAt(0);
+ TlsVerifier tlsVerifier = tlsCertificate.CreateVerifier(signatureAndHashAlgorithm.Signature);
+
+ bool verified;
+ TlsStreamVerifier tlsStreamVerifier = tlsVerifier.GetStreamVerifier(digitallySigned);
+ if (null != tlsStreamVerifier)
+ {
+ Stream output = tlsStreamVerifier.GetOutputStream();
+ output.Write(message, 0, message.Length);
+ verified = tlsStreamVerifier.IsVerified();
+ }
+ else
+ {
+ // Currently 1.2 relies on these being handled by stream verifiers
+ Assert.IsTrue(HashAlgorithm.Intrinsic != hashAlgorithm);
+
+ int cryptoHashAlgorithm = TlsCryptoUtilities.GetHash(hashAlgorithm);
+
+ TlsHash tlsHash = m_crypto.CreateHash(cryptoHashAlgorithm);
+ tlsHash.Update(message, 0, message.Length);
+ byte[] hash = tlsHash.CalculateHash();
+ verified = tlsVerifier.VerifyRawSignature(digitallySigned, hash);
+ }
+
+ Assert.IsTrue(verified);
+ }
+
+ private void ImplTestSignature13(TlsCredentialedSigner credentialedSigner, int signatureScheme)
+ {
+ byte[] message = m_crypto.CreateNonceGenerator(TlsUtilities.EmptyBytes).GenerateNonce(100);
+
+ byte[] signature;
+ TlsStreamSigner tlsStreamSigner = credentialedSigner.GetStreamSigner();
+ if (null != tlsStreamSigner)
+ {
+ Stream output = tlsStreamSigner.GetOutputStream();
+ output.Write(message, 0, message.Length);
+ signature = tlsStreamSigner.GetSignature();
+ }
+ else
+ {
+ int cryptoHashAlgorithm = SignatureScheme.GetCryptoHashAlgorithm(signatureScheme);
+
+ TlsHash tlsHash = m_crypto.CreateHash(cryptoHashAlgorithm);
+ tlsHash.Update(message, 0, message.Length);
+ byte[] hash = tlsHash.CalculateHash();
+ signature = credentialedSigner.GenerateRawSignature(hash);
+ }
+
+ DigitallySigned digitallySigned = new DigitallySigned(
+ SignatureScheme.GetSignatureAndHashAlgorithm(signatureScheme), signature);
+
+ TlsCertificate tlsCertificate = credentialedSigner.Certificate.GetCertificateAt(0);
+ TlsVerifier tlsVerifier = tlsCertificate.CreateVerifier(signatureScheme);
+
+ bool verified;
+ TlsStreamVerifier tlsStreamVerifier = tlsVerifier.GetStreamVerifier(digitallySigned);
+ if (null != tlsStreamVerifier)
+ {
+ Stream output = tlsStreamVerifier.GetOutputStream();
+ output.Write(message, 0, message.Length);
+ verified = tlsStreamVerifier.IsVerified();
+ }
+ else
+ {
+ int cryptoHashAlgorithm = SignatureScheme.GetCryptoHashAlgorithm(signatureScheme);
+
+ TlsHash tlsHash = m_crypto.CreateHash(cryptoHashAlgorithm);
+ tlsHash.Update(message, 0, message.Length);
+ byte[] hash = tlsHash.CalculateHash();
+ verified = tlsVerifier.VerifyRawSignature(digitallySigned, hash);
+ }
+
+ Assert.IsTrue(verified);
+ }
+
+ private class TestTlsCryptoParameters
+ : TlsCryptoParameters
+ {
+ private readonly ProtocolVersion m_serverVersion;
+
+ internal TestTlsCryptoParameters(ProtocolVersion serverVersion)
+ : base(null)
+ {
+ this.m_serverVersion = serverVersion;
+ }
+
+ public override ProtocolVersion ServerVersion
+ {
+ get { return m_serverVersion; }
+ }
+ }
+
+ private class TestTlsDHGroupVerifier
+ : DefaultTlsDHGroupVerifier
+ {
+ internal IList Groups
+ {
+ get { return m_groups; }
+ }
+ }
+ }
+}
diff --git a/crypto/test/src/tls/test/ByteQueueInputStreamTest.cs b/crypto/test/src/tls/test/ByteQueueInputStreamTest.cs
new file mode 100644
index 000000000..9edb3109b
--- /dev/null
+++ b/crypto/test/src/tls/test/ByteQueueInputStreamTest.cs
@@ -0,0 +1,134 @@
+using System;
+
+using NUnit.Framework;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Tls.Tests
+{
+ [TestFixture]
+ public class ByteQueueInputStreamTest
+ {
+ [Test]
+ public void TestAvailable()
+ {
+ ByteQueueInputStream input = new ByteQueueInputStream();
+
+ // buffer is empty
+ Assert.AreEqual(0, input.Available);
+
+ // after adding once
+ input.AddBytes(new byte[10]);
+ Assert.AreEqual(10, input.Available);
+
+ // after adding more than once
+ input.AddBytes(new byte[5]);
+ Assert.AreEqual(15, input.Available);
+
+ // after reading a single byte
+ input.ReadByte();
+ Assert.AreEqual(14, input.Available);
+
+ // after reading into a byte array
+ input.Read(new byte[4], 0, 4);
+ Assert.AreEqual(10, input.Available);
+
+ input.Close();
+ }
+
+ [Test]
+ public void TestSkip()
+ {
+ ByteQueueInputStream input = new ByteQueueInputStream();
+
+ // skip when buffer is empty
+ Assert.AreEqual(0, input.Skip(10));
+
+ // skip equal to available
+ input.AddBytes(new byte[2]);
+ Assert.AreEqual(2, input.Skip(2));
+ Assert.AreEqual(0, input.Available);
+
+ // skip less than available
+ input.AddBytes(new byte[10]);
+ Assert.AreEqual(5, input.Skip(5));
+ Assert.AreEqual(5, input.Available);
+
+ // skip more than available
+ Assert.AreEqual(5, input.Skip(20));
+ Assert.AreEqual(0, input.Available);
+
+ input.Close();
+ }
+
+ [Test]
+ public void TestRead()
+ {
+ ByteQueueInputStream input = new ByteQueueInputStream();
+ input.AddBytes(new byte[]{ 0x01, 0x02 });
+ input.AddBytes(new byte[]{ 0x03 });
+
+ Assert.AreEqual(0x01, input.ReadByte());
+ Assert.AreEqual(0x02, input.ReadByte());
+ Assert.AreEqual(0x03, input.ReadByte());
+ Assert.AreEqual(-1, input.ReadByte());
+
+ input.Close();
+ }
+
+ [Test]
+ public void TestReadArray()
+ {
+ ByteQueueInputStream input = new ByteQueueInputStream();
+ input.AddBytes(new byte[]{ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 });
+
+ byte[] buffer = new byte[5];
+
+ // read less than available into specified position
+ Assert.AreEqual(1, input.Read(buffer, 2, 1));
+ AssertArrayEquals(new byte[]{ 0x00, 0x00, 0x01, 0x00, 0x00 }, buffer);
+
+ // read equal to available
+ Assert.AreEqual(5, input.Read(buffer, 0, buffer.Length));
+ AssertArrayEquals(new byte[]{ 0x02, 0x03, 0x04, 0x05, 0x06 }, buffer);
+
+ // read more than available
+ input.AddBytes(new byte[]{ 0x01, 0x02, 0x03 });
+ Assert.AreEqual(3, input.Read(buffer, 0, buffer.Length));
+ AssertArrayEquals(new byte[]{ 0x01, 0x02, 0x03, 0x05, 0x06 }, buffer);
+
+ input.Close();
+ }
+
+ [Test]
+ public void TestPeek()
+ {
+ ByteQueueInputStream input = new ByteQueueInputStream();
+
+ byte[] buffer = new byte[5];
+
+ // peek more than available
+ Assert.AreEqual(0, input.Peek(buffer));
+ AssertArrayEquals(new byte[]{ 0x00, 0x00, 0x00, 0x00, 0x00 }, buffer);
+
+ // peek less than available
+ input.AddBytes(new byte[]{ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 });
+ Assert.AreEqual(5, input.Peek(buffer));
+ AssertArrayEquals(new byte[]{ 0x01, 0x02, 0x03, 0x04, 0x05 }, buffer);
+ Assert.AreEqual(6, input.Available);
+
+ // peek equal to available
+ input.ReadByte();
+ Assert.AreEqual(5, input.Peek(buffer));
+ AssertArrayEquals(new byte[]{ 0x02, 0x03, 0x04, 0x05, 0x06 }, buffer);
+ Assert.AreEqual(5, input.Available);
+
+ input.Close();
+ }
+
+ private static void AssertArrayEquals(byte[] a, byte[] b)
+ {
+ Assert.IsTrue(Arrays.AreEqual(a, b));
+ }
+ }
+}
diff --git a/crypto/test/src/tls/test/CertChainUtilities.cs b/crypto/test/src/tls/test/CertChainUtilities.cs
new file mode 100644
index 000000000..8df8e48bc
--- /dev/null
+++ b/crypto/test/src/tls/test/CertChainUtilities.cs
@@ -0,0 +1,123 @@
+using System;
+using System.Threading;
+
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Operators;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.X509;
+using Org.BouncyCastle.X509.Extension;
+
+namespace Org.BouncyCastle.Tls.Tests
+{
+ public class CertChainUtilities
+ {
+ private static readonly SecureRandom Random = new SecureRandom();
+
+ private static long serialNumber = 1L;
+
+ /// <summary>We generate the CA's certificate.</summary>
+ public static X509Certificate CreateMasterCert(string rootDN, AsymmetricCipherKeyPair keyPair)
+ {
+ //
+ // create the certificate - version 1
+ //
+ X509V1CertificateGenerator gen = new X509V1CertificateGenerator();
+ gen.SetIssuerDN(new X509Name(rootDN));
+ gen.SetSerialNumber(BigInteger.ValueOf(Interlocked.Increment(ref serialNumber)));
+ gen.SetNotBefore(DateTime.UtcNow.AddDays(-30));
+ gen.SetNotAfter(DateTime.UtcNow.AddDays(30));
+ gen.SetSubjectDN(new X509Name(rootDN));
+ gen.SetPublicKey(keyPair.Public);
+
+ return SignV1(gen, keyPair.Private);
+ }
+
+ /// <summary>We generate an intermediate certificate signed by our CA.</summary>
+ public static X509Certificate CreateIntermediateCert(string interDN, AsymmetricKeyParameter pubKey,
+ AsymmetricKeyParameter caPrivKey, X509Certificate caCert)
+ {
+ //
+ // create the certificate - version 3
+ //
+ X509V3CertificateGenerator gen = new X509V3CertificateGenerator();
+ gen.SetIssuerDN(caCert.SubjectDN);
+ gen.SetSerialNumber(BigInteger.ValueOf(Interlocked.Increment(ref serialNumber)));
+ gen.SetNotBefore(DateTime.UtcNow.AddDays(-30));
+ gen.SetNotAfter(DateTime.UtcNow.AddDays(30));
+ gen.SetSubjectDN(new X509Name(interDN));
+ gen.SetPublicKey(pubKey);
+
+ //
+ // extensions
+ //
+ gen.AddExtension(X509Extensions.SubjectKeyIdentifier, false,
+ new SubjectKeyIdentifierStructure(pubKey));
+ gen.AddExtension(X509Extensions.AuthorityKeyIdentifier, false,
+ new AuthorityKeyIdentifierStructure(caCert));
+ gen.AddExtension(X509Extensions.BasicConstraints, true,
+ new BasicConstraints(0));
+
+ return SignV3(gen, caPrivKey);
+ }
+
+ /// <summary>We generate a certificate signed by our CA's intermediate certificate.</summary>
+ public static X509Certificate CreateEndEntityCert(string endEntityDN, AsymmetricKeyParameter pubKey,
+ AsymmetricKeyParameter caPrivKey, X509Certificate caCert)
+ {
+ X509V3CertificateGenerator gen = CreateBaseEndEntityGenerator(endEntityDN, pubKey, caCert);
+
+ return SignV3(gen, caPrivKey);
+ }
+
+ /// <summary>We generate a certificate signed by our CA's intermediate certificate with ExtendedKeyUsage
+ /// extension.</summary>
+ public static X509Certificate CreateEndEntityCert(string endEntityDN, AsymmetricKeyParameter pubKey,
+ AsymmetricKeyParameter caPrivKey, X509Certificate caCert, KeyPurposeID keyPurposeID)
+ {
+ X509V3CertificateGenerator gen = CreateBaseEndEntityGenerator(endEntityDN, pubKey, caCert);
+
+ gen.AddExtension(X509Extensions.ExtendedKeyUsage, true, new ExtendedKeyUsage(keyPurposeID));
+
+ return SignV3(gen, caPrivKey);
+ }
+
+ private static X509V3CertificateGenerator CreateBaseEndEntityGenerator(string endEntityDN,
+ AsymmetricKeyParameter pubKey, X509Certificate caCert)
+ {
+ //
+ // create the certificate - version 3
+ //
+ X509V3CertificateGenerator gen = new X509V3CertificateGenerator();
+ gen.SetIssuerDN(caCert.SubjectDN);
+ gen.SetSerialNumber(BigInteger.ValueOf(Interlocked.Increment(ref serialNumber)));
+ gen.SetNotBefore(DateTime.UtcNow.AddDays(-30));
+ gen.SetNotAfter(DateTime.UtcNow.AddDays(30));
+ gen.SetSubjectDN(new X509Name(endEntityDN));
+ gen.SetPublicKey(pubKey);
+
+ //
+ // add the extensions
+ //
+ gen.AddExtension(X509Extensions.SubjectKeyIdentifier, false,
+ new SubjectKeyIdentifierStructure(pubKey));
+ gen.AddExtension(X509Extensions.AuthorityKeyIdentifier, false,
+ new AuthorityKeyIdentifierStructure(caCert.GetPublicKey()));
+ gen.AddExtension(X509Extensions.BasicConstraints, true,
+ new BasicConstraints(false));
+
+ return gen;
+ }
+
+ private static X509Certificate SignV1(X509V1CertificateGenerator gen, AsymmetricKeyParameter caPrivKey)
+ {
+ return gen.Generate(new Asn1SignatureFactory("SHA256withRSA", caPrivKey, Random));
+ }
+
+ private static X509Certificate SignV3(X509V3CertificateGenerator gen, AsymmetricKeyParameter caPrivKey)
+ {
+ return gen.Generate(new Asn1SignatureFactory("SHA256withRSA", caPrivKey, Random));
+ }
+ }
+}
diff --git a/crypto/test/src/tls/test/DtlsProtocolTest.cs b/crypto/test/src/tls/test/DtlsProtocolTest.cs
new file mode 100644
index 000000000..388003666
--- /dev/null
+++ b/crypto/test/src/tls/test/DtlsProtocolTest.cs
@@ -0,0 +1,102 @@
+using System;
+using System.Threading;
+
+using NUnit.Framework;
+
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Tls.Tests
+{
+ [TestFixture]
+ public class DtlsProtocolTest
+ {
+ [Test]
+ public void TestClientServer()
+ {
+ SecureRandom secureRandom = new SecureRandom();
+
+ DtlsClientProtocol clientProtocol = new DtlsClientProtocol();
+ DtlsServerProtocol serverProtocol = new DtlsServerProtocol();
+
+ MockDatagramAssociation network = new MockDatagramAssociation(1500);
+
+ Server server = new Server(serverProtocol, network.Server);
+
+ Thread serverThread = new Thread(new ThreadStart(server.Run));
+ serverThread.Start();
+
+ DatagramTransport clientTransport = network.Client;
+
+ clientTransport = new UnreliableDatagramTransport(clientTransport, secureRandom, 0, 0);
+
+ clientTransport = new LoggingDatagramTransport(clientTransport, Console.Out);
+
+ MockDtlsClient client = new MockDtlsClient(null);
+
+ DtlsTransport dtlsClient = clientProtocol.Connect(client, clientTransport);
+
+ for (int i = 1; i <= 10; ++i)
+ {
+ byte[] data = new byte[i];
+ Arrays.Fill(data, (byte)i);
+ dtlsClient.Send(data, 0, data.Length);
+ }
+
+ byte[] buf = new byte[dtlsClient.GetReceiveLimit()];
+ while (dtlsClient.Receive(buf, 0, buf.Length, 100) >= 0)
+ {
+ }
+
+ dtlsClient.Close();
+
+ server.Shutdown(serverThread);
+ }
+
+ internal class Server
+ {
+ private readonly DtlsServerProtocol m_serverProtocol;
+ private readonly DatagramTransport m_serverTransport;
+ private volatile bool m_isShutdown = false;
+
+ internal Server(DtlsServerProtocol serverProtocol, DatagramTransport serverTransport)
+ {
+ this.m_serverProtocol = serverProtocol;
+ this.m_serverTransport = serverTransport;
+ }
+
+ public void Run()
+ {
+ try
+ {
+ MockDtlsServer server = new MockDtlsServer();
+ DtlsTransport dtlsServer = m_serverProtocol.Accept(server, m_serverTransport);
+ byte[] buf = new byte[dtlsServer.GetReceiveLimit()];
+ while (!m_isShutdown)
+ {
+ int length = dtlsServer.Receive(buf, 0, buf.Length, 1000);
+ if (length >= 0)
+ {
+ dtlsServer.Send(buf, 0, length);
+ }
+ }
+ dtlsServer.Close();
+ }
+ catch (Exception e)
+ {
+ Console.Error.WriteLine(e);
+ Console.Error.Flush();
+ }
+ }
+
+ internal void Shutdown(Thread serverThread)
+ {
+ if (!m_isShutdown)
+ {
+ this.m_isShutdown = true;
+ serverThread.Join();
+ }
+ }
+ }
+ }
+}
diff --git a/crypto/test/src/tls/test/DtlsPskProtocolTest.cs b/crypto/test/src/tls/test/DtlsPskProtocolTest.cs
new file mode 100644
index 000000000..0da6cb661
--- /dev/null
+++ b/crypto/test/src/tls/test/DtlsPskProtocolTest.cs
@@ -0,0 +1,102 @@
+using System;
+using System.Threading;
+
+using NUnit.Framework;
+
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Tls.Tests
+{
+ [TestFixture]
+ public class DlsPskProtocolTest
+ {
+ [Test]
+ public void TestClientServer()
+ {
+ SecureRandom secureRandom = new SecureRandom();
+
+ DtlsClientProtocol clientProtocol = new DtlsClientProtocol();
+ DtlsServerProtocol serverProtocol = new DtlsServerProtocol();
+
+ MockDatagramAssociation network = new MockDatagramAssociation(1500);
+
+ Server server = new Server(serverProtocol, network.Server);
+
+ Thread serverThread = new Thread(new ThreadStart(server.Run));
+ serverThread.Start();
+
+ DatagramTransport clientTransport = network.Client;
+
+ clientTransport = new UnreliableDatagramTransport(clientTransport, secureRandom, 0, 0);
+
+ clientTransport = new LoggingDatagramTransport(clientTransport, Console.Out);
+
+ MockPskDtlsClient client = new MockPskDtlsClient(null);
+
+ DtlsTransport dtlsClient = clientProtocol.Connect(client, clientTransport);
+
+ for (int i = 1; i <= 10; ++i)
+ {
+ byte[] data = new byte[i];
+ Arrays.Fill(data, (byte)i);
+ dtlsClient.Send(data, 0, data.Length);
+ }
+
+ byte[] buf = new byte[dtlsClient.GetReceiveLimit()];
+ while (dtlsClient.Receive(buf, 0, buf.Length, 100) >= 0)
+ {
+ }
+
+ dtlsClient.Close();
+
+ server.Shutdown(serverThread);
+ }
+
+ internal class Server
+ {
+ private readonly DtlsServerProtocol m_serverProtocol;
+ private readonly DatagramTransport m_serverTransport;
+ private volatile bool m_isShutdown = false;
+
+ internal Server(DtlsServerProtocol serverProtocol, DatagramTransport serverTransport)
+ {
+ this.m_serverProtocol = serverProtocol;
+ this.m_serverTransport = serverTransport;
+ }
+
+ public void Run()
+ {
+ try
+ {
+ MockPskDtlsServer server = new MockPskDtlsServer();
+ DtlsTransport dtlsServer = m_serverProtocol.Accept(server, m_serverTransport);
+ byte[] buf = new byte[dtlsServer.GetReceiveLimit()];
+ while (!m_isShutdown)
+ {
+ int length = dtlsServer.Receive(buf, 0, buf.Length, 1000);
+ if (length >= 0)
+ {
+ dtlsServer.Send(buf, 0, length);
+ }
+ }
+ dtlsServer.Close();
+ }
+ catch (Exception e)
+ {
+ Console.Error.WriteLine(e);
+ Console.Error.Flush();
+ }
+ }
+
+ internal void Shutdown(Thread serverThread)
+ {
+ if (!m_isShutdown)
+ {
+ this.m_isShutdown = true;
+ serverThread.Join();
+ }
+ }
+ }
+ }
+}
diff --git a/crypto/test/src/tls/test/DtlsTestCase.cs b/crypto/test/src/tls/test/DtlsTestCase.cs
new file mode 100644
index 000000000..d93f17c27
--- /dev/null
+++ b/crypto/test/src/tls/test/DtlsTestCase.cs
@@ -0,0 +1,164 @@
+using System;
+using System.Threading;
+
+using NUnit.Framework;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Tls.Tests
+{
+ [TestFixture]
+ public class DtlsTestCase
+ {
+ private static void CheckDtlsVersions(ProtocolVersion[] versions)
+ {
+ if (versions != null)
+ {
+ for (int i = 0; i < versions.Length; ++i)
+ {
+ if (!versions[i].IsDtls)
+ throw new InvalidOperationException("Non-DTLS version");
+ }
+ }
+ }
+
+ [Test, TestCaseSource(typeof(DtlsTestSuite), "Suite")]
+ public void RunTest(TlsTestConfig config)
+ {
+ CheckDtlsVersions(config.clientSupportedVersions);
+ CheckDtlsVersions(config.serverSupportedVersions);
+
+ DtlsTestClientProtocol clientProtocol = new DtlsTestClientProtocol(config);
+ DtlsTestServerProtocol serverProtocol = new DtlsTestServerProtocol(config);
+
+ MockDatagramAssociation network = new MockDatagramAssociation(1500);
+
+ TlsTestClientImpl clientImpl = new TlsTestClientImpl(config);
+ TlsTestServerImpl serverImpl = new TlsTestServerImpl(config);
+
+ Server server = new Server(this, serverProtocol, network.Server, serverImpl);
+
+ Thread serverThread = new Thread(new ThreadStart(server.Run));
+ serverThread.Start();
+
+ Exception caught = null;
+ try
+ {
+ DatagramTransport clientTransport = network.Client;
+
+ if (TlsTestConfig.Debug)
+ {
+ clientTransport = new LoggingDatagramTransport(clientTransport, Console.Out);
+ }
+
+ DtlsTransport dtlsClient = clientProtocol.Connect(clientImpl, clientTransport);
+
+ for (int i = 1; i <= 10; ++i)
+ {
+ byte[] data = new byte[i];
+ Arrays.Fill(data, (byte)i);
+ dtlsClient.Send(data, 0, data.Length);
+ }
+
+ byte[] buf = new byte[dtlsClient.GetReceiveLimit()];
+ while (dtlsClient.Receive(buf, 0, buf.Length, 100) >= 0)
+ {
+ }
+
+ dtlsClient.Close();
+ }
+ catch (Exception e)
+ {
+ caught = e;
+ LogException(caught);
+ }
+
+ server.Shutdown(serverThread);
+
+ // TODO Add checks that the various streams were closed
+
+ Assert.AreEqual(config.expectFatalAlertConnectionEnd, clientImpl.FirstFatalAlertConnectionEnd,
+ "Client fatal alert connection end");
+ Assert.AreEqual(config.expectFatalAlertConnectionEnd, serverImpl.FirstFatalAlertConnectionEnd,
+ "Server fatal alert connection end");
+
+ Assert.AreEqual(config.expectFatalAlertDescription, clientImpl.FirstFatalAlertDescription,
+ "Client fatal alert description");
+ Assert.AreEqual(config.expectFatalAlertDescription, serverImpl.FirstFatalAlertDescription,
+ "Server fatal alert description");
+
+ if (config.expectFatalAlertConnectionEnd == -1)
+ {
+ Assert.IsNull(caught, "Unexpected client exception");
+ Assert.IsNull(server.Caught, "Unexpected server exception");
+ }
+ }
+
+ protected void LogException(Exception e)
+ {
+ if (TlsTestConfig.Debug)
+ {
+ Console.Error.WriteLine(e);
+ Console.Error.Flush();
+ }
+ }
+
+ internal class Server
+ {
+ private readonly DtlsTestCase m_outer;
+ private readonly DtlsTestServerProtocol m_serverProtocol;
+ private readonly DatagramTransport m_serverTransport;
+ private readonly TlsTestServerImpl m_serverImpl;
+
+ private volatile bool m_isShutdown = false;
+ private Exception m_caught = null;
+
+ internal Server(DtlsTestCase outer, DtlsTestServerProtocol serverProtocol,
+ DatagramTransport serverTransport, TlsTestServerImpl serverImpl)
+ {
+ this.m_outer = outer;
+ this.m_serverProtocol = serverProtocol;
+ this.m_serverTransport = serverTransport;
+ this.m_serverImpl = serverImpl;
+ }
+
+ public void Run()
+ {
+ try
+ {
+ DtlsTransport dtlsServer = m_serverProtocol.Accept(m_serverImpl, m_serverTransport);
+ byte[] buf = new byte[dtlsServer.GetReceiveLimit()];
+ while (!m_isShutdown)
+ {
+ int length = dtlsServer.Receive(buf, 0, buf.Length, 100);
+ if (length >= 0)
+ {
+ dtlsServer.Send(buf, 0, length);
+ }
+ }
+ dtlsServer.Close();
+ }
+ catch (Exception e)
+ {
+ this.m_caught = e;
+ m_outer.LogException(m_caught);
+ }
+ }
+
+ internal void Shutdown(Thread serverThread)
+ {
+ if (!m_isShutdown)
+ {
+ this.m_isShutdown = true;
+ //serverThread.Interrupt();
+ serverThread.Join();
+ }
+ }
+
+ internal Exception Caught
+ {
+ get { return m_caught; }
+ }
+ }
+ }
+}
diff --git a/crypto/test/src/tls/test/DtlsTestClientProtocol.cs b/crypto/test/src/tls/test/DtlsTestClientProtocol.cs
new file mode 100644
index 000000000..99a7fa07b
--- /dev/null
+++ b/crypto/test/src/tls/test/DtlsTestClientProtocol.cs
@@ -0,0 +1,27 @@
+using System;
+
+namespace Org.BouncyCastle.Tls.Tests
+{
+ internal class DtlsTestClientProtocol
+ : DtlsClientProtocol
+ {
+ protected readonly TlsTestConfig m_config;
+
+ public DtlsTestClientProtocol(TlsTestConfig config)
+ : base()
+ {
+ this.m_config = config;
+ }
+
+ protected override byte[] GenerateCertificateVerify(ClientHandshakeState state,
+ DigitallySigned certificateVerify)
+ {
+ if (certificateVerify.Algorithm != null && m_config.clientAuthSigAlgClaimed != null)
+ {
+ certificateVerify = new DigitallySigned(m_config.clientAuthSigAlgClaimed, certificateVerify.Signature);
+ }
+
+ return base.GenerateCertificateVerify(state, certificateVerify);
+ }
+ }
+}
diff --git a/crypto/test/src/tls/test/DtlsTestServerProtocol.cs b/crypto/test/src/tls/test/DtlsTestServerProtocol.cs
new file mode 100644
index 000000000..08fc2b6a9
--- /dev/null
+++ b/crypto/test/src/tls/test/DtlsTestServerProtocol.cs
@@ -0,0 +1,16 @@
+using System;
+
+namespace Org.BouncyCastle.Tls.Tests
+{
+ internal class DtlsTestServerProtocol
+ : DtlsServerProtocol
+ {
+ protected readonly TlsTestConfig m_config;
+
+ public DtlsTestServerProtocol(TlsTestConfig config)
+ : base()
+ {
+ this.m_config = config;
+ }
+ }
+}
diff --git a/crypto/test/src/tls/test/DtlsTestSuite.cs b/crypto/test/src/tls/test/DtlsTestSuite.cs
new file mode 100644
index 000000000..0af2be32c
--- /dev/null
+++ b/crypto/test/src/tls/test/DtlsTestSuite.cs
@@ -0,0 +1,260 @@
+using System;
+using System.Collections;
+
+using NUnit.Framework;
+
+namespace Org.BouncyCastle.Tls.Tests
+{
+ public class DtlsTestSuite
+ {
+ // Make the access to constants less verbose
+ internal class C : TlsTestConfig {}
+
+ public DtlsTestSuite()
+ {
+ }
+
+ public static IEnumerable Suite()
+ {
+ IList testSuite = new ArrayList();
+
+ AddFallbackTests(testSuite);
+ AddVersionTests(testSuite, ProtocolVersion.DTLSv10);
+ AddVersionTests(testSuite, ProtocolVersion.DTLSv12);
+
+ return testSuite;
+ }
+
+ private static void AddFallbackTests(IList testSuite)
+ {
+ {
+ TlsTestConfig c = CreateDtlsTestConfig(ProtocolVersion.DTLSv12);
+ c.clientFallback = true;
+
+ AddTestCase(testSuite, c, "FallbackGood");
+ }
+
+ /*
+ * NOTE: Temporarily disabled automatic test runs because of problems getting a clean exit
+ * of the DTLS server after a fatal alert. As of writing, manual runs show the correct
+ * alerts being raised
+ */
+
+#if false
+ {
+ TlsTestConfig c = CreateDtlsTestConfig(ProtocolVersion.DTLSv12);
+ c.clientFallback = true;
+ c.clientSupportedVersions = ProtocolVersion.DTLSv10.Only();
+ c.ExpectServerFatalAlert(AlertDescription.inappropriate_fallback);
+
+ AddTestCase(testSuite, c, "FallbackBad");
+ }
+#endif
+
+ {
+ TlsTestConfig c = CreateDtlsTestConfig(ProtocolVersion.DTLSv12);
+ c.clientSupportedVersions = ProtocolVersion.DTLSv10.Only();
+
+ AddTestCase(testSuite, c, "FallbackNone");
+ }
+ }
+
+ private static void AddVersionTests(IList testSuite, ProtocolVersion version)
+ {
+ string prefix = version.ToString()
+ .Replace(" ", "")
+ .Replace("\\", "")
+ .Replace(".", "")
+ + "_";
+
+ /*
+ * NOTE: Temporarily disabled automatic test runs because of problems getting a clean exit
+ * of the DTLS server after a fatal alert. As of writing, manual runs show the correct
+ * alerts being raised
+ */
+
+#if false
+ /*
+ * Server only declares support for SHA1/RSA, client selects MD5/RSA. Since the client is
+ * NOT actually tracking MD5 over the handshake, we expect fatal alert from the client.
+ */
+ if (TlsUtilities.IsTlsV12(version))
+ {
+ TlsTestConfig c = CreateDtlsTestConfig(version);
+ c.clientAuth = C.CLIENT_AUTH_VALID;
+ c.clientAuthSigAlg = new SignatureAndHashAlgorithm(HashAlgorithm.md5, SignatureAlgorithm.rsa);
+ c.serverCertReqSigAlgs = TlsUtilities.GetDefaultRsaSignatureAlgorithms();
+ c.ExpectClientFatalAlert(AlertDescription.internal_error);
+
+ AddTestCase(testSuite, c, prefix + "BadCertificateVerifyHashAlg");
+ }
+
+ /*
+ * Server only declares support for SHA1/ECDSA, client selects SHA1/RSA. Since the client is
+ * actually tracking SHA1 over the handshake, we expect fatal alert to come from the server
+ * when it verifies the selected algorithm against the CertificateRequest supported
+ * algorithms.
+ */
+ if (TlsUtilities.IsTlsV12(version))
+ {
+ TlsTestConfig c = CreateDtlsTestConfig(version);
+ c.clientAuth = C.CLIENT_AUTH_VALID;
+ c.clientAuthSigAlg = new SignatureAndHashAlgorithm(HashAlgorithm.sha1, SignatureAlgorithm.rsa);
+ c.serverCertReqSigAlgs = TlsUtilities.GetDefaultECDsaSignatureAlgorithms();
+ c.serverCheckSigAlgOfClientCerts = false;
+ c.ExpectServerFatalAlert(AlertDescription.illegal_parameter);
+
+ AddTestCase(testSuite, c, prefix + "BadCertificateVerifySigAlg");
+ }
+
+ /*
+ * Server only declares support for SHA1/ECDSA, client signs with SHA1/RSA, but sends
+ * SHA1/ECDSA in the CertificateVerify. Since the client is actually tracking SHA1 over the
+ * handshake, and the claimed algorithm is in the CertificateRequest supported algorithms,
+ * we expect fatal alert to come from the server when it finds the claimed algorithm
+ * doesn't match the client certificate.
+ */
+ if (TlsUtilities.IsTlsV12(version))
+ {
+ TlsTestConfig c = CreateDtlsTestConfig(version);
+ c.clientAuth = C.CLIENT_AUTH_VALID;
+ c.clientAuthSigAlg = new SignatureAndHashAlgorithm(HashAlgorithm.sha1, SignatureAlgorithm.rsa);
+ c.clientAuthSigAlgClaimed = new SignatureAndHashAlgorithm(HashAlgorithm.sha1,
+ SignatureAlgorithm.ecdsa);
+ c.serverCertReqSigAlgs = TlsUtilities.GetDefaultECDsaSignatureAlgorithms();
+ c.ExpectServerFatalAlert(AlertDescription.decrypt_error);
+
+ AddTestCase(testSuite, c, prefix + "BadCertificateVerifySigAlgMismatch");
+ }
+
+ {
+ TlsTestConfig c = CreateDtlsTestConfig(version);
+ c.clientAuth = C.CLIENT_AUTH_INVALID_VERIFY;
+ c.ExpectServerFatalAlert(AlertDescription.decrypt_error);
+
+ AddTestCase(testSuite, c, prefix + "BadCertificateVerifySignature");
+ }
+
+ {
+ TlsTestConfig c = CreateDtlsTestConfig(version);
+ c.clientAuth = C.CLIENT_AUTH_INVALID_CERT;
+ c.ExpectServerFatalAlert(AlertDescription.bad_certificate);
+
+ AddTestCase(testSuite, c, prefix + "BadClientCertificate");
+ }
+
+ {
+ TlsTestConfig c = CreateDtlsTestConfig(version);
+ c.clientAuth = C.CLIENT_AUTH_NONE;
+ c.serverCertReq = C.SERVER_CERT_REQ_MANDATORY;
+ c.ExpectServerFatalAlert(AlertDescription.handshake_failure);
+
+ AddTestCase(testSuite, c, prefix + "BadMandatoryCertReqDeclined");
+ }
+
+ /*
+ * Server sends SHA-256/RSA certificate, which is not the default {sha1,rsa} implied by the
+ * absent signature_algorithms extension. We expect fatal alert from the client when it
+ * verifies the certificate's 'signatureAlgorithm' against the implicit default signature_algorithms.
+ */
+ if (TlsUtilities.IsTlsV12(version))
+ {
+ TlsTestConfig c = CreateDtlsTestConfig(version);
+ c.clientSendSignatureAlgorithms = false;
+ c.serverAuthSigAlg = new SignatureAndHashAlgorithm(HashAlgorithm.sha256, SignatureAlgorithm.rsa);
+ c.ExpectClientFatalAlert(AlertDescription.certificate_unknown);
+
+ AddTestCase(testSuite, c, prefix + "BadServerCertSigAlg");
+ }
+
+ /*
+ * Server selects MD5/RSA for ServerKeyExchange signature, which is not in the default
+ * supported signature algorithms that the client sent. We expect fatal alert from the
+ * client when it verifies the selected algorithm against the supported algorithms.
+ */
+ if (TlsUtilities.IsTlsV12(version))
+ {
+ TlsTestConfig c = CreateDtlsTestConfig(version);
+ c.serverAuthSigAlg = new SignatureAndHashAlgorithm(HashAlgorithm.md5, SignatureAlgorithm.rsa);
+ c.ExpectClientFatalAlert(AlertDescription.illegal_parameter);
+
+ AddTestCase(testSuite, c, prefix + "BadServerKeyExchangeSigAlg");
+ }
+
+ /*
+ * Server selects MD5/RSA for ServerKeyExchange signature, which is not the default {sha1,rsa}
+ * implied by the absent signature_algorithms extension. We expect fatal alert from the
+ * client when it verifies the selected algorithm against the implicit default.
+ */
+ if (TlsUtilities.IsTlsV12(version))
+ {
+ TlsTestConfig c = CreateDtlsTestConfig(version);
+ c.clientCheckSigAlgOfServerCerts = false;
+ c.clientSendSignatureAlgorithms = false;
+ c.serverAuthSigAlg = new SignatureAndHashAlgorithm(HashAlgorithm.md5, SignatureAlgorithm.rsa);
+ c.ExpectClientFatalAlert(AlertDescription.illegal_parameter);
+
+ AddTestCase(testSuite, c, prefix + "BadServerKeyExchangeSigAlg2");
+ }
+#endif
+
+ {
+ TlsTestConfig c = CreateDtlsTestConfig(version);
+
+ AddTestCase(testSuite, c, prefix + "GoodDefault");
+ }
+
+ {
+ TlsTestConfig c = CreateDtlsTestConfig(version);
+ c.serverCertReq = C.SERVER_CERT_REQ_NONE;
+
+ AddTestCase(testSuite, c, prefix + "GoodNoCertReq");
+ }
+
+ {
+ TlsTestConfig c = CreateDtlsTestConfig(version);
+ c.clientAuth = C.CLIENT_AUTH_NONE;
+
+ AddTestCase(testSuite, c, prefix + "GoodOptionalCertReqDeclined");
+ }
+
+#if false
+ /*
+ * Server generates downgraded (RFC 8446) ServerHello. We expect fatal alert
+ * (illegal_parameter) from the client.
+ */
+ if (!TlsUtilities.IsTlsV12(version))
+ {
+ TlsTestConfig c = CreateDtlsTestConfig(version);
+ c.serverNegotiateVersion = version;
+ c.serverSupportedVersions = ProtocolVersion.DTLSv12.DownTo(version);
+ c.ExpectClientFatalAlert(AlertDescription.illegal_parameter);
+
+ AddTestCase(testSuite, c, prefix + "BadDowngrade");
+ }
+#endif
+ }
+
+ private static void AddTestCase(IList testSuite, TlsTestConfig config, string name)
+ {
+ testSuite.Add(new TestCaseData(config).SetName(name));
+ }
+
+ private static TlsTestConfig CreateDtlsTestConfig(ProtocolVersion serverMaxVersion)
+ {
+ TlsTestConfig c = new TlsTestConfig();
+ c.clientSupportedVersions = ProtocolVersion.DTLSv12.DownTo(ProtocolVersion.DTLSv10);
+ c.serverSupportedVersions = serverMaxVersion.DownTo(ProtocolVersion.DTLSv10);
+ return c;
+ }
+
+ public static void RunTests()
+ {
+ foreach (TestCaseData data in Suite())
+ {
+ Console.WriteLine(data.TestName);
+ new DtlsTestCase().RunTest((TlsTestConfig)data.Arguments[0]);
+ }
+ }
+ }
+}
diff --git a/crypto/test/src/tls/test/LoggingDatagramTransport.cs b/crypto/test/src/tls/test/LoggingDatagramTransport.cs
new file mode 100644
index 000000000..f675b72fc
--- /dev/null
+++ b/crypto/test/src/tls/test/LoggingDatagramTransport.cs
@@ -0,0 +1,87 @@
+using System;
+using System.IO;
+using System.Text;
+
+using Org.BouncyCastle.Utilities.Date;
+
+namespace Org.BouncyCastle.Tls.Tests
+{
+ public class LoggingDatagramTransport
+ : DatagramTransport
+ {
+ private static readonly string HEX_CHARS = "0123456789ABCDEF";
+
+ private readonly DatagramTransport m_transport;
+ private readonly TextWriter m_output;
+ private readonly long m_launchTimestamp;
+
+ public LoggingDatagramTransport(DatagramTransport transport, TextWriter output)
+ {
+ this.m_transport = transport;
+ this.m_output = output;
+ this.m_launchTimestamp = DateTimeUtilities.CurrentUnixMs();
+ }
+
+ public virtual int GetReceiveLimit()
+ {
+ return m_transport.GetReceiveLimit();
+ }
+
+ public virtual int GetSendLimit()
+ {
+ return m_transport.GetSendLimit();
+ }
+
+ public virtual int Receive(byte[] buf, int off, int len, int waitMillis)
+ {
+ int length = m_transport.Receive(buf, off, len, waitMillis);
+ if (length >= 0)
+ {
+ DumpDatagram("Received", buf, off, length);
+ }
+ return length;
+ }
+
+ public virtual void Send(byte[] buf, int off, int len)
+ {
+ DumpDatagram("Sending", buf, off, len);
+ m_transport.Send(buf, off, len);
+ }
+
+ public virtual void Close()
+ {
+ m_transport.Close();
+ }
+
+ private void DumpDatagram(string verb, byte[] buf, int off, int len)
+ {
+ long timestamp = DateTimeUtilities.CurrentUnixMs() - m_launchTimestamp;
+ StringBuilder sb = new StringBuilder("(+" + timestamp + "ms) " + verb + " " + len + " byte datagram:");
+ for (int pos = 0; pos < len; ++pos)
+ {
+ if (pos % 16 == 0)
+ {
+ sb.Append(Environment.NewLine);
+ sb.Append(" ");
+ }
+ else if (pos % 16 == 8)
+ {
+ sb.Append('-');
+ }
+ else
+ {
+ sb.Append(' ');
+ }
+ int val = buf[off + pos] & 0xFF;
+ sb.Append(HEX_CHARS[val >> 4]);
+ sb.Append(HEX_CHARS[val & 0xF]);
+ }
+ Dump(sb.ToString());
+ }
+
+ private void Dump(string s)
+ {
+ lock (this) m_output.WriteLine(s);
+ }
+ }
+}
diff --git a/crypto/test/src/tls/test/MockDatagramAssociation.cs b/crypto/test/src/tls/test/MockDatagramAssociation.cs
new file mode 100644
index 000000000..3e0c0f52b
--- /dev/null
+++ b/crypto/test/src/tls/test/MockDatagramAssociation.cs
@@ -0,0 +1,107 @@
+using System;
+using System.Collections;
+using System.Threading;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Tls.Tests
+{
+ public class MockDatagramAssociation
+ {
+ private int m_mtu;
+ private MockDatagramTransport m_client, m_server;
+
+ public MockDatagramAssociation(int mtu)
+ {
+ this.m_mtu = mtu;
+
+ IList clientQueue = new ArrayList();
+ IList serverQueue = new ArrayList();
+
+ this.m_client = new MockDatagramTransport(this, clientQueue, serverQueue);
+ this.m_server = new MockDatagramTransport(this, serverQueue, clientQueue);
+ }
+
+ public virtual DatagramTransport Client
+ {
+ get { return m_client; }
+ }
+
+ public virtual DatagramTransport Server
+ {
+ get { return m_server; }
+ }
+
+ private class MockDatagramTransport
+ : DatagramTransport
+ {
+ private readonly MockDatagramAssociation m_outer;
+ private IList m_receiveQueue, m_sendQueue;
+
+ internal MockDatagramTransport(MockDatagramAssociation outer, IList receiveQueue, IList sendQueue)
+ {
+ this.m_outer = outer;
+ this.m_receiveQueue = receiveQueue;
+ this.m_sendQueue = sendQueue;
+ }
+
+ public virtual int GetReceiveLimit()
+ {
+ return m_outer.m_mtu;
+ }
+
+ public virtual int GetSendLimit()
+ {
+ return m_outer.m_mtu;
+ }
+
+ public virtual int Receive(byte[] buf, int off, int len, int waitMillis)
+ {
+ lock (m_receiveQueue)
+ {
+ if (m_receiveQueue.Count < 1)
+ {
+ try
+ {
+ Monitor.Wait(m_receiveQueue, waitMillis);
+ }
+ catch (ThreadInterruptedException)
+ {
+ // TODO Keep waiting until full wait expired?
+ }
+
+ if (m_receiveQueue.Count < 1)
+ return -1;
+ }
+
+ byte[] packet = (byte[])m_receiveQueue[0];
+ m_receiveQueue.RemoveAt(0);
+ int copyLength = System.Math.Min(len, packet.Length);
+ Array.Copy(packet, 0, buf, off, copyLength);
+ return copyLength;
+ }
+ }
+
+ public virtual void Send(byte[] buf, int off, int len)
+ {
+ if (len > m_outer.m_mtu)
+ {
+ // TODO Simulate rejection?
+ }
+
+ byte[] packet = Arrays.CopyOfRange(buf, off, off + len);
+
+ lock (m_sendQueue)
+ {
+ m_sendQueue.Add(packet);
+ Monitor.PulseAll(m_sendQueue);
+ }
+ }
+
+ public virtual void Close()
+ {
+ // TODO?
+ }
+ }
+ }
+}
diff --git a/crypto/test/src/tls/test/MockDtlsClient.cs b/crypto/test/src/tls/test/MockDtlsClient.cs
new file mode 100644
index 000000000..5aa1ebbd3
--- /dev/null
+++ b/crypto/test/src/tls/test/MockDtlsClient.cs
@@ -0,0 +1,170 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Tls.Crypto;
+using Org.BouncyCastle.Tls.Crypto.Impl.BC;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Encoders;
+
+namespace Org.BouncyCastle.Tls.Tests
+{
+ internal class MockDtlsClient
+ : DefaultTlsClient
+ {
+ internal TlsSession m_session;
+
+ internal MockDtlsClient(TlsSession session)
+ : base(new BcTlsCrypto(new SecureRandom()))
+ {
+ this.m_session = session;
+ }
+
+ public override TlsSession GetSessionToResume()
+ {
+ return this.m_session;
+ }
+
+ public override void NotifyAlertRaised(short alertLevel, short alertDescription, string message,
+ Exception cause)
+ {
+ TextWriter output = (alertLevel == AlertLevel.fatal) ? Console.Error : Console.Out;
+ output.WriteLine("DTLS client raised alert: " + AlertLevel.GetText(alertLevel)
+ + ", " + AlertDescription.GetText(alertDescription));
+ if (message != null)
+ {
+ output.WriteLine("> " + message);
+ }
+ if (cause != null)
+ {
+ output.WriteLine(cause);
+ }
+ }
+
+ public override void NotifyAlertReceived(short alertLevel, short alertDescription)
+ {
+ TextWriter output = (alertLevel == AlertLevel.fatal) ? Console.Error : Console.Out;
+ output.WriteLine("DTLS client received alert: " + AlertLevel.GetText(alertLevel)
+ + ", " + AlertDescription.GetText(alertDescription));
+ }
+
+ public override void NotifyServerVersion(ProtocolVersion serverVersion)
+ {
+ base.NotifyServerVersion(serverVersion);
+
+ Console.WriteLine("DTLS client negotiated " + serverVersion);
+ }
+
+ public override TlsAuthentication GetAuthentication()
+ {
+ return new MyTlsAuthentication(m_context);
+ }
+
+ public override void NotifyHandshakeComplete()
+ {
+ base.NotifyHandshakeComplete();
+
+ ProtocolName protocolName = m_context.SecurityParameters.ApplicationProtocol;
+ if (protocolName != null)
+ {
+ Console.WriteLine("Client ALPN: " + protocolName.GetUtf8Decoding());
+ }
+
+ TlsSession newSession = m_context.Session;
+ if (newSession != null)
+ {
+ if (newSession.IsResumable)
+ {
+ byte[] newSessionID = newSession.SessionID;
+ string hex = ToHexString(newSessionID);
+
+ if (m_session != null && Arrays.AreEqual(m_session.SessionID, newSessionID))
+ {
+ Console.WriteLine("Client resumed session: " + hex);
+ }
+ else
+ {
+ Console.WriteLine("Client established session: " + hex);
+ }
+
+ this.m_session = newSession;
+ }
+
+ byte[] tlsServerEndPoint = m_context.ExportChannelBinding(ChannelBinding.tls_server_end_point);
+ if (null != tlsServerEndPoint)
+ {
+ Console.WriteLine("Client 'tls-server-end-point': " + ToHexString(tlsServerEndPoint));
+ }
+
+ byte[] tlsUnique = m_context.ExportChannelBinding(ChannelBinding.tls_unique);
+ Console.WriteLine("Client 'tls-unique': " + ToHexString(tlsUnique));
+ }
+ }
+
+ protected virtual string ToHexString(byte[] data)
+ {
+ return data == null ? "(null)" : Hex.ToHexString(data);
+ }
+
+ protected override ProtocolVersion[] GetSupportedVersions()
+ {
+ return ProtocolVersion.DTLSv12.DownTo(ProtocolVersion.DTLSv10);
+ }
+
+ internal class MyTlsAuthentication
+ : TlsAuthentication
+ {
+ private readonly TlsContext m_context;
+
+ internal MyTlsAuthentication(TlsContext context)
+ {
+ this.m_context = context;
+ }
+
+ public void NotifyServerCertificate(TlsServerCertificate serverCertificate)
+ {
+ TlsCertificate[] chain = serverCertificate.Certificate.GetCertificateList();
+
+ Console.WriteLine("DTLS client received server certificate chain of length " + chain.Length);
+ for (int i = 0; i != chain.Length; i++)
+ {
+ X509CertificateStructure entry = X509CertificateStructure.GetInstance(chain[i].GetEncoded());
+ // TODO Create fingerprint based on certificate signature algorithm digest
+ Console.WriteLine(" fingerprint:SHA-256 " + TlsTestUtilities.Fingerprint(entry) + " ("
+ + entry.Subject + ")");
+ }
+
+ bool isEmpty = serverCertificate == null || serverCertificate.Certificate == null
+ || serverCertificate.Certificate.IsEmpty;
+
+ if (isEmpty)
+ throw new TlsFatalAlert(AlertDescription.bad_certificate);
+
+ string[] trustedCertResources = new String[]{ "x509-server-dsa.pem", "x509-server-ecdh.pem",
+ "x509-server-ecdsa.pem", "x509-server-ed25519.pem", "x509-server-ed448.pem",
+ "x509-server-rsa_pss_256.pem", "x509-server-rsa_pss_384.pem", "x509-server-rsa_pss_512.pem",
+ "x509-server-rsa-enc.pem", "x509-server-rsa-sign.pem" };
+
+ TlsCertificate[] certPath = TlsTestUtilities.GetTrustedCertPath(m_context.Crypto, chain[0],
+ trustedCertResources);
+
+ if (null == certPath)
+ throw new TlsFatalAlert(AlertDescription.bad_certificate);
+
+ TlsUtilities.CheckPeerSigAlgs(m_context, certPath);
+ }
+
+ public TlsCredentials GetClientCredentials(CertificateRequest certificateRequest)
+ {
+ short[] certificateTypes = certificateRequest.CertificateTypes;
+ if (certificateTypes == null || !Arrays.Contains(certificateTypes, ClientCertificateType.rsa_sign))
+ return null;
+
+ return TlsTestUtilities.LoadSignerCredentials(m_context,
+ certificateRequest.SupportedSignatureAlgorithms, SignatureAlgorithm.rsa, "x509-client-rsa.pem",
+ "x509-client-key-rsa.pem");
+ }
+ }
+ }
+}
diff --git a/crypto/test/src/tls/test/MockDtlsServer.cs b/crypto/test/src/tls/test/MockDtlsServer.cs
new file mode 100644
index 000000000..18e53628e
--- /dev/null
+++ b/crypto/test/src/tls/test/MockDtlsServer.cs
@@ -0,0 +1,146 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Tls.Crypto;
+using Org.BouncyCastle.Tls.Crypto.Impl.BC;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities.Encoders;
+
+namespace Org.BouncyCastle.Tls.Tests
+{
+ internal class MockDtlsServer
+ : DefaultTlsServer
+ {
+ internal MockDtlsServer()
+ : base(new BcTlsCrypto(new SecureRandom()))
+ {
+ }
+
+ public override void NotifyAlertRaised(short alertLevel, short alertDescription, string message,
+ Exception cause)
+ {
+ TextWriter output = (alertLevel == AlertLevel.fatal) ? Console.Error : Console.Out;
+ output.WriteLine("DTLS server raised alert: " + AlertLevel.GetText(alertLevel)
+ + ", " + AlertDescription.GetText(alertDescription));
+ if (message != null)
+ {
+ output.WriteLine("> " + message);
+ }
+ if (cause != null)
+ {
+ output.WriteLine(cause);
+ }
+ }
+
+ public override void NotifyAlertReceived(short alertLevel, short alertDescription)
+ {
+ TextWriter output = (alertLevel == AlertLevel.fatal) ? Console.Error : Console.Out;
+ output.WriteLine("DTLS server received alert: " + AlertLevel.GetText(alertLevel)
+ + ", " + AlertDescription.GetText(alertDescription));
+ }
+
+ public override ProtocolVersion GetServerVersion()
+ {
+ ProtocolVersion serverVersion = base.GetServerVersion();
+
+ Console.WriteLine("DTLS server negotiated " + serverVersion);
+
+ return serverVersion;
+ }
+
+ public override CertificateRequest GetCertificateRequest()
+ {
+ short[] certificateTypes = new short[]{ ClientCertificateType.rsa_sign,
+ ClientCertificateType.dss_sign, ClientCertificateType.ecdsa_sign };
+
+ IList serverSigAlgs = null;
+ if (TlsUtilities.IsSignatureAlgorithmsExtensionAllowed(m_context.ServerVersion))
+ {
+ serverSigAlgs = TlsUtilities.GetDefaultSupportedSignatureAlgorithms(m_context);
+ }
+
+ IList certificateAuthorities = new ArrayList();
+ //certificateAuthorities.Add(TlsTestUtilities.LoadBcCertificateResource("x509-ca-dsa.pem").Subject);
+ //certificateAuthorities.Add(TlsTestUtilities.LoadBcCertificateResource("x509-ca-ecdsa.pem").Subject);
+ //certificateAuthorities.Add(TlsTestUtilities.LoadBcCertificateResource("x509-ca-rsa.pem").Subject);
+
+ // All the CA certificates are currently configured with this subject
+ certificateAuthorities.Add(new X509Name("CN=BouncyCastle TLS Test CA"));
+
+ return new CertificateRequest(certificateTypes, serverSigAlgs, certificateAuthorities);
+ }
+
+ public override void NotifyClientCertificate(Certificate clientCertificate)
+ {
+ TlsCertificate[] chain = clientCertificate.GetCertificateList();
+
+ Console.WriteLine("DTLS server received client certificate chain of length " + chain.Length);
+ for (int i = 0; i != chain.Length; i++)
+ {
+ X509CertificateStructure entry = X509CertificateStructure.GetInstance(chain[i].GetEncoded());
+ // TODO Create fingerprint based on certificate signature algorithm digest
+ Console.WriteLine(" fingerprint:SHA-256 " + TlsTestUtilities.Fingerprint(entry) + " ("
+ + entry.Subject + ")");
+ }
+
+ bool isEmpty = (clientCertificate == null || clientCertificate.IsEmpty);
+
+ if (isEmpty)
+ return;
+
+ string[] trustedCertResources = new string[]{ "x509-client-dsa.pem", "x509-client-ecdh.pem",
+ "x509-client-ecdsa.pem", "x509-client-ed25519.pem", "x509-client-ed448.pem",
+ "x509-client-rsa_pss_256.pem", "x509-client-rsa_pss_384.pem", "x509-client-rsa_pss_512.pem",
+ "x509-client-rsa.pem" };
+
+ TlsCertificate[] certPath = TlsTestUtilities.GetTrustedCertPath(m_context.Crypto, chain[0],
+ trustedCertResources);
+
+ if (null == certPath)
+ throw new TlsFatalAlert(AlertDescription.bad_certificate);
+
+ TlsUtilities.CheckPeerSigAlgs(m_context, certPath);
+ }
+
+ public override void NotifyHandshakeComplete()
+ {
+ base.NotifyHandshakeComplete();
+
+ ProtocolName protocolName = m_context.SecurityParameters.ApplicationProtocol;
+ if (protocolName != null)
+ {
+ Console.WriteLine("Server ALPN: " + protocolName.GetUtf8Decoding());
+ }
+
+ byte[] tlsServerEndPoint = m_context.ExportChannelBinding(ChannelBinding.tls_server_end_point);
+ Console.WriteLine("Server 'tls-server-end-point': " + ToHexString(tlsServerEndPoint));
+
+ byte[] tlsUnique = m_context.ExportChannelBinding(ChannelBinding.tls_unique);
+ Console.WriteLine("Server 'tls-unique': " + ToHexString(tlsUnique));
+ }
+
+ protected override TlsCredentialedDecryptor GetRsaEncryptionCredentials()
+ {
+ return TlsTestUtilities.LoadEncryptionCredentials(m_context,
+ new string[]{ "x509-server-rsa-enc.pem", "x509-ca-rsa.pem" }, "x509-server-key-rsa-enc.pem");
+ }
+
+ protected override TlsCredentialedSigner GetRsaSignerCredentials()
+ {
+ IList clientSigAlgs = m_context.SecurityParameters.ClientSigAlgs;
+ return TlsTestUtilities.LoadSignerCredentialsServer(m_context, clientSigAlgs, SignatureAlgorithm.rsa);
+ }
+
+ protected virtual string ToHexString(byte[] data)
+ {
+ return data == null ? "(null)" : Hex.ToHexString(data);
+ }
+
+ protected override ProtocolVersion[] GetSupportedVersions()
+ {
+ return ProtocolVersion.DTLSv12.DownTo(ProtocolVersion.DTLSv10);
+ }
+ }
+}
diff --git a/crypto/test/src/tls/test/MockPskDtlsClient.cs b/crypto/test/src/tls/test/MockPskDtlsClient.cs
new file mode 100644
index 000000000..c83c9e7fd
--- /dev/null
+++ b/crypto/test/src/tls/test/MockPskDtlsClient.cs
@@ -0,0 +1,161 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Tls.Crypto;
+using Org.BouncyCastle.Tls.Crypto.Impl.BC;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Encoders;
+
+namespace Org.BouncyCastle.Tls.Tests
+{
+ internal class MockPskDtlsClient
+ : PskTlsClient
+ {
+ internal TlsSession m_session;
+
+ internal MockPskDtlsClient(TlsSession session)
+ : this(session, new BasicTlsPskIdentity("client", Strings.ToUtf8ByteArray("TLS_TEST_PSK")))
+ {
+ }
+
+ internal MockPskDtlsClient(TlsSession session, TlsPskIdentity pskIdentity)
+ : base(new BcTlsCrypto(new SecureRandom()), pskIdentity)
+ {
+ this.m_session = session;
+ }
+
+ public override TlsSession GetSessionToResume()
+ {
+ return m_session;
+ }
+
+ public override void NotifyAlertRaised(short alertLevel, short alertDescription, string message,
+ Exception cause)
+ {
+ TextWriter output = (alertLevel == AlertLevel.fatal) ? Console.Error : Console.Out;
+ output.WriteLine("DTLS-PSK client raised alert: " + AlertLevel.GetText(alertLevel)
+ + ", " + AlertDescription.GetText(alertDescription));
+ if (message != null)
+ {
+ output.WriteLine("> " + message);
+ }
+ if (cause != null)
+ {
+ output.WriteLine(cause);
+ }
+ }
+
+ public override void NotifyAlertReceived(short alertLevel, short alertDescription)
+ {
+ TextWriter output = (alertLevel == AlertLevel.fatal) ? Console.Error : Console.Out;
+ output.WriteLine("DTLS-PSK client received alert: " + AlertLevel.GetText(alertLevel)
+ + ", " + AlertDescription.GetText(alertDescription));
+ }
+
+ public override void NotifyServerVersion(ProtocolVersion serverVersion)
+ {
+ base.NotifyServerVersion(serverVersion);
+
+ Console.WriteLine("DTLS-PSK client negotiated " + serverVersion);
+ }
+
+ public override TlsAuthentication GetAuthentication()
+ {
+ return new MyTlsAuthentication(m_context);
+ }
+
+ public override void NotifyHandshakeComplete()
+ {
+ base.NotifyHandshakeComplete();
+
+ ProtocolName protocolName = m_context.SecurityParameters.ApplicationProtocol;
+ if (protocolName != null)
+ {
+ Console.WriteLine("Client ALPN: " + protocolName.GetUtf8Decoding());
+ }
+
+ TlsSession newSession = m_context.Session;
+ if (newSession != null)
+ {
+ if (newSession.IsResumable)
+ {
+ byte[] newSessionID = newSession.SessionID;
+ string hex = ToHexString(newSessionID);
+
+ if (m_session != null && Arrays.AreEqual(m_session.SessionID, newSessionID))
+ {
+ Console.WriteLine("Client resumed session: " + hex);
+ }
+ else
+ {
+ Console.WriteLine("Client established session: " + hex);
+ }
+
+ this.m_session = newSession;
+ }
+
+ byte[] tlsServerEndPoint = m_context.ExportChannelBinding(ChannelBinding.tls_server_end_point);
+ if (null != tlsServerEndPoint)
+ {
+ Console.WriteLine("Client 'tls-server-end-point': " + ToHexString(tlsServerEndPoint));
+ }
+
+ byte[] tlsUnique = m_context.ExportChannelBinding(ChannelBinding.tls_unique);
+ Console.WriteLine("Client 'tls-unique': " + ToHexString(tlsUnique));
+ }
+ }
+
+ protected virtual string ToHexString(byte[] data)
+ {
+ return data == null ? "(null)" : Hex.ToHexString(data);
+ }
+
+ protected override ProtocolVersion[] GetSupportedVersions()
+ {
+ return ProtocolVersion.DTLSv12.Only();
+ }
+
+ internal class MyTlsAuthentication
+ : ServerOnlyTlsAuthentication
+ {
+ private readonly TlsContext m_context;
+
+ internal MyTlsAuthentication(TlsContext context)
+ {
+ this.m_context = context;
+ }
+
+ public override void NotifyServerCertificate(TlsServerCertificate serverCertificate)
+ {
+ TlsCertificate[] chain = serverCertificate.Certificate.GetCertificateList();
+
+ Console.WriteLine("DTLS-PSK client received server certificate chain of length " + chain.Length);
+ for (int i = 0; i != chain.Length; i++)
+ {
+ X509CertificateStructure entry = X509CertificateStructure.GetInstance(chain[i].GetEncoded());
+ // TODO Create fingerprint based on certificate signature algorithm digest
+ Console.WriteLine(" fingerprint:SHA-256 " + TlsTestUtilities.Fingerprint(entry) + " ("
+ + entry.Subject + ")");
+ }
+
+ bool isEmpty = serverCertificate == null || serverCertificate.Certificate == null
+ || serverCertificate.Certificate.IsEmpty;
+
+ if (isEmpty)
+ throw new TlsFatalAlert(AlertDescription.bad_certificate);
+
+ string[] trustedCertResources = new string[] { "x509-server-rsa-enc.pem" };
+
+ TlsCertificate[] certPath = TlsTestUtilities.GetTrustedCertPath(m_context.Crypto, chain[0],
+ trustedCertResources);
+
+ if (null == certPath)
+ throw new TlsFatalAlert(AlertDescription.bad_certificate);
+
+ TlsUtilities.CheckPeerSigAlgs(m_context, certPath);
+ }
+ }
+ }
+}
diff --git a/crypto/test/src/tls/test/MockPskDtlsServer.cs b/crypto/test/src/tls/test/MockPskDtlsServer.cs
new file mode 100644
index 000000000..bb084535a
--- /dev/null
+++ b/crypto/test/src/tls/test/MockPskDtlsServer.cs
@@ -0,0 +1,113 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Tls.Crypto.Impl.BC;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Encoders;
+
+namespace Org.BouncyCastle.Tls.Tests
+{
+ internal class MockPskDtlsServer
+ : PskTlsServer
+ {
+ internal MockPskDtlsServer()
+ : base(new BcTlsCrypto(new SecureRandom()), new MyIdentityManager())
+ {
+ }
+
+ public override void NotifyAlertRaised(short alertLevel, short alertDescription, string message,
+ Exception cause)
+ {
+ TextWriter output = (alertLevel == AlertLevel.fatal) ? Console.Error : Console.Out;
+ output.WriteLine("DTLS-PSK server raised alert: " + AlertLevel.GetText(alertLevel)
+ + ", " + AlertDescription.GetText(alertDescription));
+ if (message != null)
+ {
+ output.WriteLine("> " + message);
+ }
+ if (cause != null)
+ {
+ output.WriteLine(cause);
+ }
+ }
+
+ public override void NotifyAlertReceived(short alertLevel, short alertDescription)
+ {
+ TextWriter output = (alertLevel == AlertLevel.fatal) ? Console.Error : Console.Out;
+ output.WriteLine("DTLS-PSK server received alert: " + AlertLevel.GetText(alertLevel)
+ + ", " + AlertDescription.GetText(alertDescription));
+ }
+
+ public override ProtocolVersion GetServerVersion()
+ {
+ ProtocolVersion serverVersion = base.GetServerVersion();
+
+ Console.WriteLine("DTLS-PSK server negotiated " + serverVersion);
+
+ return serverVersion;
+ }
+
+ public override void NotifyHandshakeComplete()
+ {
+ base.NotifyHandshakeComplete();
+
+ ProtocolName protocolName = m_context.SecurityParameters.ApplicationProtocol;
+ if (protocolName != null)
+ {
+ Console.WriteLine("Server ALPN: " + protocolName.GetUtf8Decoding());
+ }
+
+ byte[] tlsServerEndPoint = m_context.ExportChannelBinding(ChannelBinding.tls_server_end_point);
+ Console.WriteLine("Server 'tls-server-end-point': " + ToHexString(tlsServerEndPoint));
+
+ byte[] tlsUnique = m_context.ExportChannelBinding(ChannelBinding.tls_unique);
+ Console.WriteLine("Server 'tls-unique': " + ToHexString(tlsUnique));
+
+ byte[] pskIdentity = m_context.SecurityParameters.PskIdentity;
+ if (pskIdentity != null)
+ {
+ string name = Strings.FromUtf8ByteArray(pskIdentity);
+ Console.WriteLine("DTLS-PSK server completed handshake for PSK identity: " + name);
+ }
+ }
+
+ protected override TlsCredentialedDecryptor GetRsaEncryptionCredentials()
+ {
+ return TlsTestUtilities.LoadEncryptionCredentials(m_context,
+ new string[] { "x509-server-rsa-enc.pem", "x509-ca-rsa.pem" }, "x509-server-key-rsa-enc.pem");
+ }
+
+ protected virtual string ToHexString(byte[] data)
+ {
+ return data == null ? "(null)" : Hex.ToHexString(data);
+ }
+
+ protected override ProtocolVersion[] GetSupportedVersions()
+ {
+ return ProtocolVersion.DTLSv12.Only();
+ }
+
+ internal class MyIdentityManager
+ : TlsPskIdentityManager
+ {
+ public byte[] GetHint()
+ {
+ return Strings.ToUtf8ByteArray("hint");
+ }
+
+ public byte[] GetPsk(byte[] identity)
+ {
+ if (identity != null)
+ {
+ string name = Strings.FromUtf8ByteArray(identity);
+ if (name.Equals("client"))
+ {
+ return Strings.ToUtf8ByteArray("TLS_TEST_PSK");
+ }
+ }
+ return null;
+ }
+ }
+ }
+}
diff --git a/crypto/test/src/tls/test/MockPskTlsClient.cs b/crypto/test/src/tls/test/MockPskTlsClient.cs
new file mode 100644
index 000000000..46774266b
--- /dev/null
+++ b/crypto/test/src/tls/test/MockPskTlsClient.cs
@@ -0,0 +1,186 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Tls.Crypto;
+using Org.BouncyCastle.Tls.Crypto.Impl.BC;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Encoders;
+
+namespace Org.BouncyCastle.Tls.Tests
+{
+ internal class MockPskTlsClient
+ : PskTlsClient
+ {
+ internal TlsSession m_session;
+
+ internal MockPskTlsClient(TlsSession session)
+ : this(session, new BasicTlsPskIdentity("client", Strings.ToUtf8ByteArray("TLS_TEST_PSK")))
+ {
+ }
+
+ internal MockPskTlsClient(TlsSession session, TlsPskIdentity pskIdentity)
+ : base(new BcTlsCrypto(new SecureRandom()), pskIdentity)
+ {
+ this.m_session = session;
+ }
+
+ protected override IList GetProtocolNames()
+ {
+ IList protocolNames = new ArrayList();
+ protocolNames.Add(ProtocolName.Http_1_1);
+ protocolNames.Add(ProtocolName.Http_2_Tls);
+ return protocolNames;
+ }
+
+ public override TlsSession GetSessionToResume()
+ {
+ return m_session;
+ }
+
+ public override void NotifyAlertRaised(short alertLevel, short alertDescription, string message,
+ Exception cause)
+ {
+ TextWriter output = (alertLevel == AlertLevel.fatal) ? Console.Error : Console.Out;
+ output.WriteLine("TLS-PSK client raised alert: " + AlertLevel.GetText(alertLevel)
+ + ", " + AlertDescription.GetText(alertDescription));
+ if (message != null)
+ {
+ output.WriteLine("> " + message);
+ }
+ if (cause != null)
+ {
+ output.WriteLine(cause);
+ }
+ }
+
+ public override void NotifyAlertReceived(short alertLevel, short alertDescription)
+ {
+ TextWriter output = (alertLevel == AlertLevel.fatal) ? Console.Error : Console.Out;
+ output.WriteLine("TLS-PSK client received alert: " + AlertLevel.GetText(alertLevel)
+ + ", " + AlertDescription.GetText(alertDescription));
+ }
+
+ public override IDictionary GetClientExtensions()
+ {
+ IDictionary clientExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(
+ base.GetClientExtensions());
+
+ {
+ /*
+ * NOTE: If you are copying test code, do not blindly set these extensions in your own client.
+ */
+ TlsExtensionsUtilities.AddMaxFragmentLengthExtension(clientExtensions, MaxFragmentLength.pow2_9);
+ TlsExtensionsUtilities.AddPaddingExtension(clientExtensions, m_context.Crypto.SecureRandom.Next(16));
+ TlsExtensionsUtilities.AddTruncatedHmacExtension(clientExtensions);
+ }
+ return clientExtensions;
+ }
+
+ public override void NotifyServerVersion(ProtocolVersion serverVersion)
+ {
+ base.NotifyServerVersion(serverVersion);
+
+ Console.WriteLine("TLS-PSK client negotiated " + serverVersion);
+ }
+
+ public override TlsAuthentication GetAuthentication()
+ {
+ return new MyTlsAuthentication(m_context);
+ }
+
+ public override void NotifyHandshakeComplete()
+ {
+ base.NotifyHandshakeComplete();
+
+ ProtocolName protocolName = m_context.SecurityParameters.ApplicationProtocol;
+ if (protocolName != null)
+ {
+ Console.WriteLine("Client ALPN: " + protocolName.GetUtf8Decoding());
+ }
+
+ TlsSession newSession = m_context.Session;
+ if (newSession != null)
+ {
+ if (newSession.IsResumable)
+ {
+ byte[] newSessionID = newSession.SessionID;
+ string hex = ToHexString(newSessionID);
+
+ if (m_session != null && Arrays.AreEqual(m_session.SessionID, newSessionID))
+ {
+ Console.WriteLine("Client resumed session: " + hex);
+ }
+ else
+ {
+ Console.WriteLine("Client established session: " + hex);
+ }
+
+ this.m_session = newSession;
+ }
+
+ byte[] tlsServerEndPoint = m_context.ExportChannelBinding(ChannelBinding.tls_server_end_point);
+ if (null != tlsServerEndPoint)
+ {
+ Console.WriteLine("Client 'tls-server-end-point': " + ToHexString(tlsServerEndPoint));
+ }
+
+ byte[] tlsUnique = m_context.ExportChannelBinding(ChannelBinding.tls_unique);
+ Console.WriteLine("Client 'tls-unique': " + ToHexString(tlsUnique));
+ }
+ }
+
+ protected virtual string ToHexString(byte[] data)
+ {
+ return data == null ? "(null)" : Hex.ToHexString(data);
+ }
+
+ protected override ProtocolVersion[] GetSupportedVersions()
+ {
+ return ProtocolVersion.TLSv12.Only();
+ }
+
+ internal class MyTlsAuthentication
+ : ServerOnlyTlsAuthentication
+ {
+ private readonly TlsContext m_context;
+
+ internal MyTlsAuthentication(TlsContext context)
+ {
+ this.m_context = context;
+ }
+
+ public override void NotifyServerCertificate(TlsServerCertificate serverCertificate)
+ {
+ TlsCertificate[] chain = serverCertificate.Certificate.GetCertificateList();
+
+ Console.WriteLine("TLS-PSK client received server certificate chain of length " + chain.Length);
+ for (int i = 0; i != chain.Length; i++)
+ {
+ X509CertificateStructure entry = X509CertificateStructure.GetInstance(chain[i].GetEncoded());
+ // TODO Create fingerprint based on certificate signature algorithm digest
+ Console.WriteLine(" fingerprint:SHA-256 " + TlsTestUtilities.Fingerprint(entry) + " ("
+ + entry.Subject + ")");
+ }
+
+ bool isEmpty = serverCertificate == null || serverCertificate.Certificate == null
+ || serverCertificate.Certificate.IsEmpty;
+
+ if (isEmpty)
+ throw new TlsFatalAlert(AlertDescription.bad_certificate);
+
+ string[] trustedCertResources = new string[] { "x509-server-rsa-enc.pem" };
+
+ TlsCertificate[] certPath = TlsTestUtilities.GetTrustedCertPath(m_context.Crypto, chain[0],
+ trustedCertResources);
+
+ if (null == certPath)
+ throw new TlsFatalAlert(AlertDescription.bad_certificate);
+
+ TlsUtilities.CheckPeerSigAlgs(m_context, certPath);
+ }
+ };
+ }
+}
diff --git a/crypto/test/src/tls/test/MockPskTlsServer.cs b/crypto/test/src/tls/test/MockPskTlsServer.cs
new file mode 100644
index 000000000..743073b04
--- /dev/null
+++ b/crypto/test/src/tls/test/MockPskTlsServer.cs
@@ -0,0 +1,122 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Tls.Crypto.Impl.BC;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Encoders;
+
+namespace Org.BouncyCastle.Tls.Tests
+{
+ internal class MockPskTlsServer
+ : PskTlsServer
+ {
+ internal MockPskTlsServer()
+ : base(new BcTlsCrypto(new SecureRandom()), new MyIdentityManager())
+ {
+ }
+
+ protected override IList GetProtocolNames()
+ {
+ IList protocolNames = new ArrayList();
+ protocolNames.Add(ProtocolName.Http_2_Tls);
+ protocolNames.Add(ProtocolName.Http_1_1);
+ return protocolNames;
+ }
+
+ public override void NotifyAlertRaised(short alertLevel, short alertDescription, string message,
+ Exception cause)
+ {
+ TextWriter output = (alertLevel == AlertLevel.fatal) ? Console.Error : Console.Out;
+ output.WriteLine("TLS-PSK server raised alert: " + AlertLevel.GetText(alertLevel)
+ + ", " + AlertDescription.GetText(alertDescription));
+ if (message != null)
+ {
+ output.WriteLine("> " + message);
+ }
+ if (cause != null)
+ {
+ output.WriteLine(cause);
+ }
+ }
+
+ public override void NotifyAlertReceived(short alertLevel, short alertDescription)
+ {
+ TextWriter output = (alertLevel == AlertLevel.fatal) ? Console.Error : Console.Out;
+ output.WriteLine("TLS-PSK server received alert: " + AlertLevel.GetText(alertLevel)
+ + ", " + AlertDescription.GetText(alertDescription));
+ }
+
+ public override ProtocolVersion GetServerVersion()
+ {
+ ProtocolVersion serverVersion = base.GetServerVersion();
+
+ Console.WriteLine("TLS-PSK server negotiated " + serverVersion);
+
+ return serverVersion;
+ }
+
+ public override void NotifyHandshakeComplete()
+ {
+ base.NotifyHandshakeComplete();
+
+ ProtocolName protocolName = m_context.SecurityParameters.ApplicationProtocol;
+ if (protocolName != null)
+ {
+ Console.WriteLine("Server ALPN: " + protocolName.GetUtf8Decoding());
+ }
+
+ byte[] tlsServerEndPoint = m_context.ExportChannelBinding(ChannelBinding.tls_server_end_point);
+ Console.WriteLine("Server 'tls-server-end-point': " + ToHexString(tlsServerEndPoint));
+
+ byte[] tlsUnique = m_context.ExportChannelBinding(ChannelBinding.tls_unique);
+ Console.WriteLine("Server 'tls-unique': " + ToHexString(tlsUnique));
+
+ byte[] pskIdentity = m_context.SecurityParameters.PskIdentity;
+ if (pskIdentity != null)
+ {
+ string name = Strings.FromUtf8ByteArray(pskIdentity);
+ Console.WriteLine("TLS-PSK server completed handshake for PSK identity: " + name);
+ }
+ }
+
+ protected override TlsCredentialedDecryptor GetRsaEncryptionCredentials()
+ {
+ return TlsTestUtilities.LoadEncryptionCredentials(m_context,
+ new string[] { "x509-server-rsa-enc.pem", "x509-ca-rsa.pem" }, "x509-server-key-rsa-enc.pem");
+ }
+
+ protected virtual string ToHexString(byte[] data)
+ {
+ return data == null ? "(null)" : Hex.ToHexString(data);
+ }
+
+ protected override ProtocolVersion[] GetSupportedVersions()
+ {
+ return ProtocolVersion.TLSv12.Only();
+ }
+
+ internal class MyIdentityManager
+ : TlsPskIdentityManager
+ {
+ public byte[] GetHint()
+ {
+ return Strings.ToUtf8ByteArray("hint");
+ }
+
+ public byte[] GetPsk(byte[] identity)
+ {
+ if (identity != null)
+ {
+ string name = Strings.FromUtf8ByteArray(identity);
+ if (name.Equals("client"))
+ {
+ return Strings.ToUtf8ByteArray("TLS_TEST_PSK");
+ }
+ }
+ return null;
+ }
+ }
+ }
+}
diff --git a/crypto/test/src/tls/test/MockSrpTlsClient.cs b/crypto/test/src/tls/test/MockSrpTlsClient.cs
new file mode 100644
index 000000000..3d2232893
--- /dev/null
+++ b/crypto/test/src/tls/test/MockSrpTlsClient.cs
@@ -0,0 +1,182 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Tls.Crypto;
+using Org.BouncyCastle.Tls.Crypto.Impl.BC;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Encoders;
+
+namespace Org.BouncyCastle.Tls.Tests
+{
+ internal class MockSrpTlsClient
+ : SrpTlsClient
+ {
+ internal TlsSession m_session;
+
+ internal MockSrpTlsClient(TlsSession session, TlsSrpIdentity srpIdentity)
+ : base(new BcTlsCrypto(new SecureRandom()), srpIdentity)
+ {
+ this.m_session = session;
+ }
+
+ protected override IList GetProtocolNames()
+ {
+ IList protocolNames = new ArrayList();
+ protocolNames.Add(ProtocolName.Http_1_1);
+ protocolNames.Add(ProtocolName.Http_2_Tls);
+ return protocolNames;
+ }
+
+ public override TlsSession GetSessionToResume()
+ {
+ return m_session;
+ }
+
+ public override void NotifyAlertRaised(short alertLevel, short alertDescription, string message,
+ Exception cause)
+ {
+ TextWriter output = (alertLevel == AlertLevel.fatal) ? Console.Error : Console.Out;
+ output.WriteLine("TLS-SRP client raised alert: " + AlertLevel.GetText(alertLevel)
+ + ", " + AlertDescription.GetText(alertDescription));
+ if (message != null)
+ {
+ output.WriteLine("> " + message);
+ }
+ if (cause != null)
+ {
+ output.WriteLine(cause);
+ }
+ }
+
+ public override void NotifyAlertReceived(short alertLevel, short alertDescription)
+ {
+ TextWriter output = (alertLevel == AlertLevel.fatal) ? Console.Error : Console.Out;
+ output.WriteLine("TLS-SRP client received alert: " + AlertLevel.GetText(alertLevel)
+ + ", " + AlertDescription.GetText(alertDescription));
+ }
+
+ public override IDictionary GetClientExtensions()
+ {
+ IDictionary clientExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(
+ base.GetClientExtensions());
+
+ {
+ /*
+ * NOTE: If you are copying test code, do not blindly set these extensions in your own client.
+ */
+ TlsExtensionsUtilities.AddMaxFragmentLengthExtension(clientExtensions, MaxFragmentLength.pow2_9);
+ TlsExtensionsUtilities.AddPaddingExtension(clientExtensions, m_context.Crypto.SecureRandom.Next(16));
+ TlsExtensionsUtilities.AddTruncatedHmacExtension(clientExtensions);
+ }
+ return clientExtensions;
+ }
+
+ public override void NotifyServerVersion(ProtocolVersion serverVersion)
+ {
+ base.NotifyServerVersion(serverVersion);
+
+ Console.WriteLine("TLS-SRP client negotiated " + serverVersion);
+ }
+
+ public override TlsAuthentication GetAuthentication()
+ {
+ return new MyTlsAuthentication(m_context);
+ }
+
+ public override void NotifyHandshakeComplete()
+ {
+ base.NotifyHandshakeComplete();
+
+ ProtocolName protocolName = m_context.SecurityParameters.ApplicationProtocol;
+ if (protocolName != null)
+ {
+ Console.WriteLine("Client ALPN: " + protocolName.GetUtf8Decoding());
+ }
+
+ TlsSession newSession = m_context.Session;
+ if (newSession != null)
+ {
+ if (newSession.IsResumable)
+ {
+ byte[] newSessionID = newSession.SessionID;
+ string hex = ToHexString(newSessionID);
+
+ if (m_session != null && Arrays.AreEqual(m_session.SessionID, newSessionID))
+ {
+ Console.WriteLine("Client resumed session: " + hex);
+ }
+ else
+ {
+ Console.WriteLine("Client established session: " + hex);
+ }
+
+ this.m_session = newSession;
+ }
+
+ byte[] tlsServerEndPoint = m_context.ExportChannelBinding(ChannelBinding.tls_server_end_point);
+ if (null != tlsServerEndPoint)
+ {
+ Console.WriteLine("Client 'tls-server-end-point': " + ToHexString(tlsServerEndPoint));
+ }
+
+ byte[] tlsUnique = m_context.ExportChannelBinding(ChannelBinding.tls_unique);
+ Console.WriteLine("Client 'tls-unique': " + ToHexString(tlsUnique));
+ }
+ }
+
+ protected virtual string ToHexString(byte[] data)
+ {
+ return data == null ? "(null)" : Hex.ToHexString(data);
+ }
+
+ protected override ProtocolVersion[] GetSupportedVersions()
+ {
+ return ProtocolVersion.TLSv12.Only();
+ }
+
+ internal class MyTlsAuthentication
+ : ServerOnlyTlsAuthentication
+ {
+ private readonly TlsContext m_context;
+
+ internal MyTlsAuthentication(TlsContext context)
+ {
+ this.m_context = context;
+ }
+
+ public override void NotifyServerCertificate(TlsServerCertificate serverCertificate)
+ {
+ TlsCertificate[] chain = serverCertificate.Certificate.GetCertificateList();
+
+ Console.WriteLine("TLS-SRP client received server certificate chain of length " + chain.Length);
+ for (int i = 0; i != chain.Length; i++)
+ {
+ X509CertificateStructure entry = X509CertificateStructure.GetInstance(chain[i].GetEncoded());
+ // TODO Create fingerprint based on certificate signature algorithm digest
+ Console.WriteLine(" fingerprint:SHA-256 " + TlsTestUtilities.Fingerprint(entry) + " ("
+ + entry.Subject + ")");
+ }
+
+ bool isEmpty = serverCertificate == null || serverCertificate.Certificate == null
+ || serverCertificate.Certificate.IsEmpty;
+
+ if (isEmpty)
+ throw new TlsFatalAlert(AlertDescription.bad_certificate);
+
+ string[] trustedCertResources = new string[] { "x509-server-dsa.pem", "x509-server-rsa_pss_256.pem",
+ "x509-server-rsa_pss_384.pem", "x509-server-rsa_pss_512.pem", "x509-server-rsa-sign.pem" };
+
+ TlsCertificate[] certPath = TlsTestUtilities.GetTrustedCertPath(m_context.Crypto, chain[0],
+ trustedCertResources);
+
+ if (null == certPath)
+ throw new TlsFatalAlert(AlertDescription.bad_certificate);
+
+ TlsUtilities.CheckPeerSigAlgs(m_context, certPath);
+ }
+ };
+ }
+}
diff --git a/crypto/test/src/tls/test/MockSrpTlsServer.cs b/crypto/test/src/tls/test/MockSrpTlsServer.cs
new file mode 100644
index 000000000..725901811
--- /dev/null
+++ b/crypto/test/src/tls/test/MockSrpTlsServer.cs
@@ -0,0 +1,143 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Crypto.Agreement.Srp;
+using Org.BouncyCastle.Crypto.Digests;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Tls.Crypto;
+using Org.BouncyCastle.Tls.Crypto.Impl.BC;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Encoders;
+
+namespace Org.BouncyCastle.Tls.Tests
+{
+ internal class MockSrpTlsServer
+ : SrpTlsServer
+ {
+ internal static readonly Srp6Group TEST_GROUP = Tls.Crypto.Srp6StandardGroups.rfc5054_1024;
+ internal static readonly byte[] TEST_IDENTITY = Strings.ToUtf8ByteArray("client");
+ internal static readonly byte[] TEST_PASSWORD = Strings.ToUtf8ByteArray("password");
+ internal static readonly TlsSrpIdentity TEST_SRP_IDENTITY = new BasicTlsSrpIdentity(TEST_IDENTITY,
+ TEST_PASSWORD);
+ internal static readonly byte[] TEST_SALT = Strings.ToUtf8ByteArray("salt");
+ internal static readonly byte[] TEST_SEED_KEY = Strings.ToUtf8ByteArray("seed_key");
+
+ internal MockSrpTlsServer()
+ : base(new BcTlsCrypto(new SecureRandom()), new MyIdentityManager(new BcTlsCrypto(new SecureRandom())))
+ {
+ }
+
+ protected override IList GetProtocolNames()
+ {
+ IList protocolNames = new ArrayList();
+ protocolNames.Add(ProtocolName.Http_2_Tls);
+ protocolNames.Add(ProtocolName.Http_1_1);
+ return protocolNames;
+ }
+
+ public override void NotifyAlertRaised(short alertLevel, short alertDescription, string message,
+ Exception cause)
+ {
+ TextWriter output = (alertLevel == AlertLevel.fatal) ? Console.Error : Console.Out;
+ output.WriteLine("TLS-SRP server raised alert: " + AlertLevel.GetText(alertLevel)
+ + ", " + AlertDescription.GetText(alertDescription));
+ if (message != null)
+ {
+ output.WriteLine("> " + message);
+ }
+ if (cause != null)
+ {
+ output.WriteLine(cause);
+ }
+ }
+
+ public override void NotifyAlertReceived(short alertLevel, short alertDescription)
+ {
+ TextWriter output = (alertLevel == AlertLevel.fatal) ? Console.Error : Console.Out;
+ output.WriteLine("TLS-SRP server received alert: " + AlertLevel.GetText(alertLevel)
+ + ", " + AlertDescription.GetText(alertDescription));
+ }
+
+ public override ProtocolVersion GetServerVersion()
+ {
+ ProtocolVersion serverVersion = base.GetServerVersion();
+
+ Console.WriteLine("TLS-SRP server negotiated " + serverVersion);
+
+ return serverVersion;
+ }
+
+ public override void NotifyHandshakeComplete()
+ {
+ base.NotifyHandshakeComplete();
+
+ ProtocolName protocolName = m_context.SecurityParameters.ApplicationProtocol;
+ if (protocolName != null)
+ {
+ Console.WriteLine("Server ALPN: " + protocolName.GetUtf8Decoding());
+ }
+
+ byte[] tlsServerEndPoint = m_context.ExportChannelBinding(ChannelBinding.tls_server_end_point);
+ Console.WriteLine("Server 'tls-server-end-point': " + ToHexString(tlsServerEndPoint));
+
+ byte[] tlsUnique = m_context.ExportChannelBinding(ChannelBinding.tls_unique);
+ Console.WriteLine("Server 'tls-unique': " + ToHexString(tlsUnique));
+
+ byte[] srpIdentity = m_context.SecurityParameters.SrpIdentity;
+ if (srpIdentity != null)
+ {
+ string name = Strings.FromUtf8ByteArray(srpIdentity);
+ Console.WriteLine("TLS-SRP server completed handshake for SRP identity: " + name);
+ }
+ }
+
+ protected override TlsCredentialedSigner GetDsaSignerCredentials()
+ {
+ IList clientSigAlgs = m_context.SecurityParameters.ClientSigAlgs;
+ return TlsTestUtilities.LoadSignerCredentialsServer(m_context, clientSigAlgs, SignatureAlgorithm.dsa);
+ }
+
+ protected override TlsCredentialedSigner GetRsaSignerCredentials()
+ {
+ IList clientSigAlgs = m_context.SecurityParameters.ClientSigAlgs;
+ return TlsTestUtilities.LoadSignerCredentialsServer(m_context, clientSigAlgs, SignatureAlgorithm.rsa);
+ }
+
+ protected virtual string ToHexString(byte[] data)
+ {
+ return data == null ? "(null)" : Hex.ToHexString(data);
+ }
+
+ internal class MyIdentityManager
+ : TlsSrpIdentityManager
+ {
+ protected SimulatedTlsSrpIdentityManager m_unknownIdentityManager;
+
+ internal MyIdentityManager(TlsCrypto crypto)
+ {
+ m_unknownIdentityManager = SimulatedTlsSrpIdentityManager.GetRfc5054Default(crypto, TEST_GROUP,
+ TEST_SEED_KEY);
+ }
+
+ public TlsSrpLoginParameters GetLoginParameters(byte[] identity)
+ {
+ if (Arrays.ConstantTimeAreEqual(TEST_IDENTITY, identity))
+ {
+ Srp6VerifierGenerator verifierGenerator = new Srp6VerifierGenerator();
+ verifierGenerator.Init(TEST_GROUP.N, TEST_GROUP.G, new Sha1Digest());
+
+ BigInteger verifier = verifierGenerator.GenerateVerifier(TEST_SALT, identity, TEST_PASSWORD);
+
+ TlsSrpConfig srpConfig = new TlsSrpConfig();
+ srpConfig.SetExplicitNG(new BigInteger[]{ TEST_GROUP.N, TEST_GROUP.G });
+
+ return new TlsSrpLoginParameters(identity, srpConfig, verifier, TEST_SALT);
+ }
+
+ return m_unknownIdentityManager.GetLoginParameters(identity);
+ }
+ }
+ }
+}
diff --git a/crypto/test/src/tls/test/MockTlsClient.cs b/crypto/test/src/tls/test/MockTlsClient.cs
new file mode 100644
index 000000000..62b699590
--- /dev/null
+++ b/crypto/test/src/tls/test/MockTlsClient.cs
@@ -0,0 +1,190 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Tls.Crypto;
+using Org.BouncyCastle.Tls.Crypto.Impl.BC;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Encoders;
+
+namespace Org.BouncyCastle.Tls.Tests
+{
+ internal class MockTlsClient
+ : DefaultTlsClient
+ {
+ internal TlsSession m_session;
+
+ internal MockTlsClient(TlsSession session)
+ : base(new BcTlsCrypto(new SecureRandom()))
+ {
+ this.m_session = session;
+ }
+
+ protected override IList GetProtocolNames()
+ {
+ IList protocolNames = new ArrayList();
+ protocolNames.Add(ProtocolName.Http_1_1);
+ protocolNames.Add(ProtocolName.Http_2_Tls);
+ return protocolNames;
+ }
+
+ public override TlsSession GetSessionToResume()
+ {
+ return m_session;
+ }
+
+ public override void NotifyAlertRaised(short alertLevel, short alertDescription, string message,
+ Exception cause)
+ {
+ TextWriter output = (alertLevel == AlertLevel.fatal) ? Console.Error : Console.Out;
+ output.WriteLine("TLS client raised alert: " + AlertLevel.GetText(alertLevel)
+ + ", " + AlertDescription.GetText(alertDescription));
+ if (message != null)
+ {
+ output.WriteLine("> " + message);
+ }
+ if (cause != null)
+ {
+ output.WriteLine(cause);
+ }
+ }
+
+ public override void NotifyAlertReceived(short alertLevel, short alertDescription)
+ {
+ TextWriter output = (alertLevel == AlertLevel.fatal) ? Console.Error : Console.Out;
+ output.WriteLine("TLS client received alert: " + AlertLevel.GetText(alertLevel)
+ + ", " + AlertDescription.GetText(alertDescription));
+ }
+
+ public override IDictionary GetClientExtensions()
+ {
+ IDictionary clientExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(
+ base.GetClientExtensions());
+
+ {
+ /*
+ * NOTE: If you are copying test code, do not blindly set these extensions in your own client.
+ */
+ TlsExtensionsUtilities.AddMaxFragmentLengthExtension(clientExtensions, MaxFragmentLength.pow2_9);
+ TlsExtensionsUtilities.AddPaddingExtension(clientExtensions, m_context.Crypto.SecureRandom.Next(16));
+ TlsExtensionsUtilities.AddTruncatedHmacExtension(clientExtensions);
+ }
+ return clientExtensions;
+ }
+
+ public override void NotifyServerVersion(ProtocolVersion serverVersion)
+ {
+ base.NotifyServerVersion(serverVersion);
+
+ Console.WriteLine("TLS client negotiated " + serverVersion);
+ }
+
+ public override TlsAuthentication GetAuthentication()
+ {
+ return new MyTlsAuthentication(m_context);
+ }
+
+ public override void NotifyHandshakeComplete()
+ {
+ base.NotifyHandshakeComplete();
+
+ ProtocolName protocolName = m_context.SecurityParameters.ApplicationProtocol;
+ if (protocolName != null)
+ {
+ Console.WriteLine("Client ALPN: " + protocolName.GetUtf8Decoding());
+ }
+
+ TlsSession newSession = m_context.Session;
+ if (newSession != null)
+ {
+ if (newSession.IsResumable)
+ {
+ byte[] newSessionID = newSession.SessionID;
+ string hex = ToHexString(newSessionID);
+
+ if (m_session != null && Arrays.AreEqual(m_session.SessionID, newSessionID))
+ {
+ Console.WriteLine("Client resumed session: " + hex);
+ }
+ else
+ {
+ Console.WriteLine("Client established session: " + hex);
+ }
+
+ this.m_session = newSession;
+ }
+
+ byte[] tlsServerEndPoint = m_context.ExportChannelBinding(ChannelBinding.tls_server_end_point);
+ if (null != tlsServerEndPoint)
+ {
+ Console.WriteLine("Client 'tls-server-end-point': " + ToHexString(tlsServerEndPoint));
+ }
+
+ byte[] tlsUnique = m_context.ExportChannelBinding(ChannelBinding.tls_unique);
+ Console.WriteLine("Client 'tls-unique': " + ToHexString(tlsUnique));
+ }
+ }
+
+ protected virtual string ToHexString(byte[] data)
+ {
+ return data == null ? "(null)" : Hex.ToHexString(data);
+ }
+
+ internal class MyTlsAuthentication
+ : TlsAuthentication
+ {
+ private readonly TlsContext m_context;
+
+ internal MyTlsAuthentication(TlsContext context)
+ {
+ this.m_context = context;
+ }
+
+ public void NotifyServerCertificate(TlsServerCertificate serverCertificate)
+ {
+ TlsCertificate[] chain = serverCertificate.Certificate.GetCertificateList();
+
+ Console.WriteLine("TLS client received server certificate chain of length " + chain.Length);
+ for (int i = 0; i != chain.Length; i++)
+ {
+ X509CertificateStructure entry = X509CertificateStructure.GetInstance(chain[i].GetEncoded());
+ // TODO Create fingerprint based on certificate signature algorithm digest
+ Console.WriteLine(" fingerprint:SHA-256 " + TlsTestUtilities.Fingerprint(entry) + " ("
+ + entry.Subject + ")");
+ }
+
+ bool isEmpty = serverCertificate == null || serverCertificate.Certificate == null
+ || serverCertificate.Certificate.IsEmpty;
+
+ if (isEmpty)
+ throw new TlsFatalAlert(AlertDescription.bad_certificate);
+
+ string[] trustedCertResources = new string[]{ "x509-server-dsa.pem", "x509-server-ecdh.pem",
+ "x509-server-ecdsa.pem", "x509-server-ed25519.pem", "x509-server-ed448.pem",
+ "x509-server-rsa_pss_256.pem", "x509-server-rsa_pss_384.pem", "x509-server-rsa_pss_512.pem",
+ "x509-server-rsa-enc.pem", "x509-server-rsa-sign.pem" };
+
+ TlsCertificate[] certPath = TlsTestUtilities.GetTrustedCertPath(m_context.Crypto, chain[0],
+ trustedCertResources);
+
+ if (null == certPath)
+ throw new TlsFatalAlert(AlertDescription.bad_certificate);
+
+ TlsUtilities.CheckPeerSigAlgs(m_context, certPath);
+ }
+
+ public TlsCredentials GetClientCredentials(CertificateRequest certificateRequest)
+ {
+ short[] certificateTypes = certificateRequest.CertificateTypes;
+ if (certificateTypes == null || !Arrays.Contains(certificateTypes, ClientCertificateType.rsa_sign))
+ return null;
+
+ return TlsTestUtilities.LoadSignerCredentials(m_context,
+ certificateRequest.SupportedSignatureAlgorithms, SignatureAlgorithm.rsa, "x509-client-rsa.pem",
+ "x509-client-key-rsa.pem");
+ }
+ };
+ }
+}
diff --git a/crypto/test/src/tls/test/MockTlsServer.cs b/crypto/test/src/tls/test/MockTlsServer.cs
new file mode 100644
index 000000000..94d4c7dfd
--- /dev/null
+++ b/crypto/test/src/tls/test/MockTlsServer.cs
@@ -0,0 +1,149 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Tls.Crypto;
+using Org.BouncyCastle.Tls.Crypto.Impl.BC;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities.Encoders;
+
+namespace Org.BouncyCastle.Tls.Tests
+{
+ internal class MockTlsServer
+ : DefaultTlsServer
+ {
+ internal MockTlsServer()
+ : base(new BcTlsCrypto(new SecureRandom()))
+ {
+ }
+
+ protected override IList GetProtocolNames()
+ {
+ IList protocolNames = new ArrayList();
+ protocolNames.Add(ProtocolName.Http_2_Tls);
+ protocolNames.Add(ProtocolName.Http_1_1);
+ return protocolNames;
+ }
+
+ public override void NotifyAlertRaised(short alertLevel, short alertDescription, string message,
+ Exception cause)
+ {
+ TextWriter output = (alertLevel == AlertLevel.fatal) ? Console.Error : Console.Out;
+ output.WriteLine("TLS server raised alert: " + AlertLevel.GetText(alertLevel)
+ + ", " + AlertDescription.GetText(alertDescription));
+ if (message != null)
+ {
+ output.WriteLine("> " + message);
+ }
+ if (cause != null)
+ {
+ output.WriteLine(cause);
+ }
+ }
+
+ public override void NotifyAlertReceived(short alertLevel, short alertDescription)
+ {
+ TextWriter output = (alertLevel == AlertLevel.fatal) ? Console.Error : Console.Out;
+ output.WriteLine("TLS server received alert: " + AlertLevel.GetText(alertLevel)
+ + ", " + AlertDescription.GetText(alertDescription));
+ }
+
+ public override ProtocolVersion GetServerVersion()
+ {
+ ProtocolVersion serverVersion = base.GetServerVersion();
+
+ Console.WriteLine("TLS server negotiated " + serverVersion);
+
+ return serverVersion;
+ }
+
+ public override CertificateRequest GetCertificateRequest()
+ {
+ short[] certificateTypes = new short[]{ ClientCertificateType.rsa_sign,
+ ClientCertificateType.dss_sign, ClientCertificateType.ecdsa_sign };
+
+ IList serverSigAlgs = null;
+ if (TlsUtilities.IsSignatureAlgorithmsExtensionAllowed(m_context.ServerVersion))
+ {
+ serverSigAlgs = TlsUtilities.GetDefaultSupportedSignatureAlgorithms(m_context);
+ }
+
+ IList certificateAuthorities = new ArrayList();
+ //certificateAuthorities.Add(TlsTestUtilities.LoadBcCertificateResource("x509-ca-dsa.pem").Subject);
+ //certificateAuthorities.Add(TlsTestUtilities.LoadBcCertificateResource("x509-ca-ecdsa.pem").Subject);
+ //certificateAuthorities.Add(TlsTestUtilities.LoadBcCertificateResource("x509-ca-rsa.pem").Subject);
+
+ // All the CA certificates are currently configured with this subject
+ certificateAuthorities.Add(new X509Name("CN=BouncyCastle TLS Test CA"));
+
+ return new CertificateRequest(certificateTypes, serverSigAlgs, certificateAuthorities);
+ }
+
+ public override void NotifyClientCertificate(Certificate clientCertificate)
+ {
+ TlsCertificate[] chain = clientCertificate.GetCertificateList();
+
+ Console.WriteLine("TLS server received client certificate chain of length " + chain.Length);
+ for (int i = 0; i < chain.Length; ++i)
+ {
+ X509CertificateStructure entry = X509CertificateStructure.GetInstance(chain[i].GetEncoded());
+ // TODO Create fingerprint based on certificate signature algorithm digest
+ Console.WriteLine(" fingerprint:SHA-256 " + TlsTestUtilities.Fingerprint(entry) + " ("
+ + entry.Subject + ")");
+ }
+
+ bool isEmpty = (clientCertificate == null || clientCertificate.IsEmpty);
+
+ if (isEmpty)
+ return;
+
+ string[] trustedCertResources = new string[]{ "x509-client-dsa.pem", "x509-client-ecdh.pem",
+ "x509-client-ecdsa.pem", "x509-client-ed25519.pem", "x509-client-ed448.pem",
+ "x509-client-rsa_pss_256.pem", "x509-client-rsa_pss_384.pem", "x509-client-rsa_pss_512.pem",
+ "x509-client-rsa.pem" };
+
+ TlsCertificate[] certPath = TlsTestUtilities.GetTrustedCertPath(m_context.Crypto, chain[0],
+ trustedCertResources);
+
+ if (null == certPath)
+ throw new TlsFatalAlert(AlertDescription.bad_certificate);
+
+ TlsUtilities.CheckPeerSigAlgs(m_context, certPath);
+ }
+
+ public override void NotifyHandshakeComplete()
+ {
+ base.NotifyHandshakeComplete();
+
+ ProtocolName protocolName = m_context.SecurityParameters.ApplicationProtocol;
+ if (protocolName != null)
+ {
+ Console.WriteLine("Server ALPN: " + protocolName.GetUtf8Decoding());
+ }
+
+ byte[] tlsServerEndPoint = m_context.ExportChannelBinding(ChannelBinding.tls_server_end_point);
+ Console.WriteLine("Server 'tls-server-end-point': " + ToHexString(tlsServerEndPoint));
+
+ byte[] tlsUnique = m_context.ExportChannelBinding(ChannelBinding.tls_unique);
+ Console.WriteLine("Server 'tls-unique': " + ToHexString(tlsUnique));
+ }
+
+ protected override TlsCredentialedDecryptor GetRsaEncryptionCredentials()
+ {
+ return TlsTestUtilities.LoadEncryptionCredentials(m_context,
+ new string[]{ "x509-server-rsa-enc.pem", "x509-ca-rsa.pem" }, "x509-server-key-rsa-enc.pem");
+ }
+
+ protected override TlsCredentialedSigner GetRsaSignerCredentials()
+ {
+ IList clientSigAlgs = m_context.SecurityParameters.ClientSigAlgs;
+ return TlsTestUtilities.LoadSignerCredentialsServer(m_context, clientSigAlgs, SignatureAlgorithm.rsa);
+ }
+
+ protected virtual string ToHexString(byte[] data)
+ {
+ return data == null ? "(null)" : Hex.ToHexString(data);
+ }
+ }
+}
diff --git a/crypto/test/src/tls/test/NetworkStream.cs b/crypto/test/src/tls/test/NetworkStream.cs
new file mode 100644
index 000000000..c5f1dfd59
--- /dev/null
+++ b/crypto/test/src/tls/test/NetworkStream.cs
@@ -0,0 +1,101 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Tls.Tests
+{
+ internal class NetworkStream
+ : Stream
+ {
+ private readonly Stream m_inner;
+ private bool m_closed = false;
+
+ internal NetworkStream(Stream inner)
+ {
+ this.m_inner = inner;
+ }
+
+ internal virtual bool IsClosed
+ {
+ get { lock (this) return m_closed; }
+ }
+
+ public override bool CanRead
+ {
+ get { return m_inner.CanRead; }
+ }
+
+ public override bool CanSeek
+ {
+ get { return m_inner.CanSeek; }
+ }
+
+ public override bool CanWrite
+ {
+ get { return m_inner.CanWrite; }
+ }
+
+ public override void Close()
+ {
+ lock (this) m_closed = true;
+ }
+
+ public override void Flush()
+ {
+ m_inner.Flush();
+ }
+
+ public override long Length
+ {
+ get { return m_inner.Length; }
+ }
+
+ public override long Position
+ {
+ get { return m_inner.Position; }
+ set { m_inner.Position = value; }
+ }
+
+ public override long Seek(long offset, SeekOrigin origin)
+ {
+ return m_inner.Seek(offset, origin);
+ }
+
+ public override void SetLength(long value)
+ {
+ m_inner.SetLength(value);
+ }
+
+ public override int Read(byte[] buffer, int offset, int count)
+ {
+ CheckNotClosed();
+ return m_inner.Read(buffer, offset, count);
+ }
+
+ public override int ReadByte()
+ {
+ CheckNotClosed();
+ return m_inner.ReadByte();
+ }
+
+ public override void Write(byte[] buf, int off, int len)
+ {
+ CheckNotClosed();
+ m_inner.Write(buf, off, len);
+ }
+
+ public override void WriteByte(byte value)
+ {
+ CheckNotClosed();
+ m_inner.WriteByte(value);
+ }
+
+ private void CheckNotClosed()
+ {
+ lock (this)
+ {
+ if (m_closed)
+ throw new ObjectDisposedException(this.GetType().Name);
+ }
+ }
+ }
+}
diff --git a/crypto/test/src/tls/test/PipedStream.cs b/crypto/test/src/tls/test/PipedStream.cs
new file mode 100644
index 000000000..6de5703e1
--- /dev/null
+++ b/crypto/test/src/tls/test/PipedStream.cs
@@ -0,0 +1,134 @@
+using System;
+using System.IO;
+using System.Threading;
+
+namespace Org.BouncyCastle.Tls.Tests
+{
+ internal class PipedStream
+ : Stream
+ {
+ private readonly MemoryStream m_buf = new MemoryStream();
+ private bool m_closed = false;
+
+ private PipedStream m_other = null;
+ private long m_readPos = 0;
+
+ internal PipedStream()
+ {
+ }
+
+ internal PipedStream(PipedStream other)
+ {
+ lock (other)
+ {
+ this.m_other = other;
+ other.m_other = this;
+ }
+ }
+
+ public override bool CanRead
+ {
+ get { return true; }
+ }
+
+ public override bool CanSeek
+ {
+ get { return false; }
+ }
+
+ public override bool CanWrite
+ {
+ get { return true; }
+ }
+
+ public override void Close()
+ {
+ lock (this)
+ {
+ m_closed = true;
+ Monitor.PulseAll(this);
+ }
+ }
+
+ public override void Flush()
+ {
+ }
+
+ public override long Length
+ {
+ get { throw new NotImplementedException(); }
+ }
+
+ public override long Position
+ {
+ get { throw new NotImplementedException(); }
+ set { throw new NotImplementedException(); }
+ }
+
+ public override long Seek(long offset, SeekOrigin origin)
+ {
+ throw new NotImplementedException();
+ }
+
+ public override void SetLength(long value)
+ {
+ throw new NotImplementedException();
+ }
+
+ public override int Read(byte[] buffer, int offset, int count)
+ {
+ lock (m_other)
+ {
+ WaitForData();
+ int len = (int)System.Math.Min(count, m_other.m_buf.Position - m_readPos);
+ Array.Copy(m_other.m_buf.GetBuffer(), m_readPos, buffer, offset, len);
+ m_readPos += len;
+ return len;
+ }
+ }
+
+ public override int ReadByte()
+ {
+ lock (m_other)
+ {
+ WaitForData();
+ bool eof = (m_readPos >= m_other.m_buf.Position);
+ return eof ? -1 : m_other.m_buf.GetBuffer()[m_readPos++];
+ }
+ }
+
+ public override void Write(byte[] buf, int off, int len)
+ {
+ lock (this)
+ {
+ CheckOpen();
+ m_buf.Write(buf, off, len);
+ Monitor.PulseAll(this);
+ }
+ }
+
+ public override void WriteByte(byte value)
+ {
+ lock (this)
+ {
+ CheckOpen();
+ m_buf.WriteByte(value);
+ Monitor.PulseAll(m_buf);
+ }
+ }
+
+ private void CheckOpen()
+ {
+ if (m_closed)
+ throw new ObjectDisposedException(this.GetType().Name);
+ }
+
+ private void WaitForData()
+ {
+ while (m_readPos >= m_other.m_buf.Position && !m_other.m_closed)
+ {
+ Monitor.Wait(m_other);
+ }
+ }
+ }
+}
diff --git a/crypto/test/src/tls/test/PrfTest.cs b/crypto/test/src/tls/test/PrfTest.cs
new file mode 100644
index 000000000..de00166ed
--- /dev/null
+++ b/crypto/test/src/tls/test/PrfTest.cs
@@ -0,0 +1,97 @@
+using System;
+
+using NUnit.Framework;
+
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Tls.Crypto;
+using Org.BouncyCastle.Tls.Crypto.Impl.BC;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Encoders;
+
+namespace Org.BouncyCastle.Tls.Tests
+{
+ [TestFixture]
+ public class PrfTest
+ {
+ [Test]
+ public void TestLwTls11()
+ {
+ byte[] pre_master_secret = Hex.Decode("86051948e4d9a0cd273b6cd3a76557fc695e2ad9517cda97081ed009588a20ab48d0b128de8f917da74e711879460b60");
+ byte[] serverHello_random = Hex.Decode("55f1f273d4cdd4abb97f6856ed10f83a799dc42403c3f60c4e504419db4fd727");
+ byte[] clientHello_random = Hex.Decode("0b71e1f7232e675112510cf654a5e6280b3bd8ff078b67ec55276bfaddb92075");
+ byte[] server_random = Hex.Decode("a62615ee7fee41993588b2542735f90910c5a0f9c5dcb64898fdf3e90dc72a5f");
+ byte[] client_random = Hex.Decode("7798a130b732d7789e59a5fc14ad331ae91199f7d122e7fd4a594036b0694873");
+ byte[] master_secret = Hex.Decode("37841ef801f8cbdb49b6a164025de3e0ea8169604ffe80bd98b45cdd34105251cedac7223045ff4c7b67c8a12bf3141c");
+ byte[] key_block = Hex.Decode("c520e2409fa54facd3da01910f50a28f2f50986beb56b0c7b4cee9122e8f7428b7f7b8277bda931c71d35fdc2ea92127a5a143f63fe145275af5bcdab26113deffbb87a67f965b3964ea1ca29df1841c1708e6f42aacd87c12c4471913f61bb994fe3790b735dd11");
+
+ byte[] msSeed = Arrays.Concatenate(clientHello_random, serverHello_random);
+
+ BcTlsCrypto crypto = new BcTlsCrypto(new SecureRandom());
+ TlsSecret masterSecret = new BcTlsSecret(crypto, pre_master_secret)
+ .DeriveUsingPrf(PrfAlgorithm.tls_prf_legacy, ExporterLabel.master_secret, msSeed, master_secret.Length);
+
+ Assert.IsTrue(Arrays.AreEqual(master_secret, masterSecret.Extract()), "master secret wrong");
+
+ byte[] keSeed = Arrays.Concatenate(server_random, client_random);
+
+ TlsSecret keyExpansion = new BcTlsSecret(crypto, master_secret)
+ .DeriveUsingPrf(PrfAlgorithm.tls_prf_legacy, ExporterLabel.key_expansion, keSeed, key_block.Length);
+
+ Assert.IsTrue(Arrays.AreEqual(key_block, keyExpansion.Extract()), "key expansion error");
+ }
+
+ [Test]
+ public void TestLwTls12_Sha256Prf()
+ {
+ byte[] pre_master_secret = Hex.Decode("f8938ecc9edebc5030c0c6a441e213cd24e6f770a50dda07876f8d55da062bcadb386b411fd4fe4313a604fce6c17fbc");
+ byte[] serverHello_random = Hex.Decode("f6c9575ed7ddd73e1f7d16eca115415812a43c2b747daaaae043abfb50053fce");
+ byte[] clientHello_random = Hex.Decode("36c129d01a3200894b9179faac589d9835d58775f9b5ea3587cb8fd0364cae8c");
+ byte[] server_random = Hex.Decode("ae6c806f8ad4d80784549dff28a4b58fd837681a51d928c3e30ee5ff14f39868");
+ byte[] client_random = Hex.Decode("62e1fd91f23f558a605f28478c58cf72637b89784d959df7e946d3f07bd1b616");
+ byte[] master_secret = Hex.Decode("202c88c00f84a17a20027079604787461176455539e705be730890602c289a5001e34eeb3a043e5d52a65e66125188bf");
+ byte[] key_block = Hex.Decode("d06139889fffac1e3a71865f504aa5d0d2a2e89506c6f2279b670c3e1b74f531016a2530c51a3a0f7e1d6590d0f0566b2f387f8d11fd4f731cdd572d2eae927f6f2f81410b25e6960be68985add6c38445ad9f8c64bf8068bf9a6679485d966f1ad6f68b43495b10a683755ea2b858d70ccac7ec8b053c6bd41ca299d4e51928");
+
+ byte[] msSeed = Arrays.Concatenate(clientHello_random, serverHello_random);
+
+ BcTlsCrypto crypto = new BcTlsCrypto(new SecureRandom());
+ TlsSecret masterSecret = new BcTlsSecret(crypto, pre_master_secret)
+ .DeriveUsingPrf(PrfAlgorithm.tls_prf_sha256, ExporterLabel.master_secret, msSeed, master_secret.Length);
+
+ Assert.IsTrue(Arrays.AreEqual(master_secret, masterSecret.Extract()), "master secret wrong");
+
+ byte[] keSeed = Arrays.Concatenate(server_random, client_random);
+
+ TlsSecret keyExpansion = new BcTlsSecret(crypto, master_secret)
+ .DeriveUsingPrf(PrfAlgorithm.tls_prf_sha256, ExporterLabel.key_expansion, keSeed, key_block.Length);
+
+ Assert.IsTrue(Arrays.AreEqual(key_block, keyExpansion.Extract()), "key expansion error");
+ }
+
+ [Test]
+ public void TestLwTls12_Sha384Prf()
+ {
+ byte[] pre_master_secret = Hex.Decode("a5e2642633f5b8c81ad3fe0c2fe3a8e5ef806b06121dd10df4bb0fe857bfdcf522558e05d2682c9a80c741a3aab1716f");
+ byte[] serverHello_random = Hex.Decode("cb6e0b3eb02976b6466dfa9651c2919414f1648fd3a7838d02153e5bd39535b6");
+ byte[] clientHello_random = Hex.Decode("abe4bf5527429ac8eb13574d2709e8012bd1a113c6d3b1d3aa2c3840518778ac");
+ byte[] server_random = Hex.Decode("1b1c8568344a65c30828e7483c0e353e2c68641c9551efae6927d9cd627a107c");
+ byte[] client_random = Hex.Decode("954b5fe1849c2ede177438261f099a2fcd884d001b9fe1de754364b1f6a6dd8e");
+ byte[] master_secret = Hex.Decode("b4d49bfa87747fe815457bc3da15073d6ac73389e703079a3503c09e14bd559a5b3c7c601c7365f6ea8c68d3d9596827");
+ byte[] key_block = Hex.Decode("10fd89ef689c7ef033387b8a8f3e5e8e7c11f680f6bdd71fbac3246a73e98d45d03185dde686e6b2369e4503e9dc5a6d2cee3e2bf2fa3f41d3de57dff3e197c8a9d5f74cc2d277119d894f8584b07a0a5822f0bd68b3433ec6adaf5c9406c5f3ddbb71bbe17ce98f3d4d5893d3179ef369f57aad908e2bf710639100c3ce7e0c");
+
+ byte[] msSeed = Arrays.Concatenate(clientHello_random, serverHello_random);
+
+ BcTlsCrypto crypto = new BcTlsCrypto(new SecureRandom());
+ TlsSecret masterSecret = new BcTlsSecret(crypto, pre_master_secret)
+ .DeriveUsingPrf(PrfAlgorithm.tls_prf_sha384, ExporterLabel.master_secret, msSeed, master_secret.Length);
+
+ Assert.IsTrue(Arrays.AreEqual(master_secret, masterSecret.Extract()), "master secret wrong");
+
+ byte[] keSeed = Arrays.Concatenate(server_random, client_random);
+
+ TlsSecret keyExpansion = new BcTlsSecret(crypto, master_secret)
+ .DeriveUsingPrf(PrfAlgorithm.tls_prf_sha384, ExporterLabel.key_expansion, keSeed, key_block.Length);
+
+ Assert.IsTrue(Arrays.AreEqual(key_block, keyExpansion.Extract()), "key expansion error");
+ }
+ }
+}
diff --git a/crypto/test/src/tls/test/PskTlsClientTest.cs b/crypto/test/src/tls/test/PskTlsClientTest.cs
new file mode 100644
index 000000000..62cfc0dc2
--- /dev/null
+++ b/crypto/test/src/tls/test/PskTlsClientTest.cs
@@ -0,0 +1,113 @@
+using System;
+using System.IO;
+using System.Net.Sockets;
+using System.Text;
+
+using NUnit.Framework;
+
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Date;
+
+namespace Org.BouncyCastle.Tls.Tests
+{
+ /// <summary>A simple test designed to conduct a TLS handshake with an external TLS server.</summary>
+ /// <remarks>
+ /// Please refer to GnuTLSSetup.html or OpenSSLSetup.html (under 'docs'), and x509-*.pem files in this package
+ /// (under 'src/test/resources') for help configuring an external TLS server.<br/<br/
+ /// In both cases, extra options are required to enable PSK ciphersuites and configure identities/keys.
+ /// </remarks>
+ [TestFixture]
+ public class PskTlsClientTest
+ {
+ [Test, Ignore]
+ public void TestConnection()
+ {
+ string host = "localhost";
+ int port = 5556;
+
+ long time1 = DateTimeUtilities.CurrentUnixMs();
+
+ /*
+ * Note: This is the default PSK identity for 'openssl s_server' testing, the server must be
+ * started with "-psk 6161616161" to make the keys match, and possibly the "-psk_hint"
+ * option should be present.
+ */
+ //string psk_identity = "Client_identity";
+ //byte[] psk = new byte[] { 0x61, 0x61, 0x61, 0x61, 0x61 };
+
+ // These correspond to the configuration of MockPskTlsServer
+ string psk_identity = "client";
+ byte[] psk = Strings.ToUtf8ByteArray("TLS_TEST_PSK");
+
+ BasicTlsPskIdentity pskIdentity = new BasicTlsPskIdentity(psk_identity, psk);
+
+ MockPskTlsClient client = new MockPskTlsClient(null, pskIdentity);
+ TlsClientProtocol protocol = OpenTlsClientConnection(host, port, client);
+ protocol.Close();
+
+ long time2 = DateTimeUtilities.CurrentUnixMs();
+ Console.WriteLine("Elapsed 1: " + (time2 - time1) + "ms");
+
+ client = new MockPskTlsClient(client.GetSessionToResume(), pskIdentity);
+ protocol = OpenTlsClientConnection(host, port, client);
+
+ long time3 = DateTimeUtilities.CurrentUnixMs();
+ Console.WriteLine("Elapsed 2: " + (time3 - time2) + "ms");
+
+ Http11Get(host, port, protocol.Stream);
+
+ protocol.Close();
+ }
+
+ private static void Http11Get(string host, int port, Stream s)
+ {
+ WriteUtf8Line(s, "GET / HTTP/1.1");
+ //WriteUtf8Line(s, "Host: " + host + ":" + port);
+ WriteUtf8Line(s, "");
+ s.Flush();
+
+ Console.WriteLine("---");
+
+ string[] ends = new string[] { "</HTML>", "HTTP/1.1 3", "HTTP/1.1 4" };
+
+ StreamReader reader = new StreamReader(s);
+
+ bool finished = false;
+ string line;
+ while (!finished && (line = reader.ReadLine()) != null)
+ {
+ Console.WriteLine("<<< " + line);
+
+ string upperLine = TlsTestUtilities.ToUpperInvariant(line);
+
+ // TEST CODE ONLY. This is not a robust way of parsing the result!
+ foreach (string end in ends)
+ {
+ if (upperLine.Contains(end))
+ {
+ finished = true;
+ break;
+ }
+ }
+ }
+
+ Console.Out.Flush();
+ }
+
+ private static TlsClientProtocol OpenTlsClientConnection(string hostname, int port, TlsClient client)
+ {
+ TcpClient tcp = new TcpClient(hostname, port);
+
+ TlsClientProtocol protocol = new TlsClientProtocol(tcp.GetStream());
+ protocol.Connect(client);
+ return protocol;
+ }
+
+ private static void WriteUtf8Line(Stream output, string line)
+ {
+ byte[] buf = Encoding.UTF8.GetBytes(line + "\r\n");
+ output.Write(buf, 0, buf.Length);
+ Console.WriteLine(">>> " + line);
+ }
+ }
+}
diff --git a/crypto/test/src/tls/test/PskTlsServerTest.cs b/crypto/test/src/tls/test/PskTlsServerTest.cs
new file mode 100644
index 000000000..9d87a8d35
--- /dev/null
+++ b/crypto/test/src/tls/test/PskTlsServerTest.cs
@@ -0,0 +1,82 @@
+using System;
+using System.IO;
+using System.Net;
+using System.Net.Sockets;
+using System.Threading;
+
+using NUnit.Framework;
+
+using Org.BouncyCastle.Utilities.IO;
+
+namespace Org.BouncyCastle.Tls.Tests
+{
+ /// <summary>A simple test designed to conduct a TLS handshake with an external TLS client.</summary>
+ /// <remarks>
+ /// Please refer to GnuTLSSetup.html or OpenSSLSetup.html (under 'docs'), and x509-*.pem files in this package
+ /// (under 'src/test/resources') for help configuring an external TLS client.
+ /// </remarks>
+ [TestFixture]
+ public class PskTlsServerTest
+ {
+ [Test, Ignore]
+ public void TestConnection()
+ {
+ int port = 5556;
+
+ TcpListener ss = new TcpListener(IPAddress.Any, port);
+ ss.Start();
+ Stream stdout = Console.OpenStandardOutput();
+ try
+ {
+ while (true)
+ {
+ TcpClient s = ss.AcceptTcpClient();
+ Console.WriteLine("--------------------------------------------------------------------------------");
+ Console.WriteLine("Accepted " + s);
+ Server serverRun = new Server(s, stdout);
+ Thread t = new Thread(new ThreadStart(serverRun.Run));
+ t.Start();
+ }
+ }
+ finally
+ {
+ ss.Stop();
+ }
+ }
+
+ internal class Server
+ {
+ private readonly TcpClient s;
+ private readonly Stream stdout;
+
+ internal Server(TcpClient s, Stream stdout)
+ {
+ this.s = s;
+ this.stdout = stdout;
+ }
+
+ public void Run()
+ {
+ try
+ {
+ MockPskTlsServer server = new MockPskTlsServer();
+ TlsServerProtocol serverProtocol = new TlsServerProtocol(s.GetStream());
+ serverProtocol.Accept(server);
+ Stream log = new TeeOutputStream(serverProtocol.Stream, stdout);
+ Streams.PipeAll(serverProtocol.Stream, log);
+ serverProtocol.Close();
+ }
+ finally
+ {
+ try
+ {
+ s.Close();
+ }
+ catch (IOException)
+ {
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/crypto/test/src/tls/test/TlsClientTest.cs b/crypto/test/src/tls/test/TlsClientTest.cs
new file mode 100644
index 000000000..27e5f342b
--- /dev/null
+++ b/crypto/test/src/tls/test/TlsClientTest.cs
@@ -0,0 +1,97 @@
+using System;
+using System.IO;
+using System.Net.Sockets;
+using System.Text;
+
+using NUnit.Framework;
+
+using Org.BouncyCastle.Utilities.Date;
+
+namespace Org.BouncyCastle.Tls.Tests
+{
+ /// <summary>A simple test designed to conduct a TLS handshake with an external TLS server.</summary>
+ /// <remarks>
+ /// Please refer to GnuTLSSetup.html or OpenSSLSetup.html (under 'docs'), and x509-*.pem files in this package
+ /// (under 'src/test/resources') for help configuring an external TLS server.
+ /// </remarks>
+ [TestFixture]
+ public class TlsClientTest
+ {
+ [Test, Ignore]
+ public void TestConnection()
+ {
+ string host = "localhost";
+ int port = 5556;
+
+ long time1 = DateTimeUtilities.CurrentUnixMs();
+
+ MockTlsClient client = new MockTlsClient(null);
+ TlsClientProtocol protocol = OpenTlsClientConnection(host, port, client);
+ protocol.Close();
+
+ long time2 = DateTimeUtilities.CurrentUnixMs();
+ Console.WriteLine("Elapsed 1: " + (time2 - time1) + "ms");
+
+ client = new MockTlsClient(client.GetSessionToResume());
+ protocol = OpenTlsClientConnection(host, port, client);
+
+ long time3 = DateTimeUtilities.CurrentUnixMs();
+ Console.WriteLine("Elapsed 2: " + (time3 - time2) + "ms");
+
+ Http11Get(host, port, protocol.Stream);
+
+ protocol.Close();
+ }
+
+ private static void Http11Get(string host, int port, Stream s)
+ {
+ WriteUtf8Line(s, "GET / HTTP/1.1");
+ //WriteUtf8Line(s, "Host: " + host + ":" + port);
+ WriteUtf8Line(s, "");
+ s.Flush();
+
+ Console.WriteLine("---");
+
+ string[] ends = new string[] { "</HTML>", "HTTP/1.1 3", "HTTP/1.1 4" };
+
+ StreamReader reader = new StreamReader(s);
+
+ bool finished = false;
+ string line;
+ while (!finished && (line = reader.ReadLine()) != null)
+ {
+ Console.WriteLine("<<< " + line);
+
+ string upperLine = TlsTestUtilities.ToUpperInvariant(line);
+
+ // TEST CODE ONLY. This is not a robust way of parsing the result!
+ foreach (string end in ends)
+ {
+ if (upperLine.Contains(end))
+ {
+ finished = true;
+ break;
+ }
+ }
+ }
+
+ Console.Out.Flush();
+ }
+
+ private static TlsClientProtocol OpenTlsClientConnection(string hostname, int port, TlsClient client)
+ {
+ TcpClient tcp = new TcpClient(hostname, port);
+
+ TlsClientProtocol protocol = new TlsClientProtocol(tcp.GetStream());
+ protocol.Connect(client);
+ return protocol;
+ }
+
+ private static void WriteUtf8Line(Stream output, string line)
+ {
+ byte[] buf = Encoding.UTF8.GetBytes(line + "\r\n");
+ output.Write(buf, 0, buf.Length);
+ Console.WriteLine(">>> " + line);
+ }
+ }
+}
diff --git a/crypto/test/src/tls/test/TlsProtocolNonBlockingTest.cs b/crypto/test/src/tls/test/TlsProtocolNonBlockingTest.cs
new file mode 100644
index 000000000..56cd39e87
--- /dev/null
+++ b/crypto/test/src/tls/test/TlsProtocolNonBlockingTest.cs
@@ -0,0 +1,126 @@
+using System;
+using System.IO;
+
+using NUnit.Framework;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Tls.Tests
+{
+ [TestFixture]
+ public class TlsProtocolNonBlockingTest
+ {
+ [Test]
+ public void TestClientServerFragmented()
+ {
+ // tests if it's really non-blocking when partial records arrive
+ ImplTestClientServer(true);
+ }
+
+ [Test]
+ public void TestClientServerNonFragmented()
+ {
+ ImplTestClientServer(false);
+ }
+
+ private static void ImplTestClientServer(bool fragment)
+ {
+ TlsClientProtocol clientProtocol = new TlsClientProtocol();
+ TlsServerProtocol serverProtocol = new TlsServerProtocol();
+
+ MockTlsClient client = new MockTlsClient(null);
+ MockTlsServer server = new MockTlsServer();
+
+ clientProtocol.Connect(client);
+ serverProtocol.Accept(server);
+
+ // pump handshake
+ bool hadDataFromServer = true;
+ bool hadDataFromClient = true;
+ while (hadDataFromServer || hadDataFromClient)
+ {
+ hadDataFromServer = PumpData(serverProtocol, clientProtocol, fragment);
+ hadDataFromClient = PumpData(clientProtocol, serverProtocol, fragment);
+ }
+
+ // send data in both directions
+ byte[] data = new byte[1024];
+ client.Crypto.SecureRandom.NextBytes(data);
+
+ WriteAndRead(clientProtocol, serverProtocol, data, fragment);
+ WriteAndRead(serverProtocol, clientProtocol, data, fragment);
+
+ // close the connection
+ clientProtocol.Close();
+ PumpData(clientProtocol, serverProtocol, fragment);
+ serverProtocol.CloseInput();
+ CheckClosed(serverProtocol);
+ CheckClosed(clientProtocol);
+ }
+
+ private static void WriteAndRead(TlsProtocol writer, TlsProtocol reader, byte[] data, bool fragment)
+ {
+ int dataSize = data.Length;
+ writer.WriteApplicationData(data, 0, dataSize);
+ PumpData(writer, reader, fragment);
+
+ Assert.AreEqual(dataSize, reader.GetAvailableInputBytes());
+ byte[] readData = new byte[dataSize];
+ reader.ReadInput(readData, 0, dataSize);
+ AssertArrayEquals(data, readData);
+ }
+
+ private static bool PumpData(TlsProtocol from, TlsProtocol to, bool fragment)
+ {
+ int byteCount = from.GetAvailableOutputBytes();
+ if (byteCount == 0)
+ return false;
+
+ if (fragment)
+ {
+ byte[] buffer = new byte[1];
+ while (from.GetAvailableOutputBytes() > 0)
+ {
+ from.ReadOutput(buffer, 0, 1);
+ to.OfferInput(buffer);
+ }
+ }
+ else
+ {
+ byte[] buffer = new byte[byteCount];
+ from.ReadOutput(buffer, 0, buffer.Length);
+ to.OfferInput(buffer);
+ }
+
+ return true;
+ }
+
+ private static void CheckClosed(TlsProtocol protocol)
+ {
+ Assert.IsTrue(protocol.IsClosed);
+
+ try
+ {
+ protocol.OfferInput(new byte[10]);
+ Assert.Fail("Input was accepted after close");
+ }
+ catch (IOException e)
+ {
+ }
+
+ try
+ {
+ protocol.WriteApplicationData(new byte[10], 0, 10);
+ Assert.Fail("Output was accepted after close");
+ }
+ catch (IOException e)
+ {
+ }
+ }
+
+ private static void AssertArrayEquals(byte[] a, byte[] b)
+ {
+ Assert.IsTrue(Arrays.AreEqual(a, b));
+ }
+ }
+}
diff --git a/crypto/test/src/tls/test/TlsProtocolTest.cs b/crypto/test/src/tls/test/TlsProtocolTest.cs
new file mode 100644
index 000000000..b4f79f9ba
--- /dev/null
+++ b/crypto/test/src/tls/test/TlsProtocolTest.cs
@@ -0,0 +1,75 @@
+using System;
+using System.IO;
+using System.Threading;
+
+using NUnit.Framework;
+
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.IO;
+
+namespace Org.BouncyCastle.Tls.Tests
+{
+ [TestFixture]
+ public class TlsProtocolTest
+ {
+ [Test]
+ public void TestClientServer()
+ {
+ PipedStream clientPipe = new PipedStream();
+ PipedStream serverPipe = new PipedStream(clientPipe);
+
+ TlsClientProtocol clientProtocol = new TlsClientProtocol(clientPipe);
+ TlsServerProtocol serverProtocol = new TlsServerProtocol(serverPipe);
+
+ MockTlsClient client = new MockTlsClient(null);
+ MockTlsServer server = new MockTlsServer();
+
+ Server serverRun = new Server(serverProtocol, server);
+ Thread serverThread = new Thread(new ThreadStart(serverRun.Run));
+ serverThread.Start();
+
+ clientProtocol.Connect(client);
+
+ byte[] data = new byte[1000];
+ client.Crypto.SecureRandom.NextBytes(data);
+
+ Stream output = clientProtocol.Stream;
+ output.Write(data, 0, data.Length);
+
+ byte[] echo = new byte[data.Length];
+ int count = Streams.ReadFully(clientProtocol.Stream, echo);
+
+ Assert.AreEqual(count, data.Length);
+ Assert.IsTrue(Arrays.AreEqual(data, echo));
+
+ output.Close();
+
+ serverThread.Join();
+ }
+
+ internal class Server
+ {
+ private readonly TlsServerProtocol m_serverProtocol;
+ private readonly TlsServer m_server;
+
+ internal Server(TlsServerProtocol serverProtocol, TlsServer server)
+ {
+ this.m_serverProtocol = serverProtocol;
+ this.m_server = server;
+ }
+
+ public void Run()
+ {
+ try
+ {
+ m_serverProtocol.Accept(m_server);
+ Streams.PipeAll(m_serverProtocol.Stream, m_serverProtocol.Stream);
+ m_serverProtocol.Close();
+ }
+ catch (Exception e)
+ {
+ }
+ }
+ }
+ }
+}
diff --git a/crypto/test/src/tls/test/TlsPskProtocolTest.cs b/crypto/test/src/tls/test/TlsPskProtocolTest.cs
new file mode 100644
index 000000000..97e6f133b
--- /dev/null
+++ b/crypto/test/src/tls/test/TlsPskProtocolTest.cs
@@ -0,0 +1,75 @@
+using System;
+using System.IO;
+using System.Threading;
+
+using NUnit.Framework;
+
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.IO;
+
+namespace Org.BouncyCastle.Tls.Tests
+{
+ [TestFixture]
+ public class TlsPskProtocolTest
+ {
+ [Test]
+ public void TestClientServer()
+ {
+ PipedStream clientPipe = new PipedStream();
+ PipedStream serverPipe = new PipedStream(clientPipe);
+
+ TlsClientProtocol clientProtocol = new TlsClientProtocol(clientPipe);
+ TlsServerProtocol serverProtocol = new TlsServerProtocol(serverPipe);
+
+ MockPskTlsClient client = new MockPskTlsClient(null);
+ MockPskTlsServer server = new MockPskTlsServer();
+
+ Server serverRun = new Server(serverProtocol, server);
+ Thread serverThread = new Thread(new ThreadStart(serverRun.Run));
+ serverThread.Start();
+
+ clientProtocol.Connect(client);
+
+ byte[] data = new byte[1000];
+ client.Crypto.SecureRandom.NextBytes(data);
+
+ Stream output = clientProtocol.Stream;
+ output.Write(data, 0, data.Length);
+
+ byte[] echo = new byte[data.Length];
+ int count = Streams.ReadFully(clientProtocol.Stream, echo);
+
+ Assert.AreEqual(count, data.Length);
+ Assert.IsTrue(Arrays.AreEqual(data, echo));
+
+ output.Close();
+
+ serverThread.Join();
+ }
+
+ internal class Server
+ {
+ private readonly TlsServerProtocol m_serverProtocol;
+ private readonly TlsServer m_server;
+
+ internal Server(TlsServerProtocol serverProtocol, TlsServer server)
+ {
+ this.m_serverProtocol = serverProtocol;
+ this.m_server = server;
+ }
+
+ public void Run()
+ {
+ try
+ {
+ m_serverProtocol.Accept(m_server);
+ Streams.PipeAll(m_serverProtocol.Stream, m_serverProtocol.Stream);
+ m_serverProtocol.Close();
+ }
+ catch (Exception e)
+ {
+ }
+ }
+ }
+ }
+}
diff --git a/crypto/test/src/tls/test/TlsServerTest.cs b/crypto/test/src/tls/test/TlsServerTest.cs
new file mode 100644
index 000000000..333ff5664
--- /dev/null
+++ b/crypto/test/src/tls/test/TlsServerTest.cs
@@ -0,0 +1,82 @@
+using System;
+using System.IO;
+using System.Net;
+using System.Net.Sockets;
+using System.Threading;
+
+using NUnit.Framework;
+
+using Org.BouncyCastle.Utilities.IO;
+
+namespace Org.BouncyCastle.Tls.Tests
+{
+ /// <summary>A simple test designed to conduct a TLS handshake with an external TLS client.</summary>
+ /// <remarks>
+ /// Please refer to GnuTLSSetup.html or OpenSSLSetup.html (under 'docs'), and x509-*.pem files in this package
+ /// (under 'src/test/resources') for help configuring an external TLS client.
+ /// </remarks>
+ [TestFixture]
+ public class TlsServerTest
+ {
+ [Test, Ignore]
+ public void TestConnection()
+ {
+ int port = 5556;
+
+ TcpListener ss = new TcpListener(IPAddress.Any, port);
+ ss.Start();
+ Stream stdout = Console.OpenStandardOutput();
+ try
+ {
+ while (true)
+ {
+ TcpClient s = ss.AcceptTcpClient();
+ Console.WriteLine("--------------------------------------------------------------------------------");
+ Console.WriteLine("Accepted " + s);
+ Server serverRun = new Server(s, stdout);
+ Thread t = new Thread(new ThreadStart(serverRun.Run));
+ t.Start();
+ }
+ }
+ finally
+ {
+ ss.Stop();
+ }
+ }
+
+ internal class Server
+ {
+ private readonly TcpClient s;
+ private readonly Stream stdout;
+
+ internal Server(TcpClient s, Stream stdout)
+ {
+ this.s = s;
+ this.stdout = stdout;
+ }
+
+ public void Run()
+ {
+ try
+ {
+ MockTlsServer server = new MockTlsServer();
+ TlsServerProtocol serverProtocol = new TlsServerProtocol(s.GetStream());
+ serverProtocol.Accept(server);
+ Stream log = new TeeOutputStream(serverProtocol.Stream, stdout);
+ Streams.PipeAll(serverProtocol.Stream, log);
+ serverProtocol.Close();
+ }
+ finally
+ {
+ try
+ {
+ s.Close();
+ }
+ catch (IOException)
+ {
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/crypto/test/src/tls/test/TlsSrpProtocolTest.cs b/crypto/test/src/tls/test/TlsSrpProtocolTest.cs
new file mode 100644
index 000000000..555b1f1f7
--- /dev/null
+++ b/crypto/test/src/tls/test/TlsSrpProtocolTest.cs
@@ -0,0 +1,75 @@
+using System;
+using System.IO;
+using System.Threading;
+
+using NUnit.Framework;
+
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.IO;
+
+namespace Org.BouncyCastle.Tls.Tests
+{
+ [TestFixture]
+ public class TlsSrpProtocolTest
+ {
+ [Test]
+ public void TestClientServer()
+ {
+ PipedStream clientPipe = new PipedStream();
+ PipedStream serverPipe = new PipedStream(clientPipe);
+
+ TlsClientProtocol clientProtocol = new TlsClientProtocol(clientPipe);
+ TlsServerProtocol serverProtocol = new TlsServerProtocol(serverPipe);
+
+ MockSrpTlsClient client = new MockSrpTlsClient(null, MockSrpTlsServer.TEST_SRP_IDENTITY);
+ MockSrpTlsServer server = new MockSrpTlsServer();
+
+ Server serverRun = new Server(serverProtocol, server);
+ Thread serverThread = new Thread(new ThreadStart(serverRun.Run));
+ serverThread.Start();
+
+ clientProtocol.Connect(client);
+
+ byte[] data = new byte[1000];
+ client.Crypto.SecureRandom.NextBytes(data);
+
+ Stream output = clientProtocol.Stream;
+ output.Write(data, 0, data.Length);
+
+ byte[] echo = new byte[data.Length];
+ int count = Streams.ReadFully(clientProtocol.Stream, echo);
+
+ Assert.AreEqual(count, data.Length);
+ Assert.IsTrue(Arrays.AreEqual(data, echo));
+
+ output.Close();
+
+ serverThread.Join();
+ }
+
+ internal class Server
+ {
+ private readonly TlsServerProtocol m_serverProtocol;
+ private readonly TlsServer m_server;
+
+ internal Server(TlsServerProtocol serverProtocol, TlsServer server)
+ {
+ this.m_serverProtocol = serverProtocol;
+ this.m_server = server;
+ }
+
+ public void Run()
+ {
+ try
+ {
+ m_serverProtocol.Accept(m_server);
+ Streams.PipeAll(m_serverProtocol.Stream, m_serverProtocol.Stream);
+ m_serverProtocol.Close();
+ }
+ catch (Exception e)
+ {
+ }
+ }
+ }
+ }
+}
diff --git a/crypto/test/src/tls/test/TlsTestCase.cs b/crypto/test/src/tls/test/TlsTestCase.cs
new file mode 100644
index 000000000..0489d22c1
--- /dev/null
+++ b/crypto/test/src/tls/test/TlsTestCase.cs
@@ -0,0 +1,182 @@
+using System;
+using System.IO;
+using System.Threading;
+
+using NUnit.Framework;
+
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.IO;
+
+namespace Org.BouncyCastle.Tls.Tests
+{
+ [TestFixture]
+ public class TlsTestCase
+ {
+ private static void CheckTlsVersions(ProtocolVersion[] versions)
+ {
+ if (versions != null)
+ {
+ for (int i = 0; i < versions.Length; ++i)
+ {
+ if (!versions[i].IsTls)
+ throw new InvalidOperationException("Non-TLS version");
+ }
+ }
+ }
+
+ [Test, TestCaseSource(typeof(TlsTestSuite), "Suite")]
+ public void RunTest(TlsTestConfig config)
+ {
+ // Disable the test if it is not being run via TlsTestSuite
+ if (config == null)
+ return;
+
+ CheckTlsVersions(config.clientSupportedVersions);
+ CheckTlsVersions(config.serverSupportedVersions);
+
+ PipedStream clientPipe = new PipedStream();
+ PipedStream serverPipe = new PipedStream(clientPipe);
+
+ NetworkStream clientNet = new NetworkStream(clientPipe);
+ NetworkStream serverNet = new NetworkStream(serverPipe);
+
+ TlsTestClientProtocol clientProtocol = new TlsTestClientProtocol(clientNet, config);
+ TlsTestServerProtocol serverProtocol = new TlsTestServerProtocol(serverNet, config);
+
+ clientProtocol.IsResumableHandshake = true;
+ serverProtocol.IsResumableHandshake = true;
+
+ TlsTestClientImpl clientImpl = new TlsTestClientImpl(config);
+ TlsTestServerImpl serverImpl = new TlsTestServerImpl(config);
+
+ Server serverRun = new Server(this, serverProtocol, serverImpl);
+ Thread serverThread = new Thread(new ThreadStart(serverRun.Run));
+ serverThread.Start();
+
+ Exception caught = null;
+ try
+ {
+ clientProtocol.Connect(clientImpl);
+
+ byte[] data = new byte[1000];
+ clientImpl.Crypto.SecureRandom.NextBytes(data);
+
+ Stream stream = clientProtocol.Stream;
+ stream.Write(data, 0, data.Length);
+
+ byte[] echo = new byte[data.Length];
+ int count = Streams.ReadFully(stream, echo, 0, echo.Length);
+
+ Assert.AreEqual(count, data.Length);
+ Assert.IsTrue(Arrays.AreEqual(data, echo));
+
+ Assert.IsTrue(Arrays.AreEqual(clientImpl.m_tlsServerEndPoint, serverImpl.m_tlsServerEndPoint));
+
+ if (!TlsUtilities.IsTlsV13(clientImpl.m_negotiatedVersion))
+ {
+ Assert.NotNull(clientImpl.m_tlsUnique);
+ Assert.NotNull(serverImpl.m_tlsUnique);
+ }
+ Assert.IsTrue(Arrays.AreEqual(clientImpl.m_tlsUnique, serverImpl.m_tlsUnique));
+
+ stream.Close();
+ }
+ catch (Exception e)
+ {
+ caught = e;
+ LogException(caught);
+ }
+
+ serverRun.AllowExit();
+ serverThread.Join();
+
+ Assert.IsTrue(clientNet.IsClosed, "Client Stream not closed");
+ Assert.IsTrue(serverNet.IsClosed, "Server Stream not closed");
+
+ Assert.AreEqual(config.expectFatalAlertConnectionEnd, clientImpl.FirstFatalAlertConnectionEnd,
+ "Client fatal alert connection end");
+ Assert.AreEqual(config.expectFatalAlertConnectionEnd, serverImpl.FirstFatalAlertConnectionEnd,
+ "Server fatal alert connection end");
+
+ Assert.AreEqual(config.expectFatalAlertDescription, clientImpl.FirstFatalAlertDescription,
+ "Client fatal alert description");
+ Assert.AreEqual(config.expectFatalAlertDescription, serverImpl.FirstFatalAlertDescription,
+ "Server fatal alert description");
+
+ if (config.expectFatalAlertConnectionEnd == -1)
+ {
+ Assert.IsNull(caught, "Unexpected client exception");
+ Assert.IsNull(serverRun.m_caught, "Unexpected server exception");
+ }
+ }
+
+ protected virtual void LogException(Exception e)
+ {
+ if (TlsTestConfig.Debug)
+ {
+ Console.Error.WriteLine(e);
+ Console.Error.Flush();
+ }
+ }
+
+ internal class Server
+ {
+ protected readonly TlsTestCase m_outer;
+ protected readonly TlsServerProtocol m_serverProtocol;
+ protected readonly TlsServer m_server;
+
+ internal bool m_canExit = false;
+ internal Exception m_caught = null;
+
+ internal Server(TlsTestCase outer, TlsTestServerProtocol serverProtocol, TlsServer server)
+ {
+ this.m_outer = outer;
+ this.m_serverProtocol = serverProtocol;
+ this.m_server = server;
+ }
+
+ internal void AllowExit()
+ {
+ lock (this)
+ {
+ m_canExit = true;
+ Monitor.PulseAll(this);
+ }
+ }
+
+ public void Run()
+ {
+ try
+ {
+ m_serverProtocol.Accept(m_server);
+ Streams.PipeAll(m_serverProtocol.Stream, m_serverProtocol.Stream);
+ m_serverProtocol.Close();
+ }
+ catch (Exception e)
+ {
+ m_caught = e;
+ m_outer.LogException(m_caught);
+ }
+
+ WaitExit();
+ }
+
+ protected void WaitExit()
+ {
+ lock (this)
+ {
+ while (!m_canExit)
+ {
+ try
+ {
+ Monitor.Wait(this);
+ }
+ catch (ThreadInterruptedException)
+ {
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/crypto/test/src/tls/test/TlsTestClientImpl.cs b/crypto/test/src/tls/test/TlsTestClientImpl.cs
new file mode 100644
index 000000000..d436df3f7
--- /dev/null
+++ b/crypto/test/src/tls/test/TlsTestClientImpl.cs
@@ -0,0 +1,370 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Tls.Crypto;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Encoders;
+
+namespace Org.BouncyCastle.Tls.Tests
+{
+ internal class TlsTestClientImpl
+ : DefaultTlsClient
+ {
+ private static readonly int[] TestCipherSuites = new int[]
+ {
+ /*
+ * TLS 1.3
+ */
+ CipherSuite.TLS_CHACHA20_POLY1305_SHA256,
+ CipherSuite.TLS_AES_128_GCM_SHA256,
+
+ /*
+ * pre-TLS 1.3
+ */
+ CipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
+ CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
+ CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
+ CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
+ CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
+ CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
+ CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
+ CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
+ CipherSuite.TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
+ CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256,
+ CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,
+ CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
+ CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256,
+ CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256,
+ CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
+ };
+
+ protected readonly TlsTestConfig m_config;
+
+ protected int m_firstFatalAlertConnectionEnd = -1;
+ protected short m_firstFatalAlertDescription = -1;
+
+ internal ProtocolVersion m_negotiatedVersion = null;
+ internal byte[] m_tlsServerEndPoint = null;
+ internal byte[] m_tlsUnique = null;
+
+ internal TlsTestClientImpl(TlsTestConfig config)
+ : base(TlsTestSuite.GetCrypto(config))
+ {
+ this.m_config = config;
+ }
+
+ internal int FirstFatalAlertConnectionEnd
+ {
+ get { return m_firstFatalAlertConnectionEnd; }
+ }
+
+ internal short FirstFatalAlertDescription
+ {
+ get { return m_firstFatalAlertDescription; }
+ }
+
+ public override IDictionary GetClientExtensions()
+ {
+ IDictionary clientExtensions = base.GetClientExtensions();
+ if (clientExtensions != null)
+ {
+ if (!m_config.clientSendSignatureAlgorithms)
+ {
+ clientExtensions.Remove(ExtensionType.signature_algorithms);
+ this.m_supportedSignatureAlgorithms = null;
+ }
+ if (!m_config.clientSendSignatureAlgorithmsCert)
+ {
+ clientExtensions.Remove(ExtensionType.signature_algorithms_cert);
+ this.m_supportedSignatureAlgorithmsCert = null;
+ }
+ }
+ return clientExtensions;
+ }
+
+ public override IList GetEarlyKeyShareGroups()
+ {
+ if (m_config.clientEmptyKeyShare)
+ return null;
+
+ return base.GetEarlyKeyShareGroups();
+ }
+
+ public override bool IsFallback()
+ {
+ return m_config.clientFallback;
+ }
+
+ public override void NotifyAlertRaised(short alertLevel, short alertDescription, string message,
+ Exception cause)
+ {
+ if (alertLevel == AlertLevel.fatal && m_firstFatalAlertConnectionEnd == -1)
+ {
+ m_firstFatalAlertConnectionEnd = ConnectionEnd.client;
+ m_firstFatalAlertDescription = alertDescription;
+ }
+
+ if (TlsTestConfig.Debug)
+ {
+ TextWriter output = (alertLevel == AlertLevel.fatal) ? Console.Error : Console.Out;
+ output.WriteLine("TLS client raised alert: " + AlertLevel.GetText(alertLevel)
+ + ", " + AlertDescription.GetText(alertDescription));
+ if (message != null)
+ {
+ output.WriteLine("> " + message);
+ }
+ if (cause != null)
+ {
+ output.WriteLine(cause);
+ }
+ }
+ }
+
+ public override void NotifyAlertReceived(short alertLevel, short alertDescription)
+ {
+ if (alertLevel == AlertLevel.fatal && m_firstFatalAlertConnectionEnd == -1)
+ {
+ m_firstFatalAlertConnectionEnd = ConnectionEnd.server;
+ m_firstFatalAlertDescription = alertDescription;
+ }
+
+ if (TlsTestConfig.Debug)
+ {
+ TextWriter output = (alertLevel == AlertLevel.fatal) ? Console.Error : Console.Out;
+ output.WriteLine("TLS client received alert: " + AlertLevel.GetText(alertLevel)
+ + ", " + AlertDescription.GetText(alertDescription));
+ }
+ }
+
+ public override void NotifyHandshakeComplete()
+ {
+ base.NotifyHandshakeComplete();
+
+ m_tlsServerEndPoint = m_context.ExportChannelBinding(ChannelBinding.tls_server_end_point);
+ m_tlsUnique = m_context.ExportChannelBinding(ChannelBinding.tls_unique);
+
+ if (TlsTestConfig.Debug)
+ {
+ Console.WriteLine("TLS client reports 'tls-server-end-point' = " + ToHexString(m_tlsServerEndPoint));
+ Console.WriteLine("TLS client reports 'tls-unique' = " + ToHexString(m_tlsUnique));
+ }
+ }
+
+ public override void NotifyServerVersion(ProtocolVersion serverVersion)
+ {
+ base.NotifyServerVersion(serverVersion);
+
+ this.m_negotiatedVersion = serverVersion;
+
+ if (TlsTestConfig.Debug)
+ {
+ Console.WriteLine("TLS client negotiated " + serverVersion);
+ }
+ }
+
+ public override TlsAuthentication GetAuthentication()
+ {
+ return new MyTlsAuthentication(this, m_context);
+ }
+
+ protected virtual Certificate CorruptCertificate(Certificate cert)
+ {
+ CertificateEntry[] certEntryList = cert.GetCertificateEntryList();
+ CertificateEntry ee = certEntryList[0];
+ TlsCertificate corruptCert = CorruptCertificateSignature(ee.Certificate);
+ certEntryList[0] = new CertificateEntry(corruptCert, ee.Extensions);
+ return new Certificate(cert.GetCertificateRequestContext(), certEntryList);
+ }
+
+ protected virtual TlsCertificate CorruptCertificateSignature(TlsCertificate tlsCertificate)
+ {
+ X509CertificateStructure cert = X509CertificateStructure.GetInstance(tlsCertificate.GetEncoded());
+
+ Asn1EncodableVector v = new Asn1EncodableVector();
+ v.Add(cert.TbsCertificate);
+ v.Add(cert.SignatureAlgorithm);
+ v.Add(CorruptSignature(cert.Signature));
+
+ cert = X509CertificateStructure.GetInstance(new DerSequence(v));
+
+ return Crypto.CreateCertificate(cert.GetEncoded(Asn1Encodable.Der));
+ }
+
+ protected virtual DerBitString CorruptSignature(DerBitString bs)
+ {
+ return new DerBitString(CorruptBit(bs.GetOctets()));
+ }
+
+ protected virtual byte[] CorruptBit(byte[] bs)
+ {
+ bs = Arrays.Clone(bs);
+
+ // Flip a random bit
+ int bit = m_context.Crypto.SecureRandom.Next(bs.Length << 3);
+ bs[bit >> 3] ^= (byte)(1 << (bit & 7));
+
+ return bs;
+ }
+
+ protected override int[] GetSupportedCipherSuites()
+ {
+ return TlsUtilities.GetSupportedCipherSuites(Crypto, TestCipherSuites);
+ }
+
+ protected override ProtocolVersion[] GetSupportedVersions()
+ {
+ if (null != m_config.clientSupportedVersions)
+ {
+ return m_config.clientSupportedVersions;
+ }
+
+ return base.GetSupportedVersions();
+ }
+
+ protected virtual string ToHexString(byte[] data)
+ {
+ return data == null ? "(null)" : Hex.ToHexString(data);
+ }
+
+ internal class MyTlsAuthentication
+ : TlsAuthentication
+ {
+ private readonly TlsTestClientImpl m_outer;
+ private readonly TlsContext m_context;
+
+ internal MyTlsAuthentication(TlsTestClientImpl outer, TlsContext context)
+ {
+ this.m_outer = outer;
+ this.m_context = context;
+ }
+
+ public virtual void NotifyServerCertificate(TlsServerCertificate serverCertificate)
+ {
+ TlsCertificate[] chain = serverCertificate.Certificate.GetCertificateList();
+
+ if (TlsTestConfig.Debug)
+ {
+ Console.WriteLine("TLS client received server certificate chain of length " + chain.Length);
+ for (int i = 0; i < chain.Length; ++i)
+ {
+ X509CertificateStructure entry = X509CertificateStructure.GetInstance(chain[i].GetEncoded());
+ // TODO Create fingerprint based on certificate signature algorithm digest
+ Console.WriteLine(" fingerprint:SHA-256 " + TlsTestUtilities.Fingerprint(entry) + " ("
+ + entry.Subject + ")");
+ }
+ }
+
+ bool isEmpty = serverCertificate == null || serverCertificate.Certificate == null
+ || serverCertificate.Certificate.IsEmpty;
+
+ if (isEmpty)
+ throw new TlsFatalAlert(AlertDescription.bad_certificate);
+
+ string[] trustedCertResources = new string[]{ "x509-server-dsa.pem", "x509-server-ecdh.pem",
+ "x509-server-ecdsa.pem", "x509-server-ed25519.pem", "x509-server-ed448.pem",
+ "x509-server-rsa_pss_256.pem", "x509-server-rsa_pss_384.pem", "x509-server-rsa_pss_512.pem",
+ "x509-server-rsa-enc.pem", "x509-server-rsa-sign.pem" };
+
+ TlsCertificate[] certPath = TlsTestUtilities.GetTrustedCertPath(m_context.Crypto, chain[0],
+ trustedCertResources);
+
+ if (null == certPath)
+ throw new TlsFatalAlert(AlertDescription.bad_certificate);
+
+ if (m_outer.m_config.clientCheckSigAlgOfServerCerts)
+ {
+ TlsUtilities.CheckPeerSigAlgs(m_context, certPath);
+ }
+ }
+
+ public virtual TlsCredentials GetClientCredentials(CertificateRequest certificateRequest)
+ {
+ TlsTestConfig config = m_outer.m_config;
+
+ if (config.serverCertReq == TlsTestConfig.SERVER_CERT_REQ_NONE)
+ throw new InvalidOperationException();
+ if (config.clientAuth == TlsTestConfig.CLIENT_AUTH_NONE)
+ return null;
+
+ bool isTlsV13 = TlsUtilities.IsTlsV13(m_context);
+
+ if (!isTlsV13)
+ {
+ short[] certificateTypes = certificateRequest.CertificateTypes;
+ if (certificateTypes == null || !Arrays.Contains(certificateTypes, ClientCertificateType.rsa_sign))
+ return null;
+ }
+
+ IList supportedSigAlgs = certificateRequest.SupportedSignatureAlgorithms;
+ if (supportedSigAlgs != null && config.clientAuthSigAlg != null)
+ {
+ supportedSigAlgs = new ArrayList(1);
+ supportedSigAlgs.Add(config.clientAuthSigAlg);
+ }
+
+ // TODO[tls13] Check also supportedSigAlgsCert against the chain signature(s)
+
+ TlsCredentialedSigner signerCredentials = TlsTestUtilities.LoadSignerCredentials(m_context,
+ supportedSigAlgs, SignatureAlgorithm.rsa, "x509-client-rsa.pem", "x509-client-key-rsa.pem");
+
+ if (config.clientAuth == TlsTestConfig.CLIENT_AUTH_VALID)
+ return signerCredentials;
+
+ return new MyTlsCredentialedSigner(m_outer, signerCredentials);
+ }
+ }
+
+ internal class MyTlsCredentialedSigner
+ : TlsCredentialedSigner
+ {
+ private readonly TlsTestClientImpl m_outer;
+ private readonly TlsCredentialedSigner m_inner;
+
+ internal MyTlsCredentialedSigner(TlsTestClientImpl outer, TlsCredentialedSigner inner)
+ {
+ this.m_outer = outer;
+ this.m_inner = inner;
+ }
+
+ public virtual byte[] GenerateRawSignature(byte[] hash)
+ {
+ byte[] sig = m_inner.GenerateRawSignature(hash);
+
+ if (m_outer.m_config.clientAuth == TlsTestConfig.CLIENT_AUTH_INVALID_VERIFY)
+ {
+ sig = m_outer.CorruptBit(sig);
+ }
+
+ return sig;
+ }
+
+ public virtual Certificate Certificate
+ {
+ get
+ {
+ Certificate cert = m_inner.Certificate;
+
+ if (m_outer.m_config.clientAuth == TlsTestConfig.CLIENT_AUTH_INVALID_CERT)
+ {
+ cert = m_outer.CorruptCertificate(cert);
+ }
+
+ return cert;
+ }
+ }
+
+ public virtual SignatureAndHashAlgorithm SignatureAndHashAlgorithm
+ {
+ get { return m_inner.SignatureAndHashAlgorithm; }
+ }
+
+ public virtual TlsStreamSigner GetStreamSigner()
+ {
+ return null;
+ }
+ };
+ }
+}
diff --git a/crypto/test/src/tls/test/TlsTestClientProtocol.cs b/crypto/test/src/tls/test/TlsTestClientProtocol.cs
new file mode 100644
index 000000000..f7e94680a
--- /dev/null
+++ b/crypto/test/src/tls/test/TlsTestClientProtocol.cs
@@ -0,0 +1,32 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Tls.Tests
+{
+ internal class TlsTestClientProtocol
+ : TlsClientProtocol
+ {
+ protected readonly TlsTestConfig m_config;
+
+ internal TlsTestClientProtocol(Stream stream, TlsTestConfig config)
+ : this(stream, stream, config)
+ {
+ }
+
+ internal TlsTestClientProtocol(Stream input, Stream output, TlsTestConfig config)
+ : base(input, output)
+ {
+ this.m_config = config;
+ }
+
+ protected override void SendCertificateVerifyMessage(DigitallySigned certificateVerify)
+ {
+ if (certificateVerify.Algorithm != null && m_config.clientAuthSigAlgClaimed != null)
+ {
+ certificateVerify = new DigitallySigned(m_config.clientAuthSigAlgClaimed, certificateVerify.Signature);
+ }
+
+ base.SendCertificateVerifyMessage(certificateVerify);
+ }
+ }
+}
diff --git a/crypto/test/src/tls/test/TlsTestConfig.cs b/crypto/test/src/tls/test/TlsTestConfig.cs
new file mode 100644
index 000000000..a15d4e535
--- /dev/null
+++ b/crypto/test/src/tls/test/TlsTestConfig.cs
@@ -0,0 +1,128 @@
+using System;
+using System.Collections;
+
+namespace Org.BouncyCastle.Tls.Tests
+{
+ public class TlsTestConfig
+ {
+ // TODO[tls-port]
+ public static readonly bool Debug = true;
+
+ /// <summary>Client does not authenticate, ignores any certificate request.</summary>
+ public const int CLIENT_AUTH_NONE = 0;
+
+ /// <summary>Client will authenticate if it receives a certificate request.</summary>
+ public const int CLIENT_AUTH_VALID = 1;
+
+ /// <summary>Client will authenticate if it receives a certificate request, with an invalid certificate.
+ /// </summary>
+ public const int CLIENT_AUTH_INVALID_CERT = 2;
+
+ /// <summary>Client will authenticate if it receives a certificate request, with an invalid CertificateVerify
+ /// signature.</summary>
+ public const int CLIENT_AUTH_INVALID_VERIFY = 3;
+
+ public const int CRYPTO_BC = 0;
+
+ /// <summary>Server will not request a client certificate.</summary>
+ public const int SERVER_CERT_REQ_NONE = 0;
+
+ /// <summary>Server will request a client certificate but receiving one is optional.</summary>
+ public const int SERVER_CERT_REQ_OPTIONAL = 1;
+
+ /// <summary>Server will request a client certificate and receiving one is mandatory.</summary>
+ public const int SERVER_CERT_REQ_MANDATORY = 2;
+
+ /// <summary>Configures the client authentication behaviour of the test client. Use CLIENT_AUTH_* constants.
+ /// </summary>
+ public int clientAuth = CLIENT_AUTH_VALID;
+
+ /// <summary>If not null, and TLS 1.2 or higher is negotiated, selects a fixed signature/ hash algorithm to be
+ /// used for the CertificateVerify signature(if one is sent).</summary>
+ public SignatureAndHashAlgorithm clientAuthSigAlg = null;
+
+ /// <summary>If not null, and TLS 1.2 or higher is negotiated, selects a fixed signature/ hash algorithm to be
+ /// _claimed_ in the CertificateVerify (if one is sent), independently of what was actually used.</summary>
+ public SignatureAndHashAlgorithm clientAuthSigAlgClaimed = null;
+
+ /// <summary>Control whether the client will call
+ /// <see cref="TlsUtilities.CheckPeerSigAlgs(TlsContext, Crypto.TlsCertificate[])"/> to check the server
+ /// certificate chain.</summary>
+ public bool clientCheckSigAlgOfServerCerts = true;
+
+ public int clientCrypto = CRYPTO_BC;
+
+ /// <summary>Configures whether the client will send an empty key_share extension in initial ClientHello.
+ /// </summary>
+ public bool clientEmptyKeyShare = false;
+
+ /// <summary>Configures whether the client will indicate version fallback via TLS_FALLBACK_SCSV.</summary>
+ public bool clientFallback = false;
+
+ /// <summary>Configures whether a (TLS 1.2+) client may send the signature_algorithms extension in ClientHello.
+ /// </summary>
+ public bool clientSendSignatureAlgorithms = true;
+
+ /// <summary>Configures whether a (TLS 1.2+) client may send the signature_algorithms_cert extension in
+ /// ClientHello.</summary>
+ public bool clientSendSignatureAlgorithmsCert = true;
+
+ /// <summary>Configures the supported protocol versions for the client. If null, uses the library's default.
+ /// </summary>
+ public ProtocolVersion[] clientSupportedVersions = null;
+
+ /// <summary>If not null, and TLS 1.2 or higher is negotiated, selects a fixed signature/ hash algorithm to be
+ /// used for the ServerKeyExchange signature(if one is sent).</summary>
+ public SignatureAndHashAlgorithm serverAuthSigAlg = null;
+
+ /// <summary>Configures whether the test server will send a certificate request.</summary>
+ public int serverCertReq = SERVER_CERT_REQ_OPTIONAL;
+
+ /// <summary>If TLS 1.2 or higher is negotiated, configures the set of supported signature algorithms in the
+ /// CertificateRequest (if one is sent). If null, uses a default set.</summary>
+ public IList serverCertReqSigAlgs = null;
+
+ /// <summary>Control whether the server will call
+ /// <see cref="TlsUtilities.CheckPeerSigAlgs(TlsContext, Crypto.TlsCertificate[])"/> to check the client
+ /// certificate chain.</summary>
+ public bool serverCheckSigAlgOfClientCerts = true;
+
+ public int serverCrypto = CRYPTO_BC;
+
+ /// <summary>Configures a protocol version the server will unconditionally negotiate.</summary>
+ /// <remarks>
+ /// Ignored if null.
+ /// </remarks>
+ public ProtocolVersion serverNegotiateVersion = null;
+
+ /// <summary>Configures the supported protocol versions for the server.</summary>
+ /// <remarks>
+ /// If null, uses the library's default.
+ /// </remarks>
+ public ProtocolVersion[] serverSupportedVersions = null;
+
+ /// <summary>Configures the connection end at which a fatal alert is expected to be raised.</summary>
+ /// <remarks>
+ /// Use <see cref="ConnectionEnd"/> constants.
+ /// </remarks>
+ public int expectFatalAlertConnectionEnd = -1;
+
+ /// <summary>Configures the type of fatal alert expected to be raised.</summary>
+ /// <remarks>
+ /// Use <see cref="AlertDescription"/> constants.
+ /// </remarks>
+ public short expectFatalAlertDescription = -1;
+
+ public virtual void ExpectClientFatalAlert(short alertDescription)
+ {
+ this.expectFatalAlertConnectionEnd = ConnectionEnd.client;
+ this.expectFatalAlertDescription = alertDescription;
+ }
+
+ public virtual void ExpectServerFatalAlert(short alertDescription)
+ {
+ this.expectFatalAlertConnectionEnd = ConnectionEnd.server;
+ this.expectFatalAlertDescription = alertDescription;
+ }
+ }
+}
diff --git a/crypto/test/src/tls/test/TlsTestServerImpl.cs b/crypto/test/src/tls/test/TlsTestServerImpl.cs
new file mode 100644
index 000000000..6bc4d315d
--- /dev/null
+++ b/crypto/test/src/tls/test/TlsTestServerImpl.cs
@@ -0,0 +1,310 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Tls.Crypto;
+using Org.BouncyCastle.Utilities.Encoders;
+
+namespace Org.BouncyCastle.Tls.Tests
+{
+ internal class TlsTestServerImpl
+ : DefaultTlsServer
+ {
+ private static readonly int[] TestCipherSuites = new int[]
+ {
+ /*
+ * TLS 1.3
+ */
+ CipherSuite.TLS_CHACHA20_POLY1305_SHA256,
+ CipherSuite.TLS_AES_256_GCM_SHA384,
+ CipherSuite.TLS_AES_128_GCM_SHA256,
+
+ /*
+ * pre-TLS 1.3
+ */
+ CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
+ CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
+ CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
+ CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384,
+ CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
+ CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
+ CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
+ CipherSuite.TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
+ CipherSuite.TLS_DHE_RSA_WITH_AES_256_GCM_SHA384,
+ CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256,
+ CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA256,
+ CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,
+ CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA,
+ CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
+ CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384,
+ CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256,
+ CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA256,
+ CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256,
+ CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA,
+ CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
+ };
+
+ protected readonly TlsTestConfig m_config;
+
+ protected int m_firstFatalAlertConnectionEnd = -1;
+ protected short m_firstFatalAlertDescription = -1;
+
+ internal byte[] m_tlsServerEndPoint = null;
+ internal byte[] m_tlsUnique = null;
+
+ internal TlsTestServerImpl(TlsTestConfig config)
+ : base(TlsTestSuite.GetCrypto(config))
+ {
+ this.m_config = config;
+ }
+
+ internal int FirstFatalAlertConnectionEnd
+ {
+ get { return m_firstFatalAlertConnectionEnd; }
+ }
+
+ internal short FirstFatalAlertDescription
+ {
+ get { return m_firstFatalAlertDescription; }
+ }
+
+ public override TlsCredentials GetCredentials()
+ {
+ /*
+ * TODO[tls13] Should really be finding the first client-supported signature scheme that the
+ * server also supports and has credentials for.
+ */
+ if (TlsUtilities.IsTlsV13(m_context))
+ {
+ return GetRsaSignerCredentials();
+ }
+
+ return base.GetCredentials();
+ }
+
+ public override void NotifyAlertRaised(short alertLevel, short alertDescription, string message,
+ Exception cause)
+ {
+ if (alertLevel == AlertLevel.fatal && m_firstFatalAlertConnectionEnd == -1)
+ {
+ m_firstFatalAlertConnectionEnd = ConnectionEnd.server;
+ m_firstFatalAlertDescription = alertDescription;
+ }
+
+ if (TlsTestConfig.Debug)
+ {
+ TextWriter output = (alertLevel == AlertLevel.fatal) ? Console.Error : Console.Out;
+ output.WriteLine("TLS server raised alert: " + AlertLevel.GetText(alertLevel)
+ + ", " + AlertDescription.GetText(alertDescription));
+ if (message != null)
+ {
+ output.WriteLine("> " + message);
+ }
+ if (cause != null)
+ {
+ output.WriteLine(cause);
+ }
+ }
+ }
+
+ public override void NotifyAlertReceived(short alertLevel, short alertDescription)
+ {
+ if (alertLevel == AlertLevel.fatal && m_firstFatalAlertConnectionEnd == -1)
+ {
+ m_firstFatalAlertConnectionEnd = ConnectionEnd.client;
+ m_firstFatalAlertDescription = alertDescription;
+ }
+
+ if (TlsTestConfig.Debug)
+ {
+ TextWriter output = (alertLevel == AlertLevel.fatal) ? Console.Error : Console.Out;
+ output.WriteLine("TLS server received alert: " + AlertLevel.GetText(alertLevel)
+ + ", " + AlertDescription.GetText(alertDescription));
+ }
+ }
+
+ public override void NotifyHandshakeComplete()
+ {
+ base.NotifyHandshakeComplete();
+
+ m_tlsServerEndPoint = m_context.ExportChannelBinding(ChannelBinding.tls_server_end_point);
+ m_tlsUnique = m_context.ExportChannelBinding(ChannelBinding.tls_unique);
+
+ if (TlsTestConfig.Debug)
+ {
+ Console.WriteLine("TLS server reports 'tls-server-end-point' = " + ToHexString(m_tlsServerEndPoint));
+ Console.WriteLine("TLS server reports 'tls-unique' = " + ToHexString(m_tlsUnique));
+ }
+ }
+
+ public override ProtocolVersion GetServerVersion()
+ {
+ ProtocolVersion serverVersion = (null != m_config.serverNegotiateVersion)
+ ? m_config.serverNegotiateVersion
+ : base.GetServerVersion();
+
+ if (TlsTestConfig.Debug)
+ {
+ Console.WriteLine("TLS server negotiated " + serverVersion);
+ }
+
+ return serverVersion;
+ }
+
+ public override CertificateRequest GetCertificateRequest()
+ {
+ if (m_config.serverCertReq == TlsTestConfig.SERVER_CERT_REQ_NONE)
+ return null;
+
+ IList serverSigAlgs = null;
+ if (TlsUtilities.IsSignatureAlgorithmsExtensionAllowed(m_context.ServerVersion))
+ {
+ serverSigAlgs = m_config.serverCertReqSigAlgs;
+ if (serverSigAlgs == null)
+ {
+ serverSigAlgs = TlsUtilities.GetDefaultSupportedSignatureAlgorithms(m_context);
+ }
+ }
+
+ IList certificateAuthorities = new ArrayList();
+ //certificateAuthorities.Add(TlsTestUtilities.LoadBcCertificateResource("x509-ca-dsa.pem").Subject);
+ //certificateAuthorities.Add(TlsTestUtilities.LoadBcCertificateResource("x509-ca-ecdsa.pem").Subject);
+ //certificateAuthorities.Add(TlsTestUtilities.LoadBcCertificateResource("x509-ca-rsa.pem").Subject);
+
+ // All the CA certificates are currently configured with this subject
+ certificateAuthorities.Add(new X509Name("CN=BouncyCastle TLS Test CA"));
+
+ if (TlsUtilities.IsTlsV13(m_context))
+ {
+ // TODO[tls13] Support for non-empty request context
+ byte[] certificateRequestContext = TlsUtilities.EmptyBytes;
+
+ // TODO[tls13] Add TlsTestConfig.serverCertReqSigAlgsCert
+ IList serverSigAlgsCert = null;
+
+ return new CertificateRequest(certificateRequestContext, serverSigAlgs, serverSigAlgsCert,
+ certificateAuthorities);
+ }
+ else
+ {
+ short[] certificateTypes = new short[]{ ClientCertificateType.rsa_sign,
+ ClientCertificateType.dss_sign, ClientCertificateType.ecdsa_sign };
+
+ return new CertificateRequest(certificateTypes, serverSigAlgs, certificateAuthorities);
+ }
+ }
+
+ public override void NotifyClientCertificate(Certificate clientCertificate)
+ {
+ bool isEmpty = (clientCertificate == null || clientCertificate.IsEmpty);
+
+ if (isEmpty != (m_config.clientAuth == TlsTestConfig.CLIENT_AUTH_NONE))
+ throw new InvalidOperationException();
+
+ if (isEmpty && (m_config.serverCertReq == TlsTestConfig.SERVER_CERT_REQ_MANDATORY))
+ {
+ short alertDescription = TlsUtilities.IsTlsV13(m_context)
+ ? AlertDescription.certificate_required
+ : AlertDescription.handshake_failure;
+
+ throw new TlsFatalAlert(alertDescription);
+ }
+
+ TlsCertificate[] chain = clientCertificate.GetCertificateList();
+
+ if (TlsTestConfig.Debug)
+ {
+ Console.WriteLine("TLS server received client certificate chain of length " + chain.Length);
+ for (int i = 0; i < chain.Length; ++i)
+ {
+ X509CertificateStructure entry = X509CertificateStructure.GetInstance(chain[0].GetEncoded());
+ // TODO Create fingerprint based on certificate signature algorithm digest
+ Console.WriteLine(" fingerprint:SHA-256 " + TlsTestUtilities.Fingerprint(entry) + " ("
+ + entry.Subject + ")");
+ }
+ }
+
+ if (isEmpty)
+ return;
+
+ string[] trustedCertResources = new string[]{ "x509-client-dsa.pem", "x509-client-ecdh.pem",
+ "x509-client-ecdsa.pem", "x509-client-ed25519.pem", "x509-client-ed448.pem",
+ "x509-client-rsa_pss_256.pem", "x509-client-rsa_pss_384.pem", "x509-client-rsa_pss_512.pem",
+ "x509-client-rsa.pem" };
+
+ TlsCertificate[] certPath = TlsTestUtilities.GetTrustedCertPath(m_context.Crypto, chain[0],
+ trustedCertResources);
+
+ if (null == certPath)
+ throw new TlsFatalAlert(AlertDescription.bad_certificate);
+
+ if (m_config.serverCheckSigAlgOfClientCerts)
+ {
+ TlsUtilities.CheckPeerSigAlgs(m_context, certPath);
+ }
+ }
+
+ protected virtual IList GetSupportedSignatureAlgorithms()
+ {
+ if (TlsUtilities.IsTlsV12(m_context) && m_config.serverAuthSigAlg != null)
+ {
+ IList signatureAlgorithms = new ArrayList(1);
+ signatureAlgorithms.Add(m_config.serverAuthSigAlg);
+ return signatureAlgorithms;
+ }
+
+ return m_context.SecurityParameters.ClientSigAlgs;
+ }
+
+ protected override TlsCredentialedSigner GetDsaSignerCredentials()
+ {
+ return LoadSignerCredentials(SignatureAlgorithm.dsa);
+ }
+
+ protected override TlsCredentialedSigner GetECDsaSignerCredentials()
+ {
+ // TODO[RFC 8422] Code should choose based on client's supported sig algs?
+ return LoadSignerCredentials(SignatureAlgorithm.ecdsa);
+ //return LoadSignerCredentials(SignatureAlgorithm.ed25519);
+ //return LoadSignerCredentials(SignatureAlgorithm.ed448);
+ }
+
+ protected override TlsCredentialedDecryptor GetRsaEncryptionCredentials()
+ {
+ return TlsTestUtilities.LoadEncryptionCredentials(m_context,
+ new string[]{ "x509-server-rsa-enc.pem", "x509-ca-rsa.pem" }, "x509-server-key-rsa-enc.pem");
+ }
+
+ protected override TlsCredentialedSigner GetRsaSignerCredentials()
+ {
+ return LoadSignerCredentials(SignatureAlgorithm.rsa);
+ }
+
+ protected override int[] GetSupportedCipherSuites()
+ {
+ return TlsUtilities.GetSupportedCipherSuites(Crypto, TestCipherSuites);
+ }
+
+ protected override ProtocolVersion[] GetSupportedVersions()
+ {
+ if (m_config.serverSupportedVersions != null)
+ {
+ return m_config.serverSupportedVersions;
+ }
+
+ return base.GetSupportedVersions();
+ }
+
+ protected virtual string ToHexString(byte[] data)
+ {
+ return data == null ? "(null)" : Hex.ToHexString(data);
+ }
+
+ private TlsCredentialedSigner LoadSignerCredentials(short signatureAlgorithm)
+ {
+ return TlsTestUtilities.LoadSignerCredentialsServer(m_context, GetSupportedSignatureAlgorithms(),
+ signatureAlgorithm);
+ }
+ }
+}
diff --git a/crypto/test/src/tls/test/TlsTestServerProtocol.cs b/crypto/test/src/tls/test/TlsTestServerProtocol.cs
new file mode 100644
index 000000000..632f6b8bf
--- /dev/null
+++ b/crypto/test/src/tls/test/TlsTestServerProtocol.cs
@@ -0,0 +1,22 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Tls.Tests
+{
+ internal class TlsTestServerProtocol
+ : TlsServerProtocol
+ {
+ protected readonly TlsTestConfig m_config;
+
+ internal TlsTestServerProtocol(Stream stream, TlsTestConfig config)
+ : this(stream, stream, config)
+ {
+ }
+
+ internal TlsTestServerProtocol(Stream input, Stream output, TlsTestConfig config)
+ : base(input, output)
+ {
+ this.m_config = config;
+ }
+ }
+}
diff --git a/crypto/test/src/tls/test/TlsTestSuite.cs b/crypto/test/src/tls/test/TlsTestSuite.cs
new file mode 100644
index 000000000..adedd8249
--- /dev/null
+++ b/crypto/test/src/tls/test/TlsTestSuite.cs
@@ -0,0 +1,300 @@
+using System;
+using System.Collections;
+
+using NUnit.Framework;
+
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Tls.Crypto;
+using Org.BouncyCastle.Tls.Crypto.Impl.BC;
+
+namespace Org.BouncyCastle.Tls.Tests
+{
+ public class TlsTestSuite
+ {
+ internal static TlsCrypto BC_CRYPTO = new BcTlsCrypto(new SecureRandom());
+
+ internal static TlsCrypto GetCrypto(TlsTestConfig config)
+ {
+ switch (config.clientCrypto)
+ {
+ case TlsTestConfig.CRYPTO_BC:
+ default:
+ return BC_CRYPTO;
+ }
+ }
+
+ // Make the access to constants less verbose
+ internal abstract class C : TlsTestConfig {}
+
+ public TlsTestSuite()
+ {
+ }
+
+ public static IEnumerable Suite()
+ {
+ IList testSuite = new ArrayList();
+ AddAllTests(testSuite, TlsTestConfig.CRYPTO_BC, TlsTestConfig.CRYPTO_BC);
+ return testSuite;
+ }
+
+ private static void AddAllTests(IList testSuite, int clientCrypto, int serverCrypto)
+ {
+ AddFallbackTests(testSuite, clientCrypto, serverCrypto);
+ AddVersionTests(testSuite, ProtocolVersion.SSLv3, clientCrypto, serverCrypto);
+ AddVersionTests(testSuite, ProtocolVersion.TLSv10, clientCrypto, serverCrypto);
+ AddVersionTests(testSuite, ProtocolVersion.TLSv11, clientCrypto, serverCrypto);
+ AddVersionTests(testSuite, ProtocolVersion.TLSv12, clientCrypto, serverCrypto);
+ AddVersionTests(testSuite, ProtocolVersion.TLSv13, clientCrypto, serverCrypto);
+ }
+
+ private static void AddFallbackTests(IList testSuite, int clientCrypto, int serverCrypto)
+ {
+ string prefix = GetCryptoName(clientCrypto) + "_" + GetCryptoName(serverCrypto) + "_";
+
+ {
+ TlsTestConfig c = CreateTlsTestConfig(ProtocolVersion.TLSv12, clientCrypto, serverCrypto);
+ c.clientFallback = true;
+
+ AddTestCase(testSuite, c, prefix + "FallbackGood");
+ }
+
+ {
+ TlsTestConfig c = CreateTlsTestConfig(ProtocolVersion.TLSv12, clientCrypto, serverCrypto);
+ c.clientFallback = true;
+ c.clientSupportedVersions = ProtocolVersion.TLSv11.DownTo(ProtocolVersion.TLSv10);
+ c.ExpectServerFatalAlert(AlertDescription.inappropriate_fallback);
+
+ AddTestCase(testSuite, c, prefix + "FallbackBad");
+ }
+
+ {
+ TlsTestConfig c = CreateTlsTestConfig(ProtocolVersion.TLSv12, clientCrypto, serverCrypto);
+ c.clientSupportedVersions = ProtocolVersion.TLSv11.DownTo(ProtocolVersion.TLSv10);
+
+ AddTestCase(testSuite, c, prefix + "FallbackNone");
+ }
+ }
+
+ private static void AddVersionTests(IList testSuite, ProtocolVersion version, int clientCrypto,
+ int serverCrypto)
+ {
+ string prefix = GetCryptoName(clientCrypto) + "_" + GetCryptoName(serverCrypto) + "_"
+ + version.ToString().Replace(" ", "").Replace(".", "") + "_";
+
+ bool isTlsV12 = TlsUtilities.IsTlsV12(version);
+ bool isTlsV13 = TlsUtilities.IsTlsV13(version);
+ bool isTlsV12Exactly = isTlsV12 && !isTlsV13;
+
+ short certReqDeclinedAlert = TlsUtilities.IsTlsV13(version)
+ ? AlertDescription.certificate_required
+ : AlertDescription.handshake_failure;
+
+ {
+ TlsTestConfig c = CreateTlsTestConfig(version, clientCrypto, serverCrypto);
+
+ AddTestCase(testSuite, c, prefix + "GoodDefault");
+ }
+
+ if (isTlsV13)
+ {
+ TlsTestConfig c = CreateTlsTestConfig(version, clientCrypto, serverCrypto);
+ c.clientEmptyKeyShare = true;
+
+ AddTestCase(testSuite, c, prefix + "GoodEmptyKeyShare");
+ }
+
+ /*
+ * Server only declares support for SHA1/RSA, client selects MD5/RSA. Since the client is
+ * NOT actually tracking MD5 over the handshake, we expect fatal alert from the client.
+ */
+ if (isTlsV12Exactly)
+ {
+ TlsTestConfig c = CreateTlsTestConfig(version, clientCrypto, serverCrypto);
+ c.clientAuth = C.CLIENT_AUTH_VALID;
+ c.clientAuthSigAlg = new SignatureAndHashAlgorithm(HashAlgorithm.md5, SignatureAlgorithm.rsa);
+ c.serverCertReqSigAlgs = TlsUtilities.GetDefaultRsaSignatureAlgorithms();
+ c.serverCheckSigAlgOfClientCerts = false;
+ c.ExpectClientFatalAlert(AlertDescription.internal_error);
+
+ AddTestCase(testSuite, c, prefix + "BadCertificateVerifyHashAlg");
+ }
+
+ /*
+ * Server only declares support for SHA1/ECDSA, client selects SHA1/RSA. Since the client is
+ * actually tracking SHA1 over the handshake, we expect fatal alert to come from the server
+ * when it verifies the selected algorithm against the CertificateRequest supported
+ * algorithms.
+ */
+ if (isTlsV12)
+ {
+ TlsTestConfig c = CreateTlsTestConfig(version, clientCrypto, serverCrypto);
+ c.clientAuth = C.CLIENT_AUTH_VALID;
+ c.clientAuthSigAlg = new SignatureAndHashAlgorithm(HashAlgorithm.sha1, SignatureAlgorithm.rsa);
+ c.serverCertReqSigAlgs = TlsUtilities.GetDefaultECDsaSignatureAlgorithms();
+ c.serverCheckSigAlgOfClientCerts = false;
+ c.ExpectServerFatalAlert(AlertDescription.illegal_parameter);
+
+ AddTestCase(testSuite, c, prefix + "BadCertificateVerifySigAlg");
+ }
+
+ /*
+ * Server only declares support for SHA1/ECDSA, client signs with SHA1/RSA, but sends
+ * SHA1/ECDSA in the CertificateVerify. Since the client is actually tracking SHA1 over the
+ * handshake, and the claimed algorithm is in the CertificateRequest supported algorithms,
+ * we expect fatal alert to come from the server when it finds the claimed algorithm
+ * doesn't match the client certificate.
+ */
+ if (isTlsV12)
+ {
+ TlsTestConfig c = CreateTlsTestConfig(version, clientCrypto, serverCrypto);
+ c.clientAuth = C.CLIENT_AUTH_VALID;
+ c.clientAuthSigAlg = new SignatureAndHashAlgorithm(HashAlgorithm.sha1, SignatureAlgorithm.rsa);
+ c.clientAuthSigAlgClaimed = new SignatureAndHashAlgorithm(HashAlgorithm.sha1, SignatureAlgorithm.ecdsa);
+ c.serverCertReqSigAlgs = TlsUtilities.GetDefaultECDsaSignatureAlgorithms();
+ c.ExpectServerFatalAlert(AlertDescription.bad_certificate);
+
+ AddTestCase(testSuite, c, prefix + "BadCertificateVerifySigAlgMismatch");
+ }
+
+ {
+ TlsTestConfig c = CreateTlsTestConfig(version, clientCrypto, serverCrypto);
+ c.clientAuth = C.CLIENT_AUTH_INVALID_VERIFY;
+ c.ExpectServerFatalAlert(AlertDescription.decrypt_error);
+
+ AddTestCase(testSuite, c, prefix + "BadCertificateVerifySignature");
+ }
+
+ {
+ TlsTestConfig c = CreateTlsTestConfig(version, clientCrypto, serverCrypto);
+ c.clientAuth = C.CLIENT_AUTH_INVALID_CERT;
+ c.ExpectServerFatalAlert(AlertDescription.bad_certificate);
+
+ AddTestCase(testSuite, c, prefix + "BadClientCertificate");
+ }
+
+ if (isTlsV13)
+ {
+ /*
+ * For TLS 1.3 the supported_algorithms extension is required in ClientHello when the
+ * server authenticates via a certificate.
+ */
+ TlsTestConfig c = CreateTlsTestConfig(version, clientCrypto, serverCrypto);
+ c.clientSendSignatureAlgorithms = false;
+ c.clientSendSignatureAlgorithmsCert = false;
+ c.ExpectServerFatalAlert(AlertDescription.missing_extension);
+
+ AddTestCase(testSuite, c, prefix + "BadClientSigAlgs");
+ }
+
+ {
+ TlsTestConfig c = CreateTlsTestConfig(version, clientCrypto, serverCrypto);
+ c.clientAuth = C.CLIENT_AUTH_NONE;
+ c.serverCertReq = C.SERVER_CERT_REQ_MANDATORY;
+ c.ExpectServerFatalAlert(certReqDeclinedAlert);
+
+ AddTestCase(testSuite, c, prefix + "BadMandatoryCertReqDeclined");
+ }
+
+ /*
+ * Server sends SHA-256/RSA certificate, which is not the default {sha1,rsa} implied by the
+ * absent signature_algorithms extension. We expect fatal alert from the client when it
+ * verifies the certificate's 'signatureAlgorithm' against the implicit default signature_algorithms.
+ */
+ if (isTlsV12Exactly)
+ {
+ TlsTestConfig c = CreateTlsTestConfig(version, clientCrypto, serverCrypto);
+ c.clientSendSignatureAlgorithms = false;
+ c.clientSendSignatureAlgorithmsCert = false;
+ c.serverAuthSigAlg = new SignatureAndHashAlgorithm(HashAlgorithm.sha256, SignatureAlgorithm.rsa);
+ c.ExpectClientFatalAlert(AlertDescription.bad_certificate);
+
+ AddTestCase(testSuite, c, prefix + "BadServerCertSigAlg");
+ }
+
+ /*
+ * Server selects MD5/RSA for ServerKeyExchange signature, which is not in the default
+ * supported signature algorithms that the client sent. We expect fatal alert from the
+ * client when it verifies the selected algorithm against the supported algorithms.
+ */
+ if (TlsUtilities.IsTlsV12(version))
+ {
+ TlsTestConfig c = CreateTlsTestConfig(version, clientCrypto, serverCrypto);
+ c.serverAuthSigAlg = new SignatureAndHashAlgorithm(HashAlgorithm.md5, SignatureAlgorithm.rsa);
+ c.ExpectClientFatalAlert(AlertDescription.illegal_parameter);
+
+ AddTestCase(testSuite, c, prefix + "BadServerKeyExchangeSigAlg");
+ }
+
+ /*
+ * Server selects MD5/RSA for ServerKeyExchange signature, which is not the default {sha1,rsa}
+ * implied by the absent signature_algorithms extension. We expect fatal alert from the
+ * client when it verifies the selected algorithm against the implicit default.
+ */
+ if (isTlsV12Exactly)
+ {
+ TlsTestConfig c = CreateTlsTestConfig(version, clientCrypto, serverCrypto);
+ c.clientCheckSigAlgOfServerCerts = false;
+ c.clientSendSignatureAlgorithms = false;
+ c.clientSendSignatureAlgorithmsCert = false;
+ c.serverAuthSigAlg = new SignatureAndHashAlgorithm(HashAlgorithm.md5, SignatureAlgorithm.rsa);
+ c.ExpectClientFatalAlert(AlertDescription.illegal_parameter);
+
+ AddTestCase(testSuite, c, prefix + "BadServerKeyExchangeSigAlg2");
+ }
+
+ {
+ TlsTestConfig c = CreateTlsTestConfig(version, clientCrypto, serverCrypto);
+ c.serverCertReq = C.SERVER_CERT_REQ_NONE;
+
+ AddTestCase(testSuite, c, prefix + "GoodNoCertReq");
+ }
+
+ {
+ TlsTestConfig c = CreateTlsTestConfig(version, clientCrypto, serverCrypto);
+ c.clientAuth = C.CLIENT_AUTH_NONE;
+
+ AddTestCase(testSuite, c, prefix + "GoodOptionalCertReqDeclined");
+ }
+
+ /*
+ * Server generates downgraded (RFC 8446) 1.1 ServerHello. We expect fatal alert
+ * (illegal_parameter) from the client.
+ */
+ if (!isTlsV13)
+ {
+ TlsTestConfig c = CreateTlsTestConfig(version, clientCrypto, serverCrypto);
+ c.serverNegotiateVersion = version;
+ c.serverSupportedVersions = ProtocolVersion.TLSv13.DownTo(version);
+ c.ExpectClientFatalAlert(AlertDescription.illegal_parameter);
+
+ AddTestCase(testSuite, c, prefix + "BadDowngrade");
+ }
+ }
+
+ private static void AddTestCase(IList testSuite, TlsTestConfig config, string name)
+ {
+ testSuite.Add(new TestCaseData(config).SetName(name));
+ }
+
+ private static TlsTestConfig CreateTlsTestConfig(ProtocolVersion serverMaxVersion, int clientCrypto,
+ int serverCrypto)
+ {
+ TlsTestConfig c = new TlsTestConfig();
+ c.clientCrypto = clientCrypto;
+ c.clientSupportedVersions = ProtocolVersion.TLSv13.DownTo(ProtocolVersion.SSLv3);
+ c.serverCrypto = serverCrypto;
+ c.serverSupportedVersions = serverMaxVersion.DownTo(ProtocolVersion.SSLv3);
+ return c;
+ }
+
+ private static string GetCryptoName(int crypto)
+ {
+ switch (crypto)
+ {
+ case TlsTestConfig.CRYPTO_BC:
+ default:
+ return "BC";
+ }
+ }
+ }
+}
diff --git a/crypto/test/src/tls/test/TlsTestUtilities.cs b/crypto/test/src/tls/test/TlsTestUtilities.cs
new file mode 100644
index 000000000..3ecacb0f0
--- /dev/null
+++ b/crypto/test/src/tls/test/TlsTestUtilities.cs
@@ -0,0 +1,412 @@
+using System;
+using System.Collections;
+using System.Globalization;
+using System.IO;
+using System.Text;
+
+using Org.BouncyCastle.Asn1.Pkcs;
+using Org.BouncyCastle.Asn1.Sec;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Asn1.X9;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Tls.Crypto;
+using Org.BouncyCastle.Tls.Crypto.Impl.BC;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Encoders;
+using Org.BouncyCastle.Utilities.IO.Pem;
+using Org.BouncyCastle.Utilities.Test;
+
+namespace Org.BouncyCastle.Tls.Tests
+{
+ public class TlsTestUtilities
+ {
+ internal static readonly byte[] RsaCertData = Base64.Decode(
+ "MIICUzCCAf2gAwIBAgIBATANBgkqhkiG9w0BAQQFADCBjzELMAkGA1UEBhMCQVUxKDAmBgNVBAoMH1RoZSBMZWdpb2" +
+ "4gb2YgdGhlIEJvdW5jeSBDYXN0bGUxEjAQBgNVBAcMCU1lbGJvdXJuZTERMA8GA1UECAwIVmljdG9yaWExLzAtBgkq" +
+ "hkiG9w0BCQEWIGZlZWRiYWNrLWNyeXB0b0Bib3VuY3ljYXN0bGUub3JnMB4XDTEzMDIyNTA2MDIwNVoXDTEzMDIyNT" +
+ "A2MDM0NVowgY8xCzAJBgNVBAYTAkFVMSgwJgYDVQQKDB9UaGUgTGVnaW9uIG9mIHRoZSBCb3VuY3kgQ2FzdGxlMRIw" +
+ "EAYDVQQHDAlNZWxib3VybmUxETAPBgNVBAgMCFZpY3RvcmlhMS8wLQYJKoZIhvcNAQkBFiBmZWVkYmFjay1jcnlwdG" +
+ "9AYm91bmN5Y2FzdGxlLm9yZzBaMA0GCSqGSIb3DQEBAQUAA0kAMEYCQQC0p+RhcFdPFqlwgrIr5YtqKmKXmEGb4Shy" +
+ "pL26Ymz66ZAPdqv7EhOdzl3lZWT6srZUMWWgQMYGiHQg4z2R7X7XAgERo0QwQjAOBgNVHQ8BAf8EBAMCBSAwEgYDVR" +
+ "0lAQH/BAgwBgYEVR0lADAcBgNVHREBAf8EEjAQgQ50ZXN0QHRlc3QudGVzdDANBgkqhkiG9w0BAQQFAANBAHU55Ncz" +
+ "eglREcTg54YLUlGWu2WOYWhit/iM1eeq8Kivro7q98eW52jTuMI3CI5ulqd0hYzshQKQaZ5GDzErMyM=");
+
+ internal static readonly byte[] DudRsaCertData = Base64.Decode(
+ "MIICUzCCAf2gAwIBAgIBATANBgkqhkiG9w0BAQQFADCBjzELMAkGA1UEBhMCQVUxKDAmBgNVBAoMH1RoZSBMZWdpb2" +
+ "4gb2YgdGhlIEJvdW5jeSBDYXN0bGUxEjAQBgNVBAcMCU1lbGJvdXJuZTERMA8GA1UECAwIVmljdG9yaWExLzAtBgkq" +
+ "hkiG9w0BCQEWIGZlZWRiYWNrLWNyeXB0b0Bib3VuY3ljYXN0bGUub3JnMB4XDTEzMDIyNTA1NDcyOFoXDTEzMDIyNT" +
+ "A1NDkwOFowgY8xCzAJBgNVBAYTAkFVMSgwJgYDVQQKDB9UaGUgTGVnaW9uIG9mIHRoZSBCb3VuY3kgQ2FzdGxlMRIw" +
+ "EAYDVQQHDAlNZWxib3VybmUxETAPBgNVBAgMCFZpY3RvcmlhMS8wLQYJKoZIhvcNAQkBFiBmZWVkYmFjay1jcnlwdG" +
+ "9AYm91bmN5Y2FzdGxlLm9yZzBaMA0GCSqGSIb3DQEBAQUAA0kAMEYCQQC0p+RhcFdPFqlwgrIr5YtqKmKXmEGb4Shy" +
+ "pL26Ymz66ZAPdqv7EhOdzl3lZWT6srZUMWWgQMYGiHQg4z2R7X7XAgERo0QwQjAOBgNVHQ8BAf8EBAMCAAEwEgYDVR" +
+ "0lAQH/BAgwBgYEVR0lADAcBgNVHREBAf8EEjAQgQ50ZXN0QHRlc3QudGVzdDANBgkqhkiG9w0BAQQFAANBAJg55PBS" +
+ "weg6obRUKF4FF6fCrWFi6oCYSQ99LWcAeupc5BofW5MstFMhCOaEucuGVqunwT5G7/DweazzCIrSzB0=");
+
+ internal static bool EqualsIgnoreCase(string a, string b)
+ {
+ return ToUpperInvariant(a) == ToUpperInvariant(b);
+ }
+
+ internal static string ToUpperInvariant(string s)
+ {
+ return s.ToUpper(CultureInfo.InvariantCulture);
+ }
+
+ internal static string Fingerprint(X509CertificateStructure c)
+ {
+ byte[] der = c.GetEncoded();
+ byte[] hash = Sha256DigestOf(der);
+ byte[] hexBytes = Hex.Encode(hash);
+ string hex = ToUpperInvariant(Encoding.ASCII.GetString(hexBytes));
+
+ StringBuilder fp = new StringBuilder();
+ int i = 0;
+ fp.Append(hex.Substring(i, 2));
+ while ((i += 2) < hex.Length)
+ {
+ fp.Append(':');
+ fp.Append(hex.Substring(i, 2));
+ }
+ return fp.ToString();
+ }
+
+ internal static byte[] Sha256DigestOf(byte[] input)
+ {
+ return DigestUtilities.CalculateDigest("SHA256", input);
+ }
+
+ internal static string GetCACertResource(short signatureAlgorithm)
+ {
+ return "x509-ca-" + GetResourceName(signatureAlgorithm) + ".pem";
+ }
+
+ internal static string GetCACertResource(string eeCertResource)
+ {
+ if (eeCertResource.StartsWith("x509-client-"))
+ {
+ eeCertResource = eeCertResource.Substring("x509-client-".Length);
+ }
+ if (eeCertResource.StartsWith("x509-server-"))
+ {
+ eeCertResource = eeCertResource.Substring("x509-server-".Length);
+ }
+ if (eeCertResource.EndsWith(".pem"))
+ {
+ eeCertResource = eeCertResource.Substring(0, eeCertResource.Length - ".pem".Length);
+ }
+
+ if (EqualsIgnoreCase("dsa", eeCertResource))
+ {
+ return GetCACertResource(SignatureAlgorithm.dsa);
+ }
+
+ if (EqualsIgnoreCase("ecdh", eeCertResource)
+ || EqualsIgnoreCase("ecdsa", eeCertResource))
+ {
+ return GetCACertResource(SignatureAlgorithm.ecdsa);
+ }
+
+ if (EqualsIgnoreCase("ed25519", eeCertResource))
+ {
+ return GetCACertResource(SignatureAlgorithm.ed25519);
+ }
+
+ if (EqualsIgnoreCase("ed448", eeCertResource))
+ {
+ return GetCACertResource(SignatureAlgorithm.ed448);
+ }
+
+ if (EqualsIgnoreCase("rsa", eeCertResource)
+ || EqualsIgnoreCase("rsa-enc", eeCertResource)
+ || EqualsIgnoreCase("rsa-sign", eeCertResource))
+ {
+ return GetCACertResource(SignatureAlgorithm.rsa);
+ }
+
+ if (EqualsIgnoreCase("rsa_pss_256", eeCertResource))
+ {
+ return GetCACertResource(SignatureAlgorithm.rsa_pss_pss_sha256);
+ }
+ if (EqualsIgnoreCase("rsa_pss_384", eeCertResource))
+ {
+ return GetCACertResource(SignatureAlgorithm.rsa_pss_pss_sha384);
+ }
+ if (EqualsIgnoreCase("rsa_pss_512", eeCertResource))
+ {
+ return GetCACertResource(SignatureAlgorithm.rsa_pss_pss_sha512);
+ }
+
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ internal static string GetResourceName(short signatureAlgorithm)
+ {
+ switch (signatureAlgorithm)
+ {
+ case SignatureAlgorithm.rsa:
+ case SignatureAlgorithm.rsa_pss_rsae_sha256:
+ case SignatureAlgorithm.rsa_pss_rsae_sha384:
+ case SignatureAlgorithm.rsa_pss_rsae_sha512:
+ return "rsa";
+ case SignatureAlgorithm.dsa:
+ return "dsa";
+ case SignatureAlgorithm.ecdsa:
+ return "ecdsa";
+ case SignatureAlgorithm.ed25519:
+ return "ed25519";
+ case SignatureAlgorithm.ed448:
+ return "ed448";
+ case SignatureAlgorithm.rsa_pss_pss_sha256:
+ return "rsa_pss_256";
+ case SignatureAlgorithm.rsa_pss_pss_sha384:
+ return "rsa_pss_384";
+ case SignatureAlgorithm.rsa_pss_pss_sha512:
+ return "rsa_pss_512";
+ default:
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ }
+
+ internal static TlsCredentialedAgreement LoadAgreementCredentials(TlsContext context, string[] certResources,
+ string keyResource)
+ {
+ TlsCrypto crypto = context.Crypto;
+ Certificate certificate = LoadCertificateChain(context, certResources);
+
+ // TODO[tls-ops] Need to have TlsCrypto construct the credentials from the certs/key (as raw data)
+ if (crypto is BcTlsCrypto)
+ {
+ AsymmetricKeyParameter privateKey = LoadBcPrivateKeyResource(keyResource);
+
+ return new BcDefaultTlsCredentialedAgreement((BcTlsCrypto)crypto, certificate, privateKey);
+ }
+ else
+ {
+ throw new NotSupportedException();
+ }
+ }
+
+ internal static TlsCredentialedDecryptor LoadEncryptionCredentials(TlsContext context, string[] certResources,
+ string keyResource)
+ {
+ TlsCrypto crypto = context.Crypto;
+ Certificate certificate = LoadCertificateChain(context, certResources);
+
+ // TODO[tls-ops] Need to have TlsCrypto construct the credentials from the certs/key (as raw data)
+ if (crypto is BcTlsCrypto)
+ {
+ AsymmetricKeyParameter privateKey = LoadBcPrivateKeyResource(keyResource);
+
+ return new BcDefaultTlsCredentialedDecryptor((BcTlsCrypto)crypto, certificate, privateKey);
+ }
+ else
+ {
+ throw new NotSupportedException();
+ }
+ }
+
+ public static TlsCredentialedSigner LoadSignerCredentials(TlsCryptoParameters cryptoParams, TlsCrypto crypto,
+ string[] certResources, string keyResource, SignatureAndHashAlgorithm signatureAndHashAlgorithm)
+ {
+ Certificate certificate = LoadCertificateChain(cryptoParams.ServerVersion, crypto, certResources);
+
+ // TODO[tls-ops] Need to have TlsCrypto construct the credentials from the certs/key (as raw data)
+ if (crypto is BcTlsCrypto)
+ {
+ AsymmetricKeyParameter privateKey = LoadBcPrivateKeyResource(keyResource);
+
+ return new BcDefaultTlsCredentialedSigner(cryptoParams, (BcTlsCrypto)crypto, privateKey, certificate, signatureAndHashAlgorithm);
+ }
+ else
+ {
+ throw new NotSupportedException();
+ }
+ }
+
+ internal static TlsCredentialedSigner LoadSignerCredentials(TlsContext context, string[] certResources,
+ string keyResource, SignatureAndHashAlgorithm signatureAndHashAlgorithm)
+ {
+ TlsCrypto crypto = context.Crypto;
+ TlsCryptoParameters cryptoParams = new TlsCryptoParameters(context);
+
+ return LoadSignerCredentials(cryptoParams, crypto, certResources, keyResource, signatureAndHashAlgorithm);
+ }
+
+ internal static TlsCredentialedSigner LoadSignerCredentials(TlsContext context,
+ IList supportedSignatureAlgorithms, short signatureAlgorithm, string certResource, string keyResource)
+ {
+ if (supportedSignatureAlgorithms == null)
+ {
+ supportedSignatureAlgorithms = TlsUtilities.GetDefaultSignatureAlgorithms(signatureAlgorithm);
+ }
+
+ SignatureAndHashAlgorithm signatureAndHashAlgorithm = null;
+
+ foreach (SignatureAndHashAlgorithm alg in supportedSignatureAlgorithms)
+ {
+ if (alg.Signature == signatureAlgorithm)
+ {
+ // Just grab the first one we find
+ signatureAndHashAlgorithm = alg;
+ break;
+ }
+ }
+
+ if (signatureAndHashAlgorithm == null)
+ return null;
+
+ return LoadSignerCredentials(context, new string[]{ certResource }, keyResource,
+ signatureAndHashAlgorithm);
+ }
+
+ internal static TlsCredentialedSigner LoadSignerCredentialsServer(TlsContext context,
+ IList supportedSignatureAlgorithms, short signatureAlgorithm)
+ {
+ string sigName = GetResourceName(signatureAlgorithm);
+
+ switch (signatureAlgorithm)
+ {
+ case SignatureAlgorithm.rsa:
+ case SignatureAlgorithm.rsa_pss_rsae_sha256:
+ case SignatureAlgorithm.rsa_pss_rsae_sha384:
+ case SignatureAlgorithm.rsa_pss_rsae_sha512:
+ sigName += "-sign";
+ break;
+ }
+
+ string certResource = "x509-server-" + sigName + ".pem";
+ string keyResource = "x509-server-key-" + sigName + ".pem";
+
+ return LoadSignerCredentials(context, supportedSignatureAlgorithms, signatureAlgorithm, certResource,
+ keyResource);
+ }
+
+ internal static Certificate LoadCertificateChain(ProtocolVersion protocolVersion, TlsCrypto crypto,
+ string[] resources)
+ {
+ if (TlsUtilities.IsTlsV13(protocolVersion))
+ {
+ CertificateEntry[] certificateEntryList = new CertificateEntry[resources.Length];
+ for (int i = 0; i < resources.Length; ++i)
+ {
+ TlsCertificate certificate = LoadCertificateResource(crypto, resources[i]);
+
+ // TODO[tls13] Add possibility of specifying e.g. CertificateStatus
+ IDictionary extensions = null;
+
+ certificateEntryList[i] = new CertificateEntry(certificate, extensions);
+ }
+
+ // TODO[tls13] Support for non-empty request context
+ byte[] certificateRequestContext = TlsUtilities.EmptyBytes;
+
+ return new Certificate(certificateRequestContext, certificateEntryList);
+ }
+ else
+ {
+ TlsCertificate[] chain = new TlsCertificate[resources.Length];
+ for (int i = 0; i < resources.Length; ++i)
+ {
+ chain[i] = LoadCertificateResource(crypto, resources[i]);
+ }
+ return new Certificate(chain);
+ }
+ }
+
+ internal static Certificate LoadCertificateChain(TlsContext context, string[] resources)
+ {
+ return LoadCertificateChain(context.ServerVersion, context.Crypto, resources);
+ }
+
+ internal static X509CertificateStructure LoadBcCertificateResource(string resource)
+ {
+ PemObject pem = LoadPemResource(resource);
+ if (pem.Type.EndsWith("CERTIFICATE"))
+ {
+ return X509CertificateStructure.GetInstance(pem.Content);
+ }
+ throw new ArgumentException("doesn't specify a valid certificate", "resource");
+ }
+
+ internal static TlsCertificate LoadCertificateResource(TlsCrypto crypto, string resource)
+ {
+ PemObject pem = LoadPemResource(resource);
+ if (pem.Type.EndsWith("CERTIFICATE"))
+ {
+ return crypto.CreateCertificate(pem.Content);
+ }
+ throw new ArgumentException("doesn't specify a valid certificate", "resource");
+ }
+
+ internal static AsymmetricKeyParameter LoadBcPrivateKeyResource(string resource)
+ {
+ PemObject pem = LoadPemResource(resource);
+ if (pem.Type.Equals("PRIVATE KEY"))
+ {
+ return PrivateKeyFactory.CreateKey(pem.Content);
+ }
+ if (pem.Type.Equals("ENCRYPTED PRIVATE KEY"))
+ {
+ throw new NotSupportedException("Encrypted PKCS#8 keys not supported");
+ }
+ if (pem.Type.Equals("RSA PRIVATE KEY"))
+ {
+ RsaPrivateKeyStructure rsa = RsaPrivateKeyStructure.GetInstance(pem.Content);
+ return new RsaPrivateCrtKeyParameters(rsa.Modulus, rsa.PublicExponent,
+ rsa.PrivateExponent, rsa.Prime1, rsa.Prime2, rsa.Exponent1,
+ rsa.Exponent2, rsa.Coefficient);
+ }
+ if (pem.Type.Equals("EC PRIVATE KEY"))
+ {
+ ECPrivateKeyStructure pKey = ECPrivateKeyStructure.GetInstance(pem.Content);
+ AlgorithmIdentifier algId = new AlgorithmIdentifier(X9ObjectIdentifiers.IdECPublicKey,
+ pKey.GetParameters());
+ PrivateKeyInfo privInfo = new PrivateKeyInfo(algId, pKey);
+ return PrivateKeyFactory.CreateKey(privInfo);
+ }
+ throw new ArgumentException("doesn't specify a valid private key", "resource");
+ }
+
+ internal static PemObject LoadPemResource(string resource)
+
+ {
+ Stream s = SimpleTest.GetTestDataAsStream("tls." + resource);
+ PemReader p = new PemReader(new StreamReader(s));
+ PemObject o = p.ReadPemObject();
+ p.Reader.Close();
+ return o;
+ }
+
+ internal static bool AreSameCertificate(TlsCrypto crypto, TlsCertificate cert, string resource)
+ {
+ // TODO Cache test resources?
+ return AreSameCertificate(cert, LoadCertificateResource(crypto, resource));
+ }
+
+ internal static bool AreSameCertificate(TlsCertificate a, TlsCertificate b)
+ {
+ // TODO[tls-ops] Support equals on TlsCertificate?
+ return Arrays.AreEqual(a.GetEncoded(), b.GetEncoded());
+ }
+
+ internal static TlsCertificate[] GetTrustedCertPath(TlsCrypto crypto, TlsCertificate cert, string[] resources)
+ {
+ foreach (string eeCertResource in resources)
+ {
+ TlsCertificate eeCert = LoadCertificateResource(crypto, eeCertResource);
+ if (AreSameCertificate(cert, eeCert))
+ {
+ string caCertResource = GetCACertResource(eeCertResource);
+ TlsCertificate caCert = LoadCertificateResource(crypto, caCertResource);
+ if (null != caCert)
+ {
+ return new TlsCertificate[]{ eeCert, caCert };
+ }
+ }
+ }
+ return null;
+ }
+ }
+}
diff --git a/crypto/test/src/tls/test/TlsUtilitiesTest.cs b/crypto/test/src/tls/test/TlsUtilitiesTest.cs
new file mode 100644
index 000000000..702c40082
--- /dev/null
+++ b/crypto/test/src/tls/test/TlsUtilitiesTest.cs
@@ -0,0 +1,66 @@
+using System;
+using System.Collections;
+
+using NUnit.Framework;
+
+namespace Org.BouncyCastle.Tls.Tests
+{
+ [TestFixture]
+ public class TlsUtilitiesTest
+ {
+ [Test]
+ public void TestChooseSignatureAndHash()
+ {
+ int keyExchangeAlgorithm = KeyExchangeAlgorithm.ECDHE_RSA;
+ short signatureAlgorithm = TlsUtilities.GetLegacySignatureAlgorithmServer(keyExchangeAlgorithm);
+
+ IList supportedSignatureAlgorithms = GetSignatureAlgorithms(false);
+ SignatureAndHashAlgorithm sigAlg = TlsUtilities.ChooseSignatureAndHashAlgorithm(ProtocolVersion.TLSv12,
+ supportedSignatureAlgorithms, signatureAlgorithm);
+ Assert.AreEqual(HashAlgorithm.sha256, sigAlg.Hash);
+
+ for (int count = 0; count < 10; ++count)
+ {
+ supportedSignatureAlgorithms = GetSignatureAlgorithms(true);
+ sigAlg = TlsUtilities.ChooseSignatureAndHashAlgorithm(ProtocolVersion.TLSv12,
+ supportedSignatureAlgorithms, signatureAlgorithm);
+ Assert.AreEqual(HashAlgorithm.sha256, sigAlg.Hash);
+ }
+ }
+
+ private static IList GetSignatureAlgorithms(bool randomise)
+ {
+ short[] hashAlgorithms = new short[]{ HashAlgorithm.sha1, HashAlgorithm.sha224, HashAlgorithm.sha256,
+ HashAlgorithm.sha384, HashAlgorithm.sha512, HashAlgorithm.md5 };
+ short[] signatureAlgorithms = new short[]{ SignatureAlgorithm.rsa, SignatureAlgorithm.dsa,
+ SignatureAlgorithm.ecdsa };
+
+ IList result = new ArrayList();
+ for (int i = 0; i < signatureAlgorithms.Length; ++i)
+ {
+ for (int j = 0; j < hashAlgorithms.Length; ++j)
+ {
+ result.Add(SignatureAndHashAlgorithm.GetInstance(hashAlgorithms[j], signatureAlgorithms[i]));
+ }
+ }
+
+ if (randomise)
+ {
+ Random r = new Random();
+ int count = result.Count;
+ for (int src = 0; src < count; ++src)
+ {
+ int dst = r.Next(count);
+ if (src != dst)
+ {
+ object a = result[src], b = result[dst];
+ result[dst] = a;
+ result[src] = b;
+ }
+ }
+ }
+
+ return result;
+ }
+ }
+}
diff --git a/crypto/test/src/tls/test/UnreliableDatagramTransport.cs b/crypto/test/src/tls/test/UnreliableDatagramTransport.cs
new file mode 100644
index 000000000..bdbfd6e67
--- /dev/null
+++ b/crypto/test/src/tls/test/UnreliableDatagramTransport.cs
@@ -0,0 +1,79 @@
+using System;
+
+using Org.BouncyCastle.Utilities.Date;
+
+namespace Org.BouncyCastle.Tls.Tests
+{
+ public class UnreliableDatagramTransport
+ : DatagramTransport
+ {
+ private readonly DatagramTransport m_transport;
+ private readonly Random m_random;
+ private readonly int m_percentPacketLossReceiving, m_percentPacketLossSending;
+
+ public UnreliableDatagramTransport(DatagramTransport transport, Random random,
+ int percentPacketLossReceiving, int percentPacketLossSending)
+ {
+ if (percentPacketLossReceiving < 0 || percentPacketLossReceiving > 100)
+ throw new ArgumentException("out of range", "percentPacketLossReceiving");
+ if (percentPacketLossSending < 0 || percentPacketLossSending > 100)
+ throw new ArgumentException("out of range", "percentPacketLossSending");
+
+ this.m_transport = transport;
+ this.m_random = random;
+ this.m_percentPacketLossReceiving = percentPacketLossReceiving;
+ this.m_percentPacketLossSending = percentPacketLossSending;
+ }
+
+ public virtual int GetReceiveLimit()
+ {
+ return m_transport.GetReceiveLimit();
+ }
+
+ public virtual int GetSendLimit()
+ {
+ return m_transport.GetSendLimit();
+ }
+
+ public virtual int Receive(byte[] buf, int off, int len, int waitMillis)
+ {
+ long endMillis = DateTimeUtilities.CurrentUnixMs() + waitMillis;
+ for (;;)
+ {
+ int length = m_transport.Receive(buf, off, len, waitMillis);
+ if (length < 0 || !LostPacket(m_percentPacketLossReceiving))
+ return length;
+
+ Console.WriteLine("PACKET LOSS (" + length + " byte packet not received)");
+
+ long now = DateTimeUtilities.CurrentUnixMs();
+ if (now >= endMillis)
+ return -1;
+
+ waitMillis = (int)(endMillis - now);
+ }
+ }
+
+ public virtual void Send(byte[] buf, int off, int len)
+ {
+ if (LostPacket(m_percentPacketLossSending))
+ {
+ Console.WriteLine("PACKET LOSS (" + len + " byte packet not sent)");
+ }
+ else
+ {
+ m_transport.Send(buf, off, len);
+ }
+ }
+
+ public virtual void Close()
+ {
+ m_transport.Close();
+ }
+
+ private bool LostPacket(int percentPacketLoss)
+ {
+ return percentPacketLoss > 0 && m_random.Next(100) < percentPacketLoss;
+ }
+ }
+}
|