summary refs log tree commit diff
diff options
context:
space:
mode:
authorPeter Dettman <peter.dettman@bouncycastle.org>2015-03-09 23:25:20 +0700
committerPeter Dettman <peter.dettman@bouncycastle.org>2015-03-09 23:25:20 +0700
commit652d1db675ca9bd3b41914649dd4e61f32c238e6 (patch)
treeb165d8e03776a30460563703d263604dfb108fa1
parentPort of TlsTestSuite from Java, and misc. TLS code (diff)
downloadBouncyCastle.NET-ed25519-652d1db675ca9bd3b41914649dd4e61f32c238e6.tar.xz
Port of DTLS tests from Java
-rw-r--r--crypto/crypto.csproj30
-rw-r--r--crypto/src/crypto/tls/DtlsReliableHandshake.cs8
-rw-r--r--crypto/test/src/crypto/tls/test/DtlsProtocolTest.cs102
-rw-r--r--crypto/test/src/crypto/tls/test/DtlsTestCase.cs153
-rw-r--r--crypto/test/src/crypto/tls/test/DtlsTestSuite.cs134
-rw-r--r--crypto/test/src/crypto/tls/test/MockDatagramAssociation.cs18
-rw-r--r--crypto/test/src/crypto/tls/test/MockDtlsClient.cs151
-rw-r--r--crypto/test/src/crypto/tls/test/MockDtlsServer.cs100
-rw-r--r--crypto/test/src/crypto/tls/test/MockPskTlsClient.cs2
-rw-r--r--crypto/test/src/crypto/tls/test/MockSrpTlsClient.cs2
-rw-r--r--crypto/test/src/crypto/tls/test/MockTlsClient.cs2
-rw-r--r--crypto/test/src/crypto/tls/test/PskTlsClientTest.cs79
-rw-r--r--crypto/test/src/crypto/tls/test/TlsTestCase.cs8
13 files changed, 775 insertions, 14 deletions
diff --git a/crypto/crypto.csproj b/crypto/crypto.csproj
index 9d185ade3..35474aebb 100644
--- a/crypto/crypto.csproj
+++ b/crypto/crypto.csproj
@@ -11088,6 +11088,21 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "test\src\crypto\tls\test\DtlsProtocolTest.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "test\src\crypto\tls\test\DtlsTestCase.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "test\src\crypto\tls\test\DtlsTestSuite.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "test\src\crypto\tls\test\LoggingDatagramTransport.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -11098,6 +11113,16 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "test\src\crypto\tls\test\MockDtlsClient.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "test\src\crypto\tls\test\MockDtlsServer.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "test\src\crypto\tls\test\MockPskTlsClient.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -11138,6 +11163,11 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "test\src\crypto\tls\test\PskTlsClientTest.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "test\src\crypto\tls\test\TlsClientTest.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
diff --git a/crypto/src/crypto/tls/DtlsReliableHandshake.cs b/crypto/src/crypto/tls/DtlsReliableHandshake.cs
index bf9e61d03..8e4439e67 100644
--- a/crypto/src/crypto/tls/DtlsReliableHandshake.cs
+++ b/crypto/src/crypto/tls/DtlsReliableHandshake.cs
@@ -114,17 +114,17 @@ namespace Org.BouncyCastle.Crypto.Tls
                 {
                     for (; ; )
                     {
-                        int Received = mRecordLayer.Receive(buf, 0, receiveLimit, readTimeoutMillis);
-                        if (Received < 0)
+                        int received = mRecordLayer.Receive(buf, 0, receiveLimit, readTimeoutMillis);
+                        if (received < 0)
                         {
                             break;
                         }
-                        if (Received < 12)
+                        if (received < 12)
                         {
                             continue;
                         }
                         int fragment_length = TlsUtilities.ReadUint24(buf, 9);
-                        if (Received != (fragment_length + 12))
+                        if (received != (fragment_length + 12))
                         {
                             continue;
                         }
diff --git a/crypto/test/src/crypto/tls/test/DtlsProtocolTest.cs b/crypto/test/src/crypto/tls/test/DtlsProtocolTest.cs
new file mode 100644
index 000000000..bc99ccc63
--- /dev/null
+++ b/crypto/test/src/crypto/tls/test/DtlsProtocolTest.cs
@@ -0,0 +1,102 @@
+using System;
+using System.IO;
+using System.Threading;
+
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+
+using NUnit.Framework;
+
+namespace Org.BouncyCastle.Crypto.Tls.Tests
+{
+    [TestFixture]
+    public class DtlsProtocolTest
+    {
+        [Test]
+        public void TestClientServer()
+        {
+            SecureRandom secureRandom = new SecureRandom();
+
+            DtlsClientProtocol clientProtocol = new DtlsClientProtocol(secureRandom);
+            DtlsServerProtocol serverProtocol = new DtlsServerProtocol(secureRandom);
+
+            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 mServerProtocol;
+            private readonly DatagramTransport mServerTransport;
+            private volatile bool isShutdown = false;
+
+            internal Server(DtlsServerProtocol serverProtocol, DatagramTransport serverTransport)
+            {
+                this.mServerProtocol = serverProtocol;
+                this.mServerTransport = serverTransport;
+            }
+
+            public void Run()
+            {
+                try
+                {
+                    MockDtlsServer server = new MockDtlsServer();
+                    DtlsTransport dtlsServer = mServerProtocol.Accept(server, mServerTransport);
+                    byte[] buf = new byte[dtlsServer.GetReceiveLimit()];
+                    while (!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.StackTrace);
+                }
+            }
+
+            internal void Shutdown(Thread serverThread)
+            {
+                if (!isShutdown)
+                {
+                    isShutdown = true;
+                    serverThread.Join();
+                }
+            }
+        }
+    }
+}
diff --git a/crypto/test/src/crypto/tls/test/DtlsTestCase.cs b/crypto/test/src/crypto/tls/test/DtlsTestCase.cs
new file mode 100644
index 000000000..d4af04fac
--- /dev/null
+++ b/crypto/test/src/crypto/tls/test/DtlsTestCase.cs
@@ -0,0 +1,153 @@
+using System;
+using System.IO;
+using System.Threading;
+
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+
+using NUnit.Framework;
+
+namespace Org.BouncyCastle.Crypto.Tls.Tests
+{
+    [TestFixture]
+    public class DtlsTestCase
+    {
+        private static void CheckDtlsVersion(ProtocolVersion version)
+        {
+            if (version != null && !version.IsDtls)
+                throw new InvalidOperationException("Non-DTLS version");
+        }
+
+        [Test, TestCaseSource(typeof(DtlsTestSuite), "Suite")]
+        public void RunTest(TlsTestConfig config)
+        {
+            CheckDtlsVersion(config.clientMinimumVersion);
+            CheckDtlsVersion(config.clientOfferVersion);
+            CheckDtlsVersion(config.serverMaximumVersion);
+            CheckDtlsVersion(config.serverMinimumVersion);
+
+            SecureRandom secureRandom = new SecureRandom();
+
+            DtlsClientProtocol clientProtocol = new DtlsClientProtocol(secureRandom);
+            DtlsServerProtocol serverProtocol = new DtlsServerProtocol(secureRandom);
+
+            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.mCaught, "Unexpected server exception");
+            }
+        }
+
+        protected void LogException(Exception e)
+        {
+            if (TlsTestConfig.DEBUG)
+            {
+                Console.Error.WriteLine(e.StackTrace);
+            }
+        }
+
+        internal class Server
+        {
+            private readonly DtlsTestCase mOuter;
+            private readonly DtlsServerProtocol mServerProtocol;
+            private readonly DatagramTransport mServerTransport;
+            private readonly TlsTestServerImpl mServerImpl;
+
+            private volatile bool isShutdown = false;
+            internal Exception mCaught = null;
+
+            internal Server(DtlsTestCase outer, DtlsServerProtocol serverProtocol, DatagramTransport serverTransport, TlsTestServerImpl serverImpl)
+            {
+                this.mOuter = outer;
+                this.mServerProtocol = serverProtocol;
+                this.mServerTransport = serverTransport;
+                this.mServerImpl = serverImpl;
+            }
+
+            public void Run()
+            {
+                try
+                {
+                    DtlsTransport dtlsServer = mServerProtocol.Accept(mServerImpl, mServerTransport);
+                    byte[] buf = new byte[dtlsServer.GetReceiveLimit()];
+                    while (!isShutdown)
+                    {
+                        int length = dtlsServer.Receive(buf, 0, buf.Length, 100);
+                        if (length >= 0)
+                        {
+                            dtlsServer.Send(buf, 0, length);
+                        }
+                    }
+                    dtlsServer.Close();
+                }
+                catch (Exception e)
+                {
+                    mCaught = e;
+                    mOuter.LogException(mCaught);
+                }
+            }
+
+            internal void Shutdown(Thread serverThread)
+            {
+                if (!isShutdown)
+                {
+                    isShutdown = true;
+                    serverThread.Interrupt();
+                    serverThread.Join();
+                }
+            }
+        }
+    }
+}
diff --git a/crypto/test/src/crypto/tls/test/DtlsTestSuite.cs b/crypto/test/src/crypto/tls/test/DtlsTestSuite.cs
new file mode 100644
index 000000000..eb9d42e5f
--- /dev/null
+++ b/crypto/test/src/crypto/tls/test/DtlsTestSuite.cs
@@ -0,0 +1,134 @@
+using System;
+using System.Collections;
+
+using NUnit.Framework;
+
+namespace Org.BouncyCastle.Crypto.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;
+
+                testSuite.Add(new TestCaseData(c).SetName("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
+             */
+
+            //{
+            //    TlsTestConfig c = CreateDtlsTestConfig(ProtocolVersion.DTLSv12);
+            //    c.clientOfferVersion = ProtocolVersion.DTLSv10;
+            //    c.clientFallback = true;
+            //    c.ExpectServerFatalAlert(AlertDescription.inappropriate_fallback);
+
+            //    testSuite.Add(new TestCaseData(c).SetName("FallbackBad"));
+            //}
+
+            {
+                TlsTestConfig c = CreateDtlsTestConfig(ProtocolVersion.DTLSv12);
+                c.clientOfferVersion = ProtocolVersion.DTLSv10;
+
+                testSuite.Add(new TestCaseData(c).SetName("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
+             */
+
+            //{
+            //    TlsTestConfig c = CreateDtlsTestConfig(version);
+            //    c.clientAuth = C.CLIENT_AUTH_INVALID_VERIFY;
+            //    c.ExpectServerFatalAlert(AlertDescription.decrypt_error);
+
+            //    testSuite.Add(new TestCaseData(c).SetName(prefix + "BadCertificateVerify"));
+            //}
+
+            //{
+            //    TlsTestConfig c = CreateDtlsTestConfig(version);
+            //    c.clientAuth = C.CLIENT_AUTH_INVALID_CERT;
+            //    c.ExpectServerFatalAlert(AlertDescription.bad_certificate);
+
+            //    testSuite.Add(new TestCaseData(c).SetName(prefix + "BadClientCertificate"));
+            //}
+
+            //{
+            //    TlsTestConfig c = CreateDtlsTestConfig(version);
+            //    c.clientAuth = C.CLIENT_AUTH_NONE;
+            //    c.serverCertReq = C.SERVER_CERT_REQ_MANDATORY;
+            //    c.ExpectServerFatalAlert(AlertDescription.handshake_failure);
+
+            //    testSuite.Add(new TestCaseData(c).SetName(prefix + "BadMandatoryCertReqDeclined"));
+            //}
+
+            {
+                TlsTestConfig c = CreateDtlsTestConfig(version);
+
+                testSuite.Add(new TestCaseData(c).SetName(prefix + "GoodDefault"));
+            }
+
+            {
+                TlsTestConfig c = CreateDtlsTestConfig(version);
+                c.serverCertReq = C.SERVER_CERT_REQ_NONE;
+
+                testSuite.Add(new TestCaseData(c).SetName(prefix + "GoodNoCertReq"));
+            }
+
+            {
+                TlsTestConfig c = CreateDtlsTestConfig(version);
+                c.clientAuth = C.CLIENT_AUTH_NONE;
+
+                testSuite.Add(new TestCaseData(c).SetName(prefix + "GoodOptionalCertReqDeclined"));
+            }
+        }
+
+        private static TlsTestConfig CreateDtlsTestConfig(ProtocolVersion version)
+        {
+            TlsTestConfig c = new TlsTestConfig();
+            c.clientMinimumVersion = ProtocolVersion.DTLSv10;
+            /*
+             * TODO We'd like to just set the offer version to DTLSv12, but there is a known issue with
+             * overly-restrictive version checks b/w BC DTLS 1.2 client, BC DTLS 1.0 server
+             */
+            c.clientOfferVersion = version;
+            c.serverMaximumVersion = version;
+            c.serverMinimumVersion = ProtocolVersion.DTLSv10;
+            return c;
+        }
+    }
+}
diff --git a/crypto/test/src/crypto/tls/test/MockDatagramAssociation.cs b/crypto/test/src/crypto/tls/test/MockDatagramAssociation.cs
index 0391f4fef..48df36ca9 100644
--- a/crypto/test/src/crypto/tls/test/MockDatagramAssociation.cs
+++ b/crypto/test/src/crypto/tls/test/MockDatagramAssociation.cs
@@ -24,14 +24,14 @@ namespace Org.BouncyCastle.Crypto.Tls.Tests
             this.server = new MockDatagramTransport(this, serverQueue, clientQueue);
         }
 
-        public DatagramTransport getClient()
+        public virtual DatagramTransport Client
         {
-            return client;
+            get { return client; }
         }
 
-        public DatagramTransport getServer()
+        public virtual DatagramTransport Server
         {
-            return server;
+            get { return server; }
         }
 
         private class MockDatagramTransport
@@ -64,8 +64,14 @@ namespace Org.BouncyCastle.Crypto.Tls.Tests
                 {
                     if (receiveQueue.Count < 1)
                     {
-                        Monitor.Wait(waitMillis);
-
+                        try
+                        {
+                            Monitor.Wait(receiveQueue, waitMillis);
+                        }
+                        catch (ThreadInterruptedException)
+                        {
+                            // TODO Keep waiting until full wait expired?
+                        }
                         if (receiveQueue.Count < 1)
                         {
                             return -1;
diff --git a/crypto/test/src/crypto/tls/test/MockDtlsClient.cs b/crypto/test/src/crypto/tls/test/MockDtlsClient.cs
new file mode 100644
index 000000000..32491c425
--- /dev/null
+++ b/crypto/test/src/crypto/tls/test/MockDtlsClient.cs
@@ -0,0 +1,151 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Encoders;
+
+namespace Org.BouncyCastle.Crypto.Tls.Tests
+{
+    public class MockDtlsClient
+        :   DefaultTlsClient
+    {
+        protected TlsSession mSession;
+
+        public MockDtlsClient(TlsSession session)
+        {
+            this.mSession = session;
+        }
+
+        public override TlsSession GetSessionToResume()
+        {
+            return this.mSession;
+        }
+
+        public override void NotifyAlertRaised(byte alertLevel, byte 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(byte alertLevel, byte alertDescription)
+        {
+            TextWriter output = (alertLevel == AlertLevel.fatal) ? Console.Error : Console.Out;
+            output.WriteLine("DTLS client received alert: " + AlertLevel.GetText(alertLevel)
+                + ", " + AlertDescription.GetText(alertDescription));
+        }
+
+        public override ProtocolVersion ClientVersion
+        {
+            get { return ProtocolVersion.DTLSv12; }
+        }
+
+        public override ProtocolVersion MinimumVersion
+        {
+            get { return ProtocolVersion.DTLSv10; }
+        }
+
+        //public override int[] GetCipherSuites()
+        //{
+        //    return Arrays.Concatenate(base.GetCipherSuites(),
+        //        new int[]
+        //        {
+        //            CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
+        //            CipherSuite.TLS_ECDHE_RSA_WITH_ESTREAM_SALSA20_SHA1,
+        //            CipherSuite.TLS_ECDHE_RSA_WITH_SALSA20_SHA1,
+        //            CipherSuite.TLS_RSA_WITH_ESTREAM_SALSA20_SHA1,
+        //            CipherSuite.TLS_RSA_WITH_SALSA20_SHA1,
+        //        });
+        //}
+
+        public override IDictionary GetClientExtensions()
+        {
+            IDictionary clientExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(base.GetClientExtensions());
+            TlsExtensionsUtilities.AddEncryptThenMacExtension(clientExtensions);
+            // TODO[draft-ietf-tls-session-hash-01] Enable once code-point assigned (only for compatible server though)
+            //TlsExtensionsUtilities.AddExtendedMasterSecretExtension(clientExtensions);
+            TlsExtensionsUtilities.AddMaxFragmentLengthExtension(clientExtensions, MaxFragmentLength.pow2_9);
+            TlsExtensionsUtilities.AddTruncatedHMacExtension(clientExtensions);
+            return clientExtensions;
+        }
+
+        public override void NotifyServerVersion(ProtocolVersion serverVersion)
+        {
+            base.NotifyServerVersion(serverVersion);
+
+            Console.WriteLine("Negotiated " + serverVersion);
+        }
+
+        public override TlsAuthentication GetAuthentication()
+        {
+            return new MyTlsAuthentication(mContext);
+        }
+
+        public override void NotifyHandshakeComplete()
+        {
+            base.NotifyHandshakeComplete();
+
+            TlsSession newSession = mContext.ResumableSession;
+            if (newSession != null)
+            {
+                byte[] newSessionID = newSession.SessionID;
+                string hex = Hex.ToHexString(newSessionID);
+
+                if (this.mSession != null && Arrays.AreEqual(this.mSession.SessionID, newSessionID))
+                {
+                    Console.WriteLine("Resumed session: " + hex);
+                }
+                else
+                {
+                    Console.WriteLine("Established session: " + hex);
+                }
+
+                this.mSession = newSession;
+            }
+        }
+
+        internal class MyTlsAuthentication
+            :   TlsAuthentication
+        {
+            private readonly TlsContext mContext;
+
+            internal MyTlsAuthentication(TlsContext context)
+            {
+                this.mContext = context;
+            }
+
+            public virtual void NotifyServerCertificate(Certificate serverCertificate)
+            {
+                X509CertificateStructure[] chain = serverCertificate.GetCertificateList();
+                Console.WriteLine("DTLS client received server certificate chain of length " + chain.Length);
+                for (int i = 0; i != chain.Length; i++)
+                {
+                    X509CertificateStructure entry = chain[i];
+                    // TODO Create fingerprint based on certificate signature algorithm digest
+                    Console.WriteLine("    fingerprint:SHA-256 " + TlsTestUtilities.Fingerprint(entry) + " ("
+                        + entry.Subject + ")");
+                }
+            }
+
+            public virtual TlsCredentials GetClientCredentials(CertificateRequest certificateRequest)
+            {
+                byte[] certificateTypes = certificateRequest.CertificateTypes;
+                if (certificateTypes == null || !Arrays.Contains(certificateTypes, ClientCertificateType.rsa_sign))
+                    return null;
+
+                return TlsTestUtilities.LoadSignerCredentials(mContext, certificateRequest.SupportedSignatureAlgorithms,
+                    SignatureAlgorithm.rsa, "x509-client.pem", "x509-client-key.pem");
+            }
+        };
+    }
+}
diff --git a/crypto/test/src/crypto/tls/test/MockDtlsServer.cs b/crypto/test/src/crypto/tls/test/MockDtlsServer.cs
new file mode 100644
index 000000000..19062181b
--- /dev/null
+++ b/crypto/test/src/crypto/tls/test/MockDtlsServer.cs
@@ -0,0 +1,100 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Tls.Tests
+{
+    public class MockDtlsServer
+        :   DefaultTlsServer
+    {
+        public override void NotifyAlertRaised(byte alertLevel, byte 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(byte alertLevel, byte alertDescription)
+        {
+            TextWriter output = (alertLevel == AlertLevel.fatal) ? Console.Error : Console.Out;
+            output.WriteLine("DTLS server received alert: " + AlertLevel.GetText(alertLevel)
+                + ", " + AlertDescription.GetText(alertDescription));
+        }
+
+        protected override int[] GetCipherSuites()
+        {
+            return Arrays.Concatenate(base.GetCipherSuites(),
+                new int[]
+                {
+                    CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
+                    CipherSuite.TLS_ECDHE_RSA_WITH_ESTREAM_SALSA20_SHA1,
+                    CipherSuite.TLS_ECDHE_RSA_WITH_SALSA20_SHA1,
+                    CipherSuite.TLS_RSA_WITH_ESTREAM_SALSA20_SHA1,
+                    CipherSuite.TLS_RSA_WITH_SALSA20_SHA1,
+                });
+        }
+
+        public override CertificateRequest GetCertificateRequest()
+        {
+            byte[] certificateTypes = new byte[]{ ClientCertificateType.rsa_sign,
+                ClientCertificateType.dss_sign, ClientCertificateType.ecdsa_sign };
+
+            IList serverSigAlgs = null;
+            if (TlsUtilities.IsSignatureAlgorithmsExtensionAllowed(mServerVersion))
+            {
+                serverSigAlgs = TlsUtilities.GetDefaultSupportedSignatureAlgorithms();
+            }
+
+            IList certificateAuthorities = new ArrayList();
+            certificateAuthorities.Add(TlsTestUtilities.LoadCertificateResource("x509-ca.pem").Subject);
+
+            return new CertificateRequest(certificateTypes, serverSigAlgs, certificateAuthorities);
+        }
+
+        public override void NotifyClientCertificate(Certificate clientCertificate)
+        {
+            X509CertificateStructure[] 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 = chain[i];
+                // TODO Create fingerprint based on certificate signature algorithm digest
+                Console.WriteLine("    fingerprint:SHA-256 " + TlsTestUtilities.Fingerprint(entry) + " ("
+                    + entry.Subject + ")");
+            }
+        }
+
+        protected override ProtocolVersion MaximumVersion
+        {
+            get { return ProtocolVersion.DTLSv12; }
+        }
+
+        protected override ProtocolVersion MinimumVersion
+        {
+            get { return ProtocolVersion.DTLSv10; }
+        }
+
+        protected override TlsEncryptionCredentials GetRsaEncryptionCredentials()
+        {
+            return TlsTestUtilities.LoadEncryptionCredentials(mContext, new string[] { "x509-server.pem", "x509-ca.pem" },
+                "x509-server-key.pem");
+        }
+
+        protected override TlsSignerCredentials GetRsaSignerCredentials()
+        {
+            return TlsTestUtilities.LoadSignerCredentials(mContext, mSupportedSignatureAlgorithms, SignatureAlgorithm.rsa,
+                "x509-server.pem", "x509-server-key.pem");
+        }
+    }
+}
diff --git a/crypto/test/src/crypto/tls/test/MockPskTlsClient.cs b/crypto/test/src/crypto/tls/test/MockPskTlsClient.cs
index a186a8b58..dfc0e93a0 100644
--- a/crypto/test/src/crypto/tls/test/MockPskTlsClient.cs
+++ b/crypto/test/src/crypto/tls/test/MockPskTlsClient.cs
@@ -122,7 +122,7 @@ namespace Org.BouncyCastle.Crypto.Tls.Tests
                 for (int i = 0; i != chain.Length; i++)
                 {
                     X509CertificateStructure entry = chain[i];
-                    // TODO Create Fingerprint based on certificate signature algorithm digest
+                    // TODO Create fingerprint based on certificate signature algorithm digest
                     Console.WriteLine("    fingerprint:SHA-256 " + TlsTestUtilities.Fingerprint(entry) + " ("
                         + entry.Subject + ")");
                 }
diff --git a/crypto/test/src/crypto/tls/test/MockSrpTlsClient.cs b/crypto/test/src/crypto/tls/test/MockSrpTlsClient.cs
index 2b9549e82..8a6b9f496 100644
--- a/crypto/test/src/crypto/tls/test/MockSrpTlsClient.cs
+++ b/crypto/test/src/crypto/tls/test/MockSrpTlsClient.cs
@@ -110,7 +110,7 @@ namespace Org.BouncyCastle.Crypto.Tls.Tests
                 for (int i = 0; i != chain.Length; i++)
                 {
                     X509CertificateStructure entry = chain[i];
-                    // TODO Create Fingerprint based on certificate signature algorithm digest
+                    // TODO Create fingerprint based on certificate signature algorithm digest
                     Console.WriteLine("    fingerprint:SHA-256 " + TlsTestUtilities.Fingerprint(entry) + " ("
                         + entry.Subject + ")");
                 }
diff --git a/crypto/test/src/crypto/tls/test/MockTlsClient.cs b/crypto/test/src/crypto/tls/test/MockTlsClient.cs
index dbeb3fe41..4d7c59afa 100644
--- a/crypto/test/src/crypto/tls/test/MockTlsClient.cs
+++ b/crypto/test/src/crypto/tls/test/MockTlsClient.cs
@@ -121,7 +121,7 @@ namespace Org.BouncyCastle.Crypto.Tls.Tests
                 for (int i = 0; i != chain.Length; i++)
                 {
                     X509CertificateStructure entry = chain[i];
-                    // TODO Create Fingerprint based on certificate signature algorithm digest
+                    // TODO Create fingerprint based on certificate signature algorithm digest
                     Console.WriteLine("    fingerprint:SHA-256 " + TlsTestUtilities.Fingerprint(entry) + " ("
                         + entry.Subject + ")");
                 }
diff --git a/crypto/test/src/crypto/tls/test/PskTlsClientTest.cs b/crypto/test/src/crypto/tls/test/PskTlsClientTest.cs
new file mode 100644
index 000000000..7072c7105
--- /dev/null
+++ b/crypto/test/src/crypto/tls/test/PskTlsClientTest.cs
@@ -0,0 +1,79 @@
+using System;
+using System.IO;
+using System.Net.Sockets;
+using System.Text;
+
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto.Tls.Tests
+{
+    /**
+     * A simple test designed to conduct a TLS handshake with an external TLS server.
+     * <p>
+     * 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.
+     * </p><p>
+     * In both cases, extra options are required to enable PSK ciphersuites and configure identities/keys.
+     * </p>
+     */
+    public class PskTlsClientTest
+    {
+        private static readonly SecureRandom secureRandom = new SecureRandom();
+
+        public static void Main(string[] args)
+        {
+            string hostname = "localhost";
+            int port = 5556;
+
+            long time1 = DateTime.UtcNow.Ticks;
+
+            /*
+             * 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 };
+
+            BasicTlsPskIdentity pskIdentity = new BasicTlsPskIdentity(psk_identity, psk);
+
+            MockPskTlsClient client = new MockPskTlsClient(null, pskIdentity);
+            TlsClientProtocol protocol = OpenTlsConnection(hostname, port, client);
+            protocol.Close();
+
+            long time2 = DateTime.UtcNow.Ticks;
+            Console.WriteLine("Elapsed 1: " + (time2 - time1)/TimeSpan.TicksPerMillisecond + "ms");
+
+            client = new MockPskTlsClient(client.GetSessionToResume(), pskIdentity);
+            protocol = OpenTlsConnection(hostname, port, client);
+
+            long time3 = DateTime.UtcNow.Ticks;
+            Console.WriteLine("Elapsed 2: " + (time3 - time2)/TimeSpan.TicksPerMillisecond + "ms");
+
+            byte[] req = Encoding.UTF8.GetBytes("GET / HTTP/1.1\r\n\r\n");
+
+            Stream tlsStream = protocol.Stream;
+            tlsStream.Write(req, 0, req.Length);
+            tlsStream.Flush();
+
+            StreamReader reader = new StreamReader(tlsStream);
+
+            String line;
+            while ((line = reader.ReadLine()) != null)
+            {
+                Console.WriteLine(">>> " + line);
+            }
+
+            protocol.Close();
+        }
+
+        internal static TlsClientProtocol OpenTlsConnection(string hostname, int port, TlsClient client)
+        {
+            TcpClient tcp = new TcpClient(hostname, port);
+
+            TlsClientProtocol protocol = new TlsClientProtocol(tcp.GetStream(), secureRandom);
+            protocol.Connect(client);
+            return protocol;
+        }
+    }
+}
diff --git a/crypto/test/src/crypto/tls/test/TlsTestCase.cs b/crypto/test/src/crypto/tls/test/TlsTestCase.cs
index 8ac1c4ed2..4b0c12710 100644
--- a/crypto/test/src/crypto/tls/test/TlsTestCase.cs
+++ b/crypto/test/src/crypto/tls/test/TlsTestCase.cs
@@ -149,7 +149,13 @@ namespace Org.BouncyCastle.Crypto.Tls.Tests
                 {
                     while (!mCanExit)
                     {
-                        Monitor.Wait(this);
+                        try
+                        {
+                            Monitor.Wait(this);
+                        }
+                        catch (ThreadInterruptedException)
+                        {
+                        }
                     }
                 }
             }