summary refs log tree commit diff
path: root/crypto/test/src/tls
diff options
context:
space:
mode:
Diffstat (limited to 'crypto/test/src/tls')
-rw-r--r--crypto/test/src/tls/test/DtlsAggregatedHandshakeRetransmissionTest.cs138
-rw-r--r--crypto/test/src/tls/test/DtlsHandshakeRetransmissionTest.cs134
-rw-r--r--crypto/test/src/tls/test/DtlsProtocolTest.cs14
-rw-r--r--crypto/test/src/tls/test/DtlsPskProtocolTest.cs12
-rw-r--r--crypto/test/src/tls/test/DtlsRawKeysProtocolTest.cs353
-rw-r--r--crypto/test/src/tls/test/DtlsTestCase.cs12
-rw-r--r--crypto/test/src/tls/test/FilteredDatagramTransport.cs112
-rw-r--r--crypto/test/src/tls/test/LoggingDatagramTransport.cs15
-rw-r--r--crypto/test/src/tls/test/MinimalHandshakeAggregator.cs254
-rw-r--r--crypto/test/src/tls/test/MockDtlsClient.cs6
-rw-r--r--crypto/test/src/tls/test/PipedStream.cs36
-rw-r--r--crypto/test/src/tls/test/PskTls13ServerTest.cs8
-rw-r--r--crypto/test/src/tls/test/PskTlsServerTest.cs8
-rw-r--r--crypto/test/src/tls/test/ServerHandshakeDropper.cs63
-rw-r--r--crypto/test/src/tls/test/Tls13PskProtocolTest.cs8
-rw-r--r--crypto/test/src/tls/test/TlsProtocolTest.cs8
-rw-r--r--crypto/test/src/tls/test/TlsPskProtocolTest.cs8
-rw-r--r--crypto/test/src/tls/test/TlsRawKeysProtocolTest.cs8
-rw-r--r--crypto/test/src/tls/test/TlsServerRawKeysTest.cs8
-rw-r--r--crypto/test/src/tls/test/TlsServerTest.cs8
-rw-r--r--crypto/test/src/tls/test/TlsSrpProtocolTest.cs8
-rw-r--r--crypto/test/src/tls/test/TlsTestCase.cs12
-rw-r--r--crypto/test/src/tls/test/UnreliableDatagramTransport.cs23
23 files changed, 1139 insertions, 117 deletions
diff --git a/crypto/test/src/tls/test/DtlsAggregatedHandshakeRetransmissionTest.cs b/crypto/test/src/tls/test/DtlsAggregatedHandshakeRetransmissionTest.cs
new file mode 100644
index 000000000..3c78b7e52
--- /dev/null
+++ b/crypto/test/src/tls/test/DtlsAggregatedHandshakeRetransmissionTest.cs
@@ -0,0 +1,138 @@
+using System;
+using System.Text;
+using System.Threading;
+
+using NUnit.Framework;
+
+using Org.BouncyCastle.Tls.Crypto;
+using Org.BouncyCastle.Tls.Crypto.Impl.BC;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Tls.Tests
+{
+    [TestFixture]
+    public class DtlsAggregatedHandshakeRetransmissionTest
+    {
+        [Test]
+        public void TestClientServer()
+        {
+            DtlsClientProtocol clientProtocol = new DtlsClientProtocol();
+            DtlsServerProtocol serverProtocol = new DtlsServerProtocol();
+
+            MockDatagramAssociation network = new MockDatagramAssociation(1500);
+
+            ServerTask serverTask = new ServerTask(serverProtocol, network.Server);
+
+            Thread serverThread = new Thread(new ThreadStart(serverTask.Run));
+            serverThread.Start();
+
+            DatagramTransport clientTransport = network.Client;
+
+            clientTransport = new ServerHandshakeDropper(clientTransport, true);
+
+            clientTransport = new LoggingDatagramTransport(clientTransport, Console.Out);
+
+            clientTransport = new MinimalHandshakeAggregator(clientTransport, false, true);
+
+            MockDtlsClient client = new MockDtlsClient(null);
+
+            client.SetHandshakeTimeoutMillis(30000);    // Test gets stuck, so we need it to time out.
+
+            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();
+
+            serverTask.Shutdown(serverThread);
+        }
+
+        internal class ServerTask
+        {
+            private readonly DtlsServerProtocol m_serverProtocol;
+            private readonly DatagramTransport m_serverTransport;
+            private volatile bool m_isShutdown = false;
+
+            internal ServerTask(DtlsServerProtocol serverProtocol, DatagramTransport serverTransport)
+            {
+                this.m_serverProtocol = serverProtocol;
+                this.m_serverTransport = serverTransport;
+            }
+
+            public void Run()
+            {
+                try
+                {
+                    TlsCrypto serverCrypto = new BcTlsCrypto();
+
+                    DtlsRequest request = null;
+
+                    // Use DtlsVerifier to require a HelloVerifyRequest cookie exchange before accepting
+                    {
+                        DtlsVerifier verifier = new DtlsVerifier(serverCrypto);
+
+                        // NOTE: Test value only - would typically be the client IP address
+                        byte[] clientID = Encoding.UTF8.GetBytes("MockDtlsClient");
+
+                        int receiveLimit = m_serverTransport.GetReceiveLimit();
+                        int dummyOffset = serverCrypto.SecureRandom.Next(16) + 1;
+                        byte[] buf = new byte[dummyOffset + m_serverTransport.GetReceiveLimit()];
+
+                        do
+                        {
+                            if (m_isShutdown)
+                                return;
+
+                            int length = m_serverTransport.Receive(buf, dummyOffset, receiveLimit, 100);
+                            if (length > 0)
+                            {
+                                request = verifier.VerifyRequest(clientID, buf, dummyOffset, length, m_serverTransport);
+                            }
+                        }
+                        while (request == null);
+                    }
+
+                    // NOTE: A real server would handle each DtlsRequest in a new task/thread and continue accepting
+                    {
+                        MockDtlsServer server = new MockDtlsServer(serverCrypto);
+                        DtlsTransport dtlsTransport = m_serverProtocol.Accept(server, m_serverTransport, request);
+                        byte[] buf = new byte[dtlsTransport.GetReceiveLimit()];
+                        while (!m_isShutdown)
+                        {
+                            int length = dtlsTransport.Receive(buf, 0, buf.Length, 100);
+                            if (length >= 0)
+                            {
+                                dtlsTransport.Send(buf, 0, length);
+                            }
+                        }
+                        dtlsTransport.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/DtlsHandshakeRetransmissionTest.cs b/crypto/test/src/tls/test/DtlsHandshakeRetransmissionTest.cs
new file mode 100644
index 000000000..6c897ff04
--- /dev/null
+++ b/crypto/test/src/tls/test/DtlsHandshakeRetransmissionTest.cs
@@ -0,0 +1,134 @@
+using System;
+using System.Text;
+using System.Threading;
+
+using NUnit.Framework;
+
+using Org.BouncyCastle.Tls.Crypto;
+using Org.BouncyCastle.Tls.Crypto.Impl.BC;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Tls.Tests
+{
+    [TestFixture]
+    public class DtlsHandshakeRetransmissionTest
+    {
+        [Test]
+        public void TestClientServer()
+        {
+            DtlsClientProtocol clientProtocol = new DtlsClientProtocol();
+            DtlsServerProtocol serverProtocol = new DtlsServerProtocol();
+
+            MockDatagramAssociation network = new MockDatagramAssociation(1500);
+
+            ServerTask serverTask = new ServerTask(serverProtocol, network.Server);
+
+            Thread serverThread = new Thread(new ThreadStart(serverTask.Run));
+            serverThread.Start();
+
+            DatagramTransport clientTransport = network.Client;
+
+            clientTransport = new ServerHandshakeDropper(clientTransport, true);
+
+            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();
+
+            serverTask.Shutdown(serverThread);
+        }
+
+        internal class ServerTask
+        {
+            private readonly DtlsServerProtocol m_serverProtocol;
+            private readonly DatagramTransport m_serverTransport;
+            private volatile bool m_isShutdown = false;
+
+            internal ServerTask(DtlsServerProtocol serverProtocol, DatagramTransport serverTransport)
+            {
+                this.m_serverProtocol = serverProtocol;
+                this.m_serverTransport = serverTransport;
+            }
+
+            public void Run()
+            {
+                try
+                {
+                    TlsCrypto serverCrypto = new BcTlsCrypto();
+
+                    DtlsRequest request = null;
+
+                    // Use DtlsVerifier to require a HelloVerifyRequest cookie exchange before accepting
+                    {
+                        DtlsVerifier verifier = new DtlsVerifier(serverCrypto);
+
+                        // NOTE: Test value only - would typically be the client IP address
+                        byte[] clientID = Encoding.UTF8.GetBytes("MockDtlsClient");
+
+                        int receiveLimit = m_serverTransport.GetReceiveLimit();
+                        int dummyOffset = serverCrypto.SecureRandom.Next(16) + 1;
+                        byte[] buf = new byte[dummyOffset + m_serverTransport.GetReceiveLimit()];
+
+                        do
+                        {
+                            if (m_isShutdown)
+                                return;
+
+                            int length = m_serverTransport.Receive(buf, dummyOffset, receiveLimit, 100);
+                            if (length > 0)
+                            {
+                                request = verifier.VerifyRequest(clientID, buf, dummyOffset, length, m_serverTransport);
+                            }
+                        }
+                        while (request == null);
+                    }
+
+                    // NOTE: A real server would handle each DtlsRequest in a new task/thread and continue accepting
+                    {
+                        MockDtlsServer server = new MockDtlsServer(serverCrypto);
+                        DtlsTransport dtlsTransport = m_serverProtocol.Accept(server, m_serverTransport, request);
+                        byte[] buf = new byte[dtlsTransport.GetReceiveLimit()];
+                        while (!m_isShutdown)
+                        {
+                            int length = dtlsTransport.Receive(buf, 0, buf.Length, 100);
+                            if (length >= 0)
+                            {
+                                dtlsTransport.Send(buf, 0, length);
+                            }
+                        }
+                        dtlsTransport.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/DtlsProtocolTest.cs b/crypto/test/src/tls/test/DtlsProtocolTest.cs
index 79b919999..9e3f968d1 100644
--- a/crypto/test/src/tls/test/DtlsProtocolTest.cs
+++ b/crypto/test/src/tls/test/DtlsProtocolTest.cs
@@ -24,9 +24,9 @@ namespace Org.BouncyCastle.Tls.Tests
 
             MockDatagramAssociation network = new MockDatagramAssociation(1500);
 
-            Server server = new Server(serverProtocol, network.Server);
+            ServerTask serverTask = new ServerTask(serverProtocol, network.Server);
 
-            Thread serverThread = new Thread(new ThreadStart(server.Run));
+            Thread serverThread = new Thread(new ThreadStart(serverTask.Run));
             serverThread.Start();
 
             DatagramTransport clientTransport = network.Client;
@@ -53,16 +53,16 @@ namespace Org.BouncyCastle.Tls.Tests
 
             dtlsClient.Close();
 
-            server.Shutdown(serverThread);
+            serverTask.Shutdown(serverThread);
         }
 
-        internal class Server
+        internal class ServerTask
         {
             private readonly DtlsServerProtocol m_serverProtocol;
             private readonly DatagramTransport m_serverTransport;
             private volatile bool m_isShutdown = false;
 
-            internal Server(DtlsServerProtocol serverProtocol, DatagramTransport serverTransport)
+            internal ServerTask(DtlsServerProtocol serverProtocol, DatagramTransport serverTransport)
             {
                 this.m_serverProtocol = serverProtocol;
                 this.m_serverTransport = serverTransport;
@@ -92,7 +92,7 @@ namespace Org.BouncyCastle.Tls.Tests
                             if (m_isShutdown)
                                 return;
 
-                            int length = m_serverTransport.Receive(buf, dummyOffset, receiveLimit, 1000);
+                            int length = m_serverTransport.Receive(buf, dummyOffset, receiveLimit, 100);
                             if (length > 0)
                             {
                                 request = verifier.VerifyRequest(clientID, buf, dummyOffset, length, m_serverTransport);
@@ -108,7 +108,7 @@ namespace Org.BouncyCastle.Tls.Tests
                         byte[] buf = new byte[dtlsTransport.GetReceiveLimit()];
                         while (!m_isShutdown)
                         {
-                            int length = dtlsTransport.Receive(buf, 0, buf.Length, 1000);
+                            int length = dtlsTransport.Receive(buf, 0, buf.Length, 100);
                             if (length >= 0)
                             {
                                 dtlsTransport.Send(buf, 0, length);
diff --git a/crypto/test/src/tls/test/DtlsPskProtocolTest.cs b/crypto/test/src/tls/test/DtlsPskProtocolTest.cs
index f0e685541..412548442 100644
--- a/crypto/test/src/tls/test/DtlsPskProtocolTest.cs
+++ b/crypto/test/src/tls/test/DtlsPskProtocolTest.cs
@@ -21,9 +21,9 @@ namespace Org.BouncyCastle.Tls.Tests
 
             MockDatagramAssociation network = new MockDatagramAssociation(1500);
 
-            Server server = new Server(serverProtocol, network.Server);
+            ServerTask serverTask = new ServerTask(serverProtocol, network.Server);
 
-            Thread serverThread = new Thread(new ThreadStart(server.Run));
+            Thread serverThread = new Thread(new ThreadStart(serverTask.Run));
             serverThread.Start();
 
             DatagramTransport clientTransport = network.Client;
@@ -50,16 +50,16 @@ namespace Org.BouncyCastle.Tls.Tests
 
             dtlsClient.Close();
 
-            server.Shutdown(serverThread);
+            serverTask.Shutdown(serverThread);
         }
 
-        internal class Server
+        internal class ServerTask
         {
             private readonly DtlsServerProtocol m_serverProtocol;
             private readonly DatagramTransport m_serverTransport;
             private volatile bool m_isShutdown = false;
 
-            internal Server(DtlsServerProtocol serverProtocol, DatagramTransport serverTransport)
+            internal ServerTask(DtlsServerProtocol serverProtocol, DatagramTransport serverTransport)
             {
                 this.m_serverProtocol = serverProtocol;
                 this.m_serverTransport = serverTransport;
@@ -74,7 +74,7 @@ namespace Org.BouncyCastle.Tls.Tests
                     byte[] buf = new byte[dtlsServer.GetReceiveLimit()];
                     while (!m_isShutdown)
                     {
-                        int length = dtlsServer.Receive(buf, 0, buf.Length, 1000);
+                        int length = dtlsServer.Receive(buf, 0, buf.Length, 100);
                         if (length >= 0)
                         {
                             dtlsServer.Send(buf, 0, length);
diff --git a/crypto/test/src/tls/test/DtlsRawKeysProtocolTest.cs b/crypto/test/src/tls/test/DtlsRawKeysProtocolTest.cs
new file mode 100644
index 000000000..36e22b58d
--- /dev/null
+++ b/crypto/test/src/tls/test/DtlsRawKeysProtocolTest.cs
@@ -0,0 +1,353 @@
+using System;
+using System.Text;
+using System.Threading;
+
+using NUnit.Framework;
+
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Tls.Crypto;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Tls.Tests
+{
+    [TestFixture]
+    public class DtlsRawKeysProtocolTest
+    {
+        private readonly SecureRandom Random = new SecureRandom();
+
+        [Test]
+        public void TestClientSendsExtensionButServerDoesNotSupportIt()
+        {
+            TestClientSendsExtensionButServerDoesNotSupportIt(ProtocolVersion.DTLSv12);
+        }
+
+        // TODO[dtls13]
+        //[Test]
+        //public void TestClientSendsExtensionButServerDoesNotSupportIt_13()
+        //{
+        //    TestClientSendsExtensionButServerDoesNotSupportIt(ProtocolVersion.DTLSv13);
+        //}
+
+        private void TestClientSendsExtensionButServerDoesNotSupportIt(ProtocolVersion tlsVersion)
+        {
+            MockRawKeysTlsClient client = new MockRawKeysTlsClient(CertificateType.X509, -1,
+                new short[]{ CertificateType.RawPublicKey, CertificateType.X509 }, null, GenerateKeyPair(),
+                tlsVersion);
+            MockRawKeysTlsServer server = new MockRawKeysTlsServer(CertificateType.X509, -1, null, GenerateKeyPair(),
+                tlsVersion);
+            PumpData(client, server);
+        }
+
+        [Test]
+        public void TestExtensionsAreOmittedIfSpecifiedButOnlyContainX509()
+        {
+            TestExtensionsAreOmittedIfSpecifiedButOnlyContainX509(ProtocolVersion.DTLSv12);
+        }
+
+        // TODO[dtls13]
+        //[Test]
+        //public void TestExtensionsAreOmittedIfSpecifiedButOnlyContainX509_13()
+        //{
+        //    TestExtensionsAreOmittedIfSpecifiedButOnlyContainX509(ProtocolVersion.DTLSv13);
+        //}
+
+        private void TestExtensionsAreOmittedIfSpecifiedButOnlyContainX509(ProtocolVersion tlsVersion)
+        {
+            MockRawKeysTlsClient client = new MockRawKeysTlsClient(CertificateType.X509, CertificateType.X509,
+                new short[]{ CertificateType.X509 }, new short[]{ CertificateType.X509 }, GenerateKeyPair(),
+                tlsVersion);
+            MockRawKeysTlsServer server = new MockRawKeysTlsServer(CertificateType.X509, CertificateType.X509,
+                new short[]{ CertificateType.X509 }, GenerateKeyPair(), tlsVersion);
+            PumpData(client, server);
+
+            Assert.IsFalse(server.m_receivedClientExtensions.ContainsKey(ExtensionType.client_certificate_type),
+                "client cert type extension should not be sent");
+            Assert.IsFalse(server.m_receivedClientExtensions.ContainsKey(ExtensionType.server_certificate_type),
+                "server cert type extension should not be sent");
+        }
+
+        [Test]
+        public void TestBothSidesUseRawKey()
+        {
+            TestBothSidesUseRawKey(ProtocolVersion.DTLSv12);
+        }
+
+        // TODO[dtls13]
+        //[Test]
+        //public void TestBothSidesUseRawKey_13()
+        //{
+        //    TestBothSidesUseRawKey(ProtocolVersion.DTLSv13);
+        //}
+
+        private void TestBothSidesUseRawKey(ProtocolVersion tlsVersion)
+        {
+            MockRawKeysTlsClient client = new MockRawKeysTlsClient(CertificateType.RawPublicKey,
+                CertificateType.RawPublicKey, new short[]{ CertificateType.RawPublicKey },
+                new short[]{ CertificateType.RawPublicKey }, GenerateKeyPair(), tlsVersion);
+            MockRawKeysTlsServer server = new MockRawKeysTlsServer(CertificateType.RawPublicKey,
+                CertificateType.RawPublicKey, new short[]{ CertificateType.RawPublicKey }, GenerateKeyPair(),
+                tlsVersion);
+            PumpData(client, server);
+        }
+
+        [Test]
+        public void TestServerUsesRawKeyAndClientIsAnonymous()
+        {
+            TestServerUsesRawKeyAndClientIsAnonymous(ProtocolVersion.DTLSv12);
+        }
+
+        // TODO[dtls13]
+        //[Test]
+        //public void TestServerUsesRawKeyAndClientIsAnonymous_13()
+        //{
+        //    TestServerUsesRawKeyAndClientIsAnonymous(ProtocolVersion.DTLSv13);
+        //}
+
+        private void TestServerUsesRawKeyAndClientIsAnonymous(ProtocolVersion tlsVersion)
+        {
+            MockRawKeysTlsClient client = new MockRawKeysTlsClient(CertificateType.RawPublicKey, -1,
+                new short[]{ CertificateType.RawPublicKey }, null, GenerateKeyPair(), tlsVersion);
+            MockRawKeysTlsServer server = new MockRawKeysTlsServer(CertificateType.RawPublicKey, -1, null,
+                GenerateKeyPair(), tlsVersion);
+            PumpData(client, server);
+        }
+
+        [Test]
+        public void TestServerUsesRawKeyAndClientUsesX509()
+        {
+            TestServerUsesRawKeyAndClientUsesX509(ProtocolVersion.DTLSv12);
+        }
+
+        // TODO[dtls13]
+        //[Test]
+        //public void TestServerUsesRawKeyAndClientUsesX509_13()
+        //{
+        //    TestServerUsesRawKeyAndClientUsesX509(ProtocolVersion.DTLSv13);
+        //}
+
+        private void TestServerUsesRawKeyAndClientUsesX509(ProtocolVersion tlsVersion)
+        {
+            MockRawKeysTlsClient client = new MockRawKeysTlsClient(CertificateType.RawPublicKey,
+                CertificateType.X509, new short[]{ CertificateType.RawPublicKey }, null, GenerateKeyPair(),
+                tlsVersion);
+            MockRawKeysTlsServer server = new MockRawKeysTlsServer(CertificateType.RawPublicKey,
+                CertificateType.X509, null, GenerateKeyPair(), tlsVersion);
+            PumpData(client, server);
+        }
+
+        [Test]
+        public void TestServerUsesX509AndClientUsesRawKey()
+        {
+            TestServerUsesX509AndClientUsesRawKey(ProtocolVersion.DTLSv12);
+        }
+
+        // TODO[dtls13]
+        //[Test]
+        //public void TestServerUsesX509AndClientUsesRawKey_13()
+        //{
+        //    TestServerUsesX509AndClientUsesRawKey(ProtocolVersion.DTLSv13);
+        //}
+
+        private void TestServerUsesX509AndClientUsesRawKey(ProtocolVersion tlsVersion)
+        {
+            MockRawKeysTlsClient client = new MockRawKeysTlsClient(CertificateType.X509, CertificateType.RawPublicKey,
+                null, new short[]{ CertificateType.RawPublicKey }, GenerateKeyPair(), tlsVersion);
+            MockRawKeysTlsServer server = new MockRawKeysTlsServer(CertificateType.X509, CertificateType.RawPublicKey,
+                new short[]{ CertificateType.RawPublicKey }, GenerateKeyPair(), tlsVersion);
+            PumpData(client, server);
+        }
+
+        // NOTE: Test disabled because of problems getting a clean exit of the DTLS server after a fatal alert.
+/*
+        [Test]
+        public void TestClientSendsClientCertExtensionButServerHasNoCommonTypes()
+        {
+            TestClientSendsClientCertExtensionButServerHasNoCommonTypes(ProtocolVersion.DTLSv12);
+        }
+
+        // TODO[dtls13]
+        //[Test]
+        //public void TestClientSendsClientCertExtensionButServerHasNoCommonTypes_13()
+        //{
+        //    TestClientSendsClientCertExtensionButServerHasNoCommonTypes(ProtocolVersion.DTLSv13);
+        //}
+
+        private void TestClientSendsClientCertExtensionButServerHasNoCommonTypes(ProtocolVersion tlsVersion)
+        {
+            try
+            {
+                MockRawKeysTlsClient client = new MockRawKeysTlsClient(CertificateType.X509,
+                    CertificateType.RawPublicKey, null, new short[]{ CertificateType.RawPublicKey }, GenerateKeyPair(),
+                    tlsVersion);
+                MockRawKeysTlsServer server = new MockRawKeysTlsServer(CertificateType.X509, CertificateType.X509,
+                    new short[]{ CertificateType.X509 }, GenerateKeyPair(), tlsVersion);
+                PumpData(client, server);
+                Assert.Fail("Should have caused unsupported_certificate alert");
+            }
+            catch (TlsFatalAlertReceived alert)
+            {
+                Assert.AreEqual(AlertDescription.unsupported_certificate, alert.AlertDescription,
+                    "Should have caused unsupported_certificate alert");
+            }
+        }
+*/
+
+        // NOTE: Test disabled because of problems getting a clean exit of the DTLS server after a fatal alert.
+/*
+        [Test]
+        public void TestClientSendsServerCertExtensionButServerHasNoCommonTypes()
+        {
+            TestClientSendsServerCertExtensionButServerHasNoCommonTypes(ProtocolVersion.DTLSv12);
+        }
+
+        // TODO[dtls13]
+        //[Test]
+        //public void TestClientSendsServerCertExtensionButServerHasNoCommonTypes_13()
+        //{
+        //    TestClientSendsServerCertExtensionButServerHasNoCommonTypes(ProtocolVersion.DTLSv13);
+        //}
+
+        private void TestClientSendsServerCertExtensionButServerHasNoCommonTypes(ProtocolVersion tlsVersion)
+        {
+            try
+            {
+                MockRawKeysTlsClient client = new MockRawKeysTlsClient(CertificateType.RawPublicKey,
+                    CertificateType.RawPublicKey, new short[]{ CertificateType.RawPublicKey }, null, GenerateKeyPair(),
+                    tlsVersion);
+                MockRawKeysTlsServer server = new MockRawKeysTlsServer(CertificateType.X509,
+                    CertificateType.RawPublicKey, new short[]{ CertificateType.RawPublicKey }, GenerateKeyPair(),
+                    tlsVersion);
+                PumpData(client, server);
+                Assert.Fail("Should have caused unsupported_certificate alert");
+            }
+            catch (TlsFatalAlertReceived alert)
+            {
+                Assert.AreEqual(AlertDescription.unsupported_certificate, alert.AlertDescription,
+                    "Should have caused unsupported_certificate alert");
+            }
+        }
+*/
+
+        private Ed25519PrivateKeyParameters GenerateKeyPair()
+        {
+            return new Ed25519PrivateKeyParameters(Random);
+        }
+
+        private void PumpData(TlsClient client, TlsServer server)
+        {
+            DtlsClientProtocol clientProtocol = new DtlsClientProtocol();
+            DtlsServerProtocol serverProtocol = new DtlsServerProtocol();
+
+            MockDatagramAssociation network = new MockDatagramAssociation(1500);
+
+            ServerTask serverTask = new ServerTask(serverProtocol, server, network.Server);
+
+            Thread serverThread = new Thread(new ThreadStart(serverTask.Run));
+            serverThread.Start();
+
+            DatagramTransport clientTransport = network.Client;
+
+            clientTransport = new UnreliableDatagramTransport(clientTransport, Random, 0, 0);
+
+            clientTransport = new LoggingDatagramTransport(clientTransport, Console.Out);
+
+            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();
+
+            serverTask.Shutdown(serverThread);
+        }
+
+        internal class ServerTask
+        {
+            private readonly DtlsServerProtocol m_serverProtocol;
+            private readonly TlsServer m_server;
+            private readonly DatagramTransport m_serverTransport;
+            private volatile bool m_isShutdown = false;
+
+            internal ServerTask(DtlsServerProtocol serverProtocol, TlsServer server, DatagramTransport serverTransport)
+            {
+                this.m_serverProtocol = serverProtocol;
+                this.m_server = server;
+                this.m_serverTransport = serverTransport;
+            }
+
+            public void Run()
+            {
+                try
+                {
+                    TlsCrypto serverCrypto = m_server.Crypto;
+
+                    DtlsRequest request = null;
+
+                    // Use DtlsVerifier to require a HelloVerifyRequest cookie exchange before accepting
+                    {
+                        DtlsVerifier verifier = new DtlsVerifier(serverCrypto);
+
+                        // NOTE: Test value only - would typically be the client IP address
+                        byte[] clientID = Encoding.UTF8.GetBytes("MockRawKeysTlsClient");
+
+                        int receiveLimit = m_serverTransport.GetReceiveLimit();
+                        int dummyOffset = serverCrypto.SecureRandom.Next(16) + 1;
+                        byte[] buf = new byte[dummyOffset + m_serverTransport.GetReceiveLimit()];
+
+                        do
+                        {
+                            if (m_isShutdown)
+                                return;
+
+                            int length = m_serverTransport.Receive(buf, dummyOffset, receiveLimit, 100);
+                            if (length > 0)
+                            {
+                                request = verifier.VerifyRequest(clientID, buf, dummyOffset, length, m_serverTransport);
+                            }
+                        }
+                        while (request == null);
+                    }
+
+                    // NOTE: A real server would handle each DtlsRequest in a new task/thread and continue accepting
+                    {
+                        DtlsTransport dtlsTransport = m_serverProtocol.Accept(m_server, m_serverTransport, request);
+                        byte[] buf = new byte[dtlsTransport.GetReceiveLimit()];
+                        while (!m_isShutdown)
+                        {
+                            int length = dtlsTransport.Receive(buf, 0, buf.Length, 100);
+                            if (length >= 0)
+                            {
+                                dtlsTransport.Send(buf, 0, length);
+                            }
+                        }
+                        dtlsTransport.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
index d93f17c27..32ddc6c21 100644
--- a/crypto/test/src/tls/test/DtlsTestCase.cs
+++ b/crypto/test/src/tls/test/DtlsTestCase.cs
@@ -36,9 +36,9 @@ namespace Org.BouncyCastle.Tls.Tests
             TlsTestClientImpl clientImpl = new TlsTestClientImpl(config);
             TlsTestServerImpl serverImpl = new TlsTestServerImpl(config);
 
-            Server server = new Server(this, serverProtocol, network.Server, serverImpl);
+            ServerTask serverTask = new ServerTask(this, serverProtocol, network.Server, serverImpl);
 
-            Thread serverThread = new Thread(new ThreadStart(server.Run));
+            Thread serverThread = new Thread(new ThreadStart(serverTask.Run));
             serverThread.Start();
 
             Exception caught = null;
@@ -73,7 +73,7 @@ namespace Org.BouncyCastle.Tls.Tests
                 LogException(caught);
             }
 
-            server.Shutdown(serverThread);
+            serverTask.Shutdown(serverThread);
 
             // TODO Add checks that the various streams were closed
 
@@ -90,7 +90,7 @@ namespace Org.BouncyCastle.Tls.Tests
             if (config.expectFatalAlertConnectionEnd == -1)
             {
                 Assert.IsNull(caught, "Unexpected client exception");
-                Assert.IsNull(server.Caught, "Unexpected server exception");
+                Assert.IsNull(serverTask.Caught, "Unexpected server exception");
             }
         }
 
@@ -103,7 +103,7 @@ namespace Org.BouncyCastle.Tls.Tests
             }
         }
 
-        internal class Server
+        internal class ServerTask
         {
             private readonly DtlsTestCase m_outer;
             private readonly DtlsTestServerProtocol m_serverProtocol;
@@ -113,7 +113,7 @@ namespace Org.BouncyCastle.Tls.Tests
             private volatile bool m_isShutdown = false;
             private Exception m_caught = null;
 
-            internal Server(DtlsTestCase outer, DtlsTestServerProtocol serverProtocol,
+            internal ServerTask(DtlsTestCase outer, DtlsTestServerProtocol serverProtocol,
                 DatagramTransport serverTransport, TlsTestServerImpl serverImpl)
             {
                 this.m_outer = outer;
diff --git a/crypto/test/src/tls/test/FilteredDatagramTransport.cs b/crypto/test/src/tls/test/FilteredDatagramTransport.cs
new file mode 100644
index 000000000..23c0839d6
--- /dev/null
+++ b/crypto/test/src/tls/test/FilteredDatagramTransport.cs
@@ -0,0 +1,112 @@
+using System;
+
+using Org.BouncyCastle.Utilities.Date;
+
+namespace Org.BouncyCastle.Tls.Tests
+{
+    public class FilteredDatagramTransport
+        : DatagramTransport
+    {
+        public delegate bool FilterPredicate(byte[] buf, int off, int len);
+
+        public static bool AlwaysAllow(byte[] buf, int off, int len) => true;
+
+        private readonly DatagramTransport m_transport;
+
+        private readonly FilterPredicate m_allowReceiving, m_allowSending;
+
+        public FilteredDatagramTransport(DatagramTransport transport, FilterPredicate allowReceiving,
+            FilterPredicate allowSending)
+        {
+            m_transport = transport;
+            m_allowReceiving = allowReceiving;
+            m_allowSending = allowSending;
+        }
+
+        public virtual int GetReceiveLimit() => m_transport.GetReceiveLimit();
+
+        public virtual int GetSendLimit() => 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 || m_allowReceiving(buf, off, len))
+                    return length;
+
+                Console.WriteLine("PACKET FILTERED ({0} byte packet not received)", length);
+
+                long now = DateTimeUtilities.CurrentUnixMs();
+                if (now >= endMillis)
+                    return -1;
+
+                waitMillis = (int)(endMillis - now);
+            }
+        }
+
+//#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+#if NET6_0_OR_GREATER
+        public virtual int Receive(Span<byte> buffer, int waitMillis)
+        {
+            long endMillis = DateTimeUtilities.CurrentUnixMs() + waitMillis;
+            for (;;)
+            {
+                int length = m_transport.Receive(buffer, waitMillis);
+                if (length < 0 || m_allowReceiving(buffer.ToArray(), 0, buffer.Length))
+                    return length;
+
+                Console.WriteLine("PACKET FILTERED ({0} byte packet not received)", length);
+
+                long now = DateTimeUtilities.CurrentUnixMs();
+                if (now >= endMillis)
+                    return -1;
+
+                waitMillis = (int)(endMillis - now);
+            }
+        }
+#endif
+
+        public virtual void Send(byte[] buf, int off, int len)
+        {
+            if (!m_allowSending(buf, off, len))
+            {
+                Console.WriteLine("PACKET FILTERED ({0} byte packet not sent)", len);
+            }
+            else
+            {
+                m_transport.Send(buf, off, len);
+            }
+        }
+
+        //#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+#if NET6_0_OR_GREATER
+        public virtual void Send(ReadOnlySpan<byte> buffer)
+        {
+            if (!m_allowSending(buffer.ToArray(), 0, buffer.Length))
+            {
+                Console.WriteLine("PACKET FILTERED ({0} byte packet not sent)", buffer.Length);
+            }
+            else
+            {
+                m_transport.Send(buffer);
+            }
+        }
+#endif
+
+        public virtual void Close() => m_transport.Close();
+
+        //static FilterPredicate ALWAYS_ALLOW = new FilterPredicate() {
+        //    @Override
+        //    public boolean allowPacket(byte[] buf, int off, int len)
+        //    {
+        //        return true;
+        //    }
+        //};
+
+        //interface FilterPredicate {
+        //    boolean allowPacket(byte[] buf, int off, int len);
+        //}
+    }
+}
diff --git a/crypto/test/src/tls/test/LoggingDatagramTransport.cs b/crypto/test/src/tls/test/LoggingDatagramTransport.cs
index 59113cf73..d03e99551 100644
--- a/crypto/test/src/tls/test/LoggingDatagramTransport.cs
+++ b/crypto/test/src/tls/test/LoggingDatagramTransport.cs
@@ -22,15 +22,9 @@ namespace Org.BouncyCastle.Tls.Tests
             this.m_launchTimestamp = DateTimeUtilities.CurrentUnixMs();
         }
 
-        public virtual int GetReceiveLimit()
-        {
-            return m_transport.GetReceiveLimit();
-        }
+        public virtual int GetReceiveLimit() => m_transport.GetReceiveLimit();
 
-        public virtual int GetSendLimit()
-        {
-            return m_transport.GetSendLimit();
-        }
+        public virtual int GetSendLimit() => m_transport.GetSendLimit();
 
         public virtual int Receive(byte[] buf, int off, int len, int waitMillis)
         {
@@ -80,10 +74,7 @@ namespace Org.BouncyCastle.Tls.Tests
         }
 #endif
 
-        public virtual void Close()
-        {
-            m_transport.Close();
-        }
+        public virtual void Close() => m_transport.Close();
 
 //#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
 #if NET6_0_OR_GREATER
diff --git a/crypto/test/src/tls/test/MinimalHandshakeAggregator.cs b/crypto/test/src/tls/test/MinimalHandshakeAggregator.cs
new file mode 100644
index 000000000..645e32851
--- /dev/null
+++ b/crypto/test/src/tls/test/MinimalHandshakeAggregator.cs
@@ -0,0 +1,254 @@
+using System;
+
+using Org.BouncyCastle.Utilities.Date;
+
+namespace Org.BouncyCastle.Tls.Tests
+{
+    /**
+     * A very minimal and stupid class to aggregate DTLS handshake messages.  Only sufficient for unit tests.
+     */
+    public class MinimalHandshakeAggregator
+        : DatagramTransport
+    {
+        private readonly DatagramTransport m_transport;
+
+        private readonly bool m_aggregateReceiving, m_aggregateSending;
+
+        private byte[] m_receiveBuf, m_sendBuf;
+
+        private int m_receiveRecordCount, m_sendRecordCount;
+
+        private byte[] AddToBuf(byte[] baseBuf, byte[] buf, int off, int len)
+        {
+            byte[] ret = new byte[baseBuf.Length + len];
+            Array.Copy(baseBuf, 0, ret, 0, baseBuf.Length);
+            Array.Copy(buf, off, ret, baseBuf.Length, len);
+            return ret;
+        }
+
+//#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+#if NET6_0_OR_GREATER
+        private byte[] AddToBuf(byte[] baseBuf, ReadOnlySpan<byte> buf)
+        {
+            byte[] ret = new byte[baseBuf.Length + buf.Length];
+            Array.Copy(baseBuf, 0, ret, 0, baseBuf.Length);
+            buf.CopyTo(ret[baseBuf.Length..]);
+            return ret;
+        }
+#endif
+
+        private void AddToReceiveBuf(byte[] buf, int off, int len)
+        {
+            m_receiveBuf = AddToBuf(m_receiveBuf, buf, off, len);
+            m_receiveRecordCount++;
+        }
+
+//#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+#if NET6_0_OR_GREATER
+        private void AddToReceiveBuf(ReadOnlySpan<byte> buf)
+        {
+            m_receiveBuf = AddToBuf(m_receiveBuf, buf);
+            m_receiveRecordCount++;
+        }
+#endif
+
+        private void ResetReceiveBuf()
+        {
+            m_receiveBuf = new byte[0];
+            m_receiveRecordCount = 0;
+        }
+
+        private void AddToSendBuf(byte[] buf, int off, int len)
+        {
+            m_sendBuf = AddToBuf(m_sendBuf, buf, off, len);
+            m_sendRecordCount++;
+        }
+
+//#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+#if NET6_0_OR_GREATER
+        private void AddToSendBuf(ReadOnlySpan<byte> buf)
+        {
+            m_sendBuf = AddToBuf(m_sendBuf, buf);
+            m_sendRecordCount++;
+        }
+#endif
+
+        private void ResetSendBuf()
+        {
+            m_sendBuf = new byte[0];
+            m_sendRecordCount = 0;
+        }
+
+        /** Whether the buffered aggregated data should be flushed after this packet.
+         * This is done on the end of the first flight - ClientHello and ServerHelloDone - and anything that is
+         * Epoch 1.
+         */
+        private bool FlushAfterThisPacket(byte[] buf, int off, int len)
+        {
+            int epoch = TlsUtilities.ReadUint16(buf, off + 3);
+            if (epoch > 0)
+                return true;
+
+            short contentType = TlsUtilities.ReadUint8(buf, off);
+            if (ContentType.handshake != contentType)
+                return false;
+
+            short msgType = TlsUtilities.ReadUint8(buf, off + 13);
+            switch (msgType)
+            {
+            case HandshakeType.client_hello:
+            case HandshakeType.server_hello_done:
+                return true;
+            default:
+                return false;
+            }
+        }
+
+//#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+#if NET6_0_OR_GREATER
+        private bool FlushAfterThisPacket(ReadOnlySpan<byte> buffer)
+        {
+            int epoch = TlsUtilities.ReadUint16(buffer[3..]);
+            if (epoch > 0)
+                return true;
+
+            short contentType = TlsUtilities.ReadUint8(buffer);
+            if (ContentType.handshake != contentType)
+                return false;
+
+            short msgType = TlsUtilities.ReadUint8(buffer[13..]);
+            switch (msgType)
+            {
+            case HandshakeType.client_hello:
+            case HandshakeType.server_hello_done:
+                return true;
+            default:
+                return false;
+            }
+        }
+#endif
+
+        public MinimalHandshakeAggregator(DatagramTransport transport, bool aggregateReceiving, bool aggregateSending)
+        {
+            m_transport = transport;
+            m_aggregateReceiving = aggregateReceiving;
+            m_aggregateSending = aggregateSending;
+
+            ResetReceiveBuf();
+            ResetSendBuf();
+        }
+
+        public virtual int GetReceiveLimit() => m_transport.GetReceiveLimit();
+
+        public virtual int GetSendLimit() => 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 || !m_aggregateReceiving)
+                    return length;
+
+                AddToReceiveBuf(buf, off, length);
+
+                if (FlushAfterThisPacket(buf, off, length))
+                {
+                    if (m_receiveRecordCount > 1)
+                    {
+                        Console.WriteLine("RECEIVING {0} RECORDS IN {1} BYTE PACKET", m_receiveRecordCount, length);
+                    }
+                    Array.Copy(m_receiveBuf, 0, buf, off, System.Math.Min(len, m_receiveBuf.Length));
+                    ResetReceiveBuf();
+                    return length;
+                }
+
+                long now = DateTimeUtilities.CurrentUnixMs();
+                if (now >= endMillis)
+                    return -1;
+
+                waitMillis = (int)(endMillis - now);
+            }
+        }
+
+//#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+#if NET6_0_OR_GREATER
+        public virtual int Receive(Span<byte> buffer, int waitMillis)
+        {
+            long endMillis = DateTimeUtilities.CurrentUnixMs() + waitMillis;
+            for (;;)
+            {
+                int length = m_transport.Receive(buffer, waitMillis);
+                if (length < 0 || !m_aggregateReceiving)
+                    return length;
+
+                AddToReceiveBuf(buffer);
+
+                if (FlushAfterThisPacket(buffer))
+                {
+                    if (m_receiveRecordCount > 1)
+                    {
+                        Console.WriteLine("RECEIVING {0} RECORDS IN {1} BYTE PACKET", m_receiveRecordCount, length);
+                    }
+                    int resultLength = System.Math.Min(buffer.Length, m_receiveBuf.Length);
+                    m_receiveBuf.AsSpan(0, resultLength).CopyTo(buffer);
+                    ResetReceiveBuf();
+                    return resultLength;
+                }
+
+                long now = DateTimeUtilities.CurrentUnixMs();
+                if (now >= endMillis)
+                    return -1;
+
+                waitMillis = (int)(endMillis - now);
+            }
+        }
+#endif
+
+        public virtual void Send(byte[] buf, int off, int len)
+        {
+            if (!m_aggregateSending)
+            {
+                m_transport.Send(buf, off, len);
+                return;
+            }
+            AddToSendBuf(buf, off, len);
+
+            if (FlushAfterThisPacket(buf, off, len))
+            {
+                if (m_sendRecordCount > 1)
+                {
+                    Console.WriteLine("SENDING {0} RECORDS IN {1} BYTE PACKET", m_sendRecordCount, m_sendBuf.Length);
+                }
+                m_transport.Send(m_sendBuf, 0, m_sendBuf.Length);
+                ResetSendBuf();
+            }
+        }
+
+        //#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+#if NET6_0_OR_GREATER
+        public virtual void Send(ReadOnlySpan<byte> buffer)
+        {
+            if (!m_aggregateSending)
+            {
+                m_transport.Send(buffer);
+                return;
+            }
+            AddToSendBuf(buffer);
+
+            if (FlushAfterThisPacket(buffer))
+            {
+                if (m_sendRecordCount > 1)
+                {
+                    Console.WriteLine("SENDING {0} RECORDS IN {1} BYTE PACKET", m_sendRecordCount, m_sendBuf.Length);
+                }
+                m_transport.Send(m_sendBuf, 0, m_sendBuf.Length);
+                ResetSendBuf();
+            }
+        }
+#endif
+
+        public virtual void Close() => m_transport.Close();
+    }
+}
diff --git a/crypto/test/src/tls/test/MockDtlsClient.cs b/crypto/test/src/tls/test/MockDtlsClient.cs
index 9898acd08..2e7ab9450 100644
--- a/crypto/test/src/tls/test/MockDtlsClient.cs
+++ b/crypto/test/src/tls/test/MockDtlsClient.cs
@@ -15,6 +15,8 @@ namespace Org.BouncyCastle.Tls.Tests
     {
         internal TlsSession m_session;
 
+        private int m_handshakeTimeoutMillis = 0;
+
         internal MockDtlsClient(TlsSession session)
             : base(new BcTlsCrypto())
         {
@@ -26,6 +28,10 @@ namespace Org.BouncyCastle.Tls.Tests
             return this.m_session;
         }
 
+        public override int GetHandshakeTimeoutMillis() => m_handshakeTimeoutMillis;
+
+        public void SetHandshakeTimeoutMillis(int millis) => m_handshakeTimeoutMillis = millis;
+
         public override void NotifyAlertRaised(short alertLevel, short alertDescription, string message,
             Exception cause)
         {
diff --git a/crypto/test/src/tls/test/PipedStream.cs b/crypto/test/src/tls/test/PipedStream.cs
index 82cd352af..66b913e10 100644
--- a/crypto/test/src/tls/test/PipedStream.cs
+++ b/crypto/test/src/tls/test/PipedStream.cs
@@ -26,45 +26,25 @@ namespace Org.BouncyCastle.Tls.Tests
             }
         }
 
-        public override bool CanRead
-        {
-            get { return true; }
-        }
-
-        public override bool CanSeek
-        {
-            get { return false; }
-        }
-
-        public override bool CanWrite
-        {
-            get { return true; }
-        }
+        public override bool CanRead => true;
+        public override bool CanSeek => false;
+        public override bool CanWrite => true;
 
         public override void Flush()
         {
         }
 
-        public override long Length
-        {
-            get { throw new NotImplementedException(); }
-        }
+        public override long Length => throw new NotSupportedException();
 
         public override long Position
         {
-            get { throw new NotImplementedException(); }
-            set { throw new NotImplementedException(); }
+            get { throw new NotSupportedException(); }
+            set { throw new NotSupportedException(); }
         }
 
-        public override long Seek(long offset, SeekOrigin origin)
-        {
-            throw new NotImplementedException();
-        }
+        public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException();
 
-        public override void SetLength(long value)
-        {
-            throw new NotImplementedException();
-        }
+        public override void SetLength(long value) => throw new NotSupportedException();
 
         public override int Read(byte[] buffer, int offset, int count)
         {
diff --git a/crypto/test/src/tls/test/PskTls13ServerTest.cs b/crypto/test/src/tls/test/PskTls13ServerTest.cs
index f48832fe9..7b5f23665 100644
--- a/crypto/test/src/tls/test/PskTls13ServerTest.cs
+++ b/crypto/test/src/tls/test/PskTls13ServerTest.cs
@@ -28,8 +28,8 @@ namespace Org.BouncyCastle.Tls.Tests
                     TcpClient s = ss.AcceptTcpClient();
                     Console.WriteLine("--------------------------------------------------------------------------------");
                     Console.WriteLine("Accepted " + s);
-                    Server serverRun = new Server(s, stdout);
-                    Thread t = new Thread(new ThreadStart(serverRun.Run));
+                    ServerTask serverTask = new ServerTask(s, stdout);
+                    Thread t = new Thread(new ThreadStart(serverTask.Run));
                     t.Start();
                 }
             }
@@ -39,12 +39,12 @@ namespace Org.BouncyCastle.Tls.Tests
             }
         }
 
-        internal class Server
+        internal class ServerTask
         {
             private readonly TcpClient s;
             private readonly Stream stdout;
 
-            internal Server(TcpClient s, Stream stdout)
+            internal ServerTask(TcpClient s, Stream stdout)
             {
                 this.s = s;
                 this.stdout = stdout;
diff --git a/crypto/test/src/tls/test/PskTlsServerTest.cs b/crypto/test/src/tls/test/PskTlsServerTest.cs
index 4d1d4c722..3a54ee4ff 100644
--- a/crypto/test/src/tls/test/PskTlsServerTest.cs
+++ b/crypto/test/src/tls/test/PskTlsServerTest.cs
@@ -33,8 +33,8 @@ namespace Org.BouncyCastle.Tls.Tests
                     TcpClient s = ss.AcceptTcpClient();
                     Console.WriteLine("--------------------------------------------------------------------------------");
                     Console.WriteLine("Accepted " + s);
-                    Server serverRun = new Server(s, stdout);
-                    Thread t = new Thread(new ThreadStart(serverRun.Run));
+                    ServerTask serverTask = new ServerTask(s, stdout);
+                    Thread t = new Thread(new ThreadStart(serverTask.Run));
                     t.Start();
                 }
             }
@@ -44,12 +44,12 @@ namespace Org.BouncyCastle.Tls.Tests
             }
         }
 
-        internal class Server
+        internal class ServerTask
         {
             private readonly TcpClient s;
             private readonly Stream stdout;
 
-            internal Server(TcpClient s, Stream stdout)
+            internal ServerTask(TcpClient s, Stream stdout)
             {
                 this.s = s;
                 this.stdout = stdout;
diff --git a/crypto/test/src/tls/test/ServerHandshakeDropper.cs b/crypto/test/src/tls/test/ServerHandshakeDropper.cs
new file mode 100644
index 000000000..89c752333
--- /dev/null
+++ b/crypto/test/src/tls/test/ServerHandshakeDropper.cs
@@ -0,0 +1,63 @@
+using System;
+
+namespace Org.BouncyCastle.Tls.Tests
+{
+    /** This is a [Transport] wrapper which causes the first retransmission of the second flight of a server
+     * handshake to be dropped. */
+    public class ServerHandshakeDropper
+        : FilteredDatagramTransport
+    {
+        private static FilterPredicate Choose(bool condition, FilterPredicate left, FilterPredicate right)
+        {
+            if (condition) { return left; } else { return right; }
+        }
+
+        public ServerHandshakeDropper(DatagramTransport transport, bool dropOnReceive)
+            : base(transport,
+                Choose(dropOnReceive, new DropFirstServerFinalFlight().AllowPacket, AlwaysAllow),
+                Choose(dropOnReceive, AlwaysAllow, new DropFirstServerFinalFlight().AllowPacket))
+        {
+        }
+
+        /** This drops the first instance of DTLS packets that either begin with a ChangeCipherSpec, or handshake in
+         * epoch 1.  This is the server's final flight of the handshake.  It will test whether the client properly
+         * retransmits its second flight, and the server properly retransmits the dropped flight.
+         */
+        private class DropFirstServerFinalFlight
+        {
+            private bool m_sawChangeCipherSpec = false;
+            private bool m_sawEpoch1Handshake = false;
+
+            private bool IsChangeCipherSpec(byte[] buf, int off, int len)
+            {
+                short contentType = TlsUtilities.ReadUint8(buf, off);
+                return ContentType.change_cipher_spec == contentType;
+            }
+
+            private bool IsEpoch1Handshake(byte[] buf, int off, int len)
+            {
+                short contentType = TlsUtilities.ReadUint8(buf, off);
+                if (ContentType.handshake != contentType)
+                    return false;
+
+                int epoch = TlsUtilities.ReadUint16(buf, off + 3);
+                return 1 == epoch;
+            }
+
+            public bool AllowPacket(byte[] buf, int off, int len)
+            {
+                if (!m_sawChangeCipherSpec && IsChangeCipherSpec(buf, off, len))
+                {
+                    m_sawChangeCipherSpec = true;
+                    return false;
+                }
+                if (!m_sawEpoch1Handshake && IsEpoch1Handshake(buf, off, len))
+                {
+                    m_sawEpoch1Handshake = true;
+                    return false;
+                }
+                return true;
+            }
+        }
+    }
+}
diff --git a/crypto/test/src/tls/test/Tls13PskProtocolTest.cs b/crypto/test/src/tls/test/Tls13PskProtocolTest.cs
index b66e781a2..7684a54f4 100644
--- a/crypto/test/src/tls/test/Tls13PskProtocolTest.cs
+++ b/crypto/test/src/tls/test/Tls13PskProtocolTest.cs
@@ -24,8 +24,8 @@ namespace Org.BouncyCastle.Tls.Tests
             MockPskTls13Client client = new MockPskTls13Client();
             MockPskTls13Server server = new MockPskTls13Server();
 
-            Server serverRun = new Server(serverProtocol, server);
-            Thread serverThread = new Thread(new ThreadStart(serverRun.Run));
+            ServerTask serverTask = new ServerTask(serverProtocol, server);
+            Thread serverThread = new Thread(new ThreadStart(serverTask.Run));
             serverThread.Start();
 
             clientProtocol.Connect(client);
@@ -47,12 +47,12 @@ namespace Org.BouncyCastle.Tls.Tests
             serverThread.Join();
         }
 
-        internal class Server
+        internal class ServerTask
         {
             private readonly TlsServerProtocol m_serverProtocol;
             private readonly TlsServer m_server;
 
-            internal Server(TlsServerProtocol serverProtocol, TlsServer server)
+            internal ServerTask(TlsServerProtocol serverProtocol, TlsServer server)
             {
                 this.m_serverProtocol = serverProtocol;
                 this.m_server = server;
diff --git a/crypto/test/src/tls/test/TlsProtocolTest.cs b/crypto/test/src/tls/test/TlsProtocolTest.cs
index c0287d913..aa3347fdb 100644
--- a/crypto/test/src/tls/test/TlsProtocolTest.cs
+++ b/crypto/test/src/tls/test/TlsProtocolTest.cs
@@ -24,8 +24,8 @@ namespace Org.BouncyCastle.Tls.Tests
             MockTlsClient client = new MockTlsClient(null);
             MockTlsServer server = new MockTlsServer();
 
-            Server serverRun = new Server(serverProtocol, server);
-            Thread serverThread = new Thread(new ThreadStart(serverRun.Run));
+            ServerTask serverTask = new ServerTask(serverProtocol, server);
+            Thread serverThread = new Thread(new ThreadStart(serverTask.Run));
             serverThread.Start();
 
             clientProtocol.Connect(client);
@@ -47,12 +47,12 @@ namespace Org.BouncyCastle.Tls.Tests
             serverThread.Join();
         }
 
-        internal class Server
+        internal class ServerTask
         {
             private readonly TlsServerProtocol m_serverProtocol;
             private readonly TlsServer m_server;
 
-            internal Server(TlsServerProtocol serverProtocol, TlsServer server)
+            internal ServerTask(TlsServerProtocol serverProtocol, TlsServer server)
             {
                 this.m_serverProtocol = serverProtocol;
                 this.m_server = server;
diff --git a/crypto/test/src/tls/test/TlsPskProtocolTest.cs b/crypto/test/src/tls/test/TlsPskProtocolTest.cs
index 449f9ea16..a9ee95f3d 100644
--- a/crypto/test/src/tls/test/TlsPskProtocolTest.cs
+++ b/crypto/test/src/tls/test/TlsPskProtocolTest.cs
@@ -24,8 +24,8 @@ namespace Org.BouncyCastle.Tls.Tests
             MockPskTlsClient client = new MockPskTlsClient(null);
             MockPskTlsServer server = new MockPskTlsServer();
 
-            Server serverRun = new Server(serverProtocol, server);
-            Thread serverThread = new Thread(new ThreadStart(serverRun.Run));
+            ServerTask serverTask = new ServerTask(serverProtocol, server);
+            Thread serverThread = new Thread(new ThreadStart(serverTask.Run));
             serverThread.Start();
 
             clientProtocol.Connect(client);
@@ -47,12 +47,12 @@ namespace Org.BouncyCastle.Tls.Tests
             serverThread.Join();
         }
 
-        internal class Server
+        internal class ServerTask
         {
             private readonly TlsServerProtocol m_serverProtocol;
             private readonly TlsServer m_server;
 
-            internal Server(TlsServerProtocol serverProtocol, TlsServer server)
+            internal ServerTask(TlsServerProtocol serverProtocol, TlsServer server)
             {
                 this.m_serverProtocol = serverProtocol;
                 this.m_server = server;
diff --git a/crypto/test/src/tls/test/TlsRawKeysProtocolTest.cs b/crypto/test/src/tls/test/TlsRawKeysProtocolTest.cs
index 976df4009..7c13cb27e 100644
--- a/crypto/test/src/tls/test/TlsRawKeysProtocolTest.cs
+++ b/crypto/test/src/tls/test/TlsRawKeysProtocolTest.cs
@@ -228,8 +228,8 @@ namespace Org.BouncyCastle.Tls.Tests
             TlsClientProtocol clientProtocol = new TlsClientProtocol(clientPipe);
             TlsServerProtocol serverProtocol = new TlsServerProtocol(serverPipe);
 
-            Server serverRun = new Server(serverProtocol, server);
-            Thread serverThread = new Thread(new ThreadStart(serverRun.Run));
+            ServerTask serverTask = new ServerTask(serverProtocol, server);
+            Thread serverThread = new Thread(new ThreadStart(serverTask.Run));
             serverThread.Start();
 
             clientProtocol.Connect(client);
@@ -254,12 +254,12 @@ namespace Org.BouncyCastle.Tls.Tests
             serverThread.Join();
         }
 
-        internal class Server
+        internal class ServerTask
         {
             private readonly TlsServerProtocol m_serverProtocol;
             private readonly TlsServer m_server;
 
-            internal Server(TlsServerProtocol serverProtocol, TlsServer server)
+            internal ServerTask(TlsServerProtocol serverProtocol, TlsServer server)
             {
                 m_serverProtocol = serverProtocol;
                 m_server = server;
diff --git a/crypto/test/src/tls/test/TlsServerRawKeysTest.cs b/crypto/test/src/tls/test/TlsServerRawKeysTest.cs
index 22e35505c..70b6a24c1 100644
--- a/crypto/test/src/tls/test/TlsServerRawKeysTest.cs
+++ b/crypto/test/src/tls/test/TlsServerRawKeysTest.cs
@@ -37,8 +37,8 @@ namespace Org.BouncyCastle.Tls.Tests
                     TcpClient s = ss.AcceptTcpClient();
                     Console.WriteLine("--------------------------------------------------------------------------------");
                     Console.WriteLine("Accepted " + s);
-                    Server serverRun = new Server(s, stdout, tlsVersion);
-                    Thread t = new Thread(new ThreadStart(serverRun.Run));
+                    ServerTask serverTask = new ServerTask(s, stdout, tlsVersion);
+                    Thread t = new Thread(new ThreadStart(serverTask.Run));
                     t.Start();
                 }
             }
@@ -48,13 +48,13 @@ namespace Org.BouncyCastle.Tls.Tests
             }
         }
 
-        internal class Server
+        internal class ServerTask
         {
             private readonly TcpClient s;
             private readonly Stream stdout;
             private readonly ProtocolVersion tlsVersion;
 
-            internal Server(TcpClient s, Stream stdout, ProtocolVersion tlsVersion)
+            internal ServerTask(TcpClient s, Stream stdout, ProtocolVersion tlsVersion)
             {
                 this.s = s;
                 this.stdout = stdout;
diff --git a/crypto/test/src/tls/test/TlsServerTest.cs b/crypto/test/src/tls/test/TlsServerTest.cs
index 2f2285942..0a19373ec 100644
--- a/crypto/test/src/tls/test/TlsServerTest.cs
+++ b/crypto/test/src/tls/test/TlsServerTest.cs
@@ -33,8 +33,8 @@ namespace Org.BouncyCastle.Tls.Tests
                     TcpClient s = ss.AcceptTcpClient();
                     Console.WriteLine("--------------------------------------------------------------------------------");
                     Console.WriteLine("Accepted " + s);
-                    Server serverRun = new Server(s, stdout);
-                    Thread t = new Thread(new ThreadStart(serverRun.Run));
+                    ServerTask serverTask = new ServerTask(s, stdout);
+                    Thread t = new Thread(new ThreadStart(serverTask.Run));
                     t.Start();
                 }
             }
@@ -44,12 +44,12 @@ namespace Org.BouncyCastle.Tls.Tests
             }
         }
 
-        internal class Server
+        internal class ServerTask
         {
             private readonly TcpClient s;
             private readonly Stream stdout;
 
-            internal Server(TcpClient s, Stream stdout)
+            internal ServerTask(TcpClient s, Stream stdout)
             {
                 this.s = s;
                 this.stdout = stdout;
diff --git a/crypto/test/src/tls/test/TlsSrpProtocolTest.cs b/crypto/test/src/tls/test/TlsSrpProtocolTest.cs
index f85a67dd5..60f0bc9d5 100644
--- a/crypto/test/src/tls/test/TlsSrpProtocolTest.cs
+++ b/crypto/test/src/tls/test/TlsSrpProtocolTest.cs
@@ -24,8 +24,8 @@ namespace Org.BouncyCastle.Tls.Tests
             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));
+            ServerTask serverTask = new ServerTask(serverProtocol, server);
+            Thread serverThread = new Thread(new ThreadStart(serverTask.Run));
             serverThread.Start();
 
             clientProtocol.Connect(client);
@@ -47,12 +47,12 @@ namespace Org.BouncyCastle.Tls.Tests
             serverThread.Join();
         }
 
-        internal class Server
+        internal class ServerTask
         {
             private readonly TlsServerProtocol m_serverProtocol;
             private readonly TlsServer m_server;
 
-            internal Server(TlsServerProtocol serverProtocol, TlsServer server)
+            internal ServerTask(TlsServerProtocol serverProtocol, TlsServer server)
             {
                 this.m_serverProtocol = serverProtocol;
                 this.m_server = server;
diff --git a/crypto/test/src/tls/test/TlsTestCase.cs b/crypto/test/src/tls/test/TlsTestCase.cs
index cb136db92..ad2dfee0c 100644
--- a/crypto/test/src/tls/test/TlsTestCase.cs
+++ b/crypto/test/src/tls/test/TlsTestCase.cs
@@ -49,8 +49,8 @@ namespace Org.BouncyCastle.Tls.Tests
             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));
+            ServerTask serverTask = new ServerTask(this, serverProtocol, serverImpl);
+            Thread serverThread = new Thread(new ThreadStart(serverTask.Run));
             serverThread.Start();
 
             Exception caught = null;
@@ -89,7 +89,7 @@ namespace Org.BouncyCastle.Tls.Tests
                 LogException(caught);
             }
 
-            serverRun.AllowExit();
+            serverTask.AllowExit();
             serverThread.Join();
 
             Assert.IsTrue(clientNet.IsClosed, "Client Stream not closed");
@@ -108,7 +108,7 @@ namespace Org.BouncyCastle.Tls.Tests
             if (config.expectFatalAlertConnectionEnd == -1)
             {
                 Assert.IsNull(caught, "Unexpected client exception");
-                Assert.IsNull(serverRun.m_caught, "Unexpected server exception");
+                Assert.IsNull(serverTask.m_caught, "Unexpected server exception");
             }
         }
 
@@ -121,7 +121,7 @@ namespace Org.BouncyCastle.Tls.Tests
             }
         }
 
-        internal class Server
+        internal class ServerTask
         {
             protected readonly TlsTestCase m_outer;
             protected readonly TlsServerProtocol m_serverProtocol;
@@ -130,7 +130,7 @@ namespace Org.BouncyCastle.Tls.Tests
             internal bool m_canExit = false;
             internal Exception m_caught = null;
 
-            internal Server(TlsTestCase outer, TlsTestServerProtocol serverProtocol, TlsServer server)
+            internal ServerTask(TlsTestCase outer, TlsTestServerProtocol serverProtocol, TlsServer server)
             {
                 this.m_outer = outer;
                 this.m_serverProtocol = serverProtocol;
diff --git a/crypto/test/src/tls/test/UnreliableDatagramTransport.cs b/crypto/test/src/tls/test/UnreliableDatagramTransport.cs
index 7769db9d1..0aed2d7a1 100644
--- a/crypto/test/src/tls/test/UnreliableDatagramTransport.cs
+++ b/crypto/test/src/tls/test/UnreliableDatagramTransport.cs
@@ -25,15 +25,9 @@ namespace Org.BouncyCastle.Tls.Tests
             this.m_percentPacketLossSending = percentPacketLossSending;
         }
 
-        public virtual int GetReceiveLimit()
-        {
-            return m_transport.GetReceiveLimit();
-        }
+        public virtual int GetReceiveLimit() => m_transport.GetReceiveLimit();
 
-        public virtual int GetSendLimit()
-        {
-            return m_transport.GetSendLimit();
-        }
+        public virtual int GetSendLimit() => m_transport.GetSendLimit();
 
         public virtual int Receive(byte[] buf, int off, int len, int waitMillis)
         {
@@ -48,7 +42,7 @@ namespace Org.BouncyCastle.Tls.Tests
                 if (length < 0 || !LostPacket(m_percentPacketLossReceiving))
                     return length;
 
-                Console.WriteLine("PACKET LOSS (" + length + " byte packet not received)");
+                Console.WriteLine("PACKET LOSS ({0} byte packet not received)", length);
 
                 long now = DateTimeUtilities.CurrentUnixMs();
                 if (now >= endMillis)
@@ -70,7 +64,7 @@ namespace Org.BouncyCastle.Tls.Tests
                 if (length < 0 || !LostPacket(m_percentPacketLossReceiving))
                     return length;
 
-                Console.WriteLine("PACKET LOSS (" + length + " byte packet not received)");
+                Console.WriteLine("PACKET LOSS ({0} byte packet not received)", length);
 
                 long now = DateTimeUtilities.CurrentUnixMs();
                 if (now >= endMillis)
@@ -89,7 +83,7 @@ namespace Org.BouncyCastle.Tls.Tests
 #else
             if (LostPacket(m_percentPacketLossSending))
             {
-                Console.WriteLine("PACKET LOSS (" + len + " byte packet not sent)");
+                Console.WriteLine("PACKET LOSS ({0} byte packet not sent)", len);
             }
             else
             {
@@ -104,7 +98,7 @@ namespace Org.BouncyCastle.Tls.Tests
         {
             if (LostPacket(m_percentPacketLossSending))
             {
-                Console.WriteLine("PACKET LOSS (" + buffer.Length + " byte packet not sent)");
+                Console.WriteLine("PACKET LOSS ({0} byte packet not sent)", buffer.Length);
             }
             else
             {
@@ -113,10 +107,7 @@ namespace Org.BouncyCastle.Tls.Tests
         }
 #endif
 
-        public virtual void Close()
-        {
-            m_transport.Close();
-        }
+        public virtual void Close() => m_transport.Close();
 
         private bool LostPacket(int percentPacketLoss)
         {