summary refs log tree commit diff
diff options
context:
space:
mode:
authorJeffrey Stedfast <jeff@xamarin.com>2015-02-13 15:10:02 -0500
committerJeffrey Stedfast <jeff@xamarin.com>2015-02-13 15:10:02 -0500
commit00f427405a0d431052dfb4f00bfcfe48932d32e3 (patch)
treea860a827ec81be7f764c1223c9c04e5d5088fd16
parentMerge branch 'master' into vs2010 (diff)
parentPort of WNaf precomp optimization from Java (diff)
downloadBouncyCastle.NET-ed25519-00f427405a0d431052dfb4f00bfcfe48932d32e3.tar.xz
Merge branch 'master' into vs2010
-rw-r--r--crypto/License.html2
-rw-r--r--crypto/crypto.csproj15
-rw-r--r--crypto/src/AssemblyInfo.cs2
-rw-r--r--crypto/src/crypto/tls/BasicTlsPskIdentity.cs43
-rw-r--r--crypto/src/crypto/tls/DtlsClientProtocol.cs1
-rw-r--r--crypto/src/crypto/tls/PskTlsClient.cs17
-rw-r--r--crypto/src/crypto/tls/PskTlsServer.cs347
-rw-r--r--crypto/src/crypto/tls/SecurityParameters.cs6
-rw-r--r--crypto/src/crypto/tls/ServerDHParams.cs3
-rw-r--r--crypto/src/crypto/tls/SessionParameters.cs20
-rw-r--r--crypto/src/crypto/tls/TlsECDheKeyExchange.cs67
-rw-r--r--crypto/src/crypto/tls/TlsEccUtilities.cs63
-rw-r--r--crypto/src/crypto/tls/TlsPskIdentityManager.cs11
-rw-r--r--crypto/src/crypto/tls/TlsPskKeyExchange.cs97
-rw-r--r--crypto/src/math/ec/ECAlgorithms.cs14
-rw-r--r--crypto/src/math/ec/ECCurve.cs61
-rw-r--r--crypto/src/math/ec/multiplier/WNafUtilities.cs101
-rw-r--r--crypto/src/security/SignerUtilities.cs4
18 files changed, 749 insertions, 125 deletions
diff --git a/crypto/License.html b/crypto/License.html
index 1c5c7b0ec..cd92d1b0e 100644
--- a/crypto/License.html
+++ b/crypto/License.html
@@ -9,7 +9,7 @@
 <h2>The Bouncy Castle Cryptographic C#&reg; API</h2>
 <h3>License:</h3>
 The Bouncy Castle License<br>
-Copyright (c) 2000-2014 The Legion of the Bouncy Castle Inc.
+Copyright (c) 2000-2015 The Legion of the Bouncy Castle Inc.
 (http://www.bouncycastle.org)<br>
 Permission is hereby granted, free of charge, to any person obtaining a
 copy of this software and associated documentation files (the "Software"), to deal in the
diff --git a/crypto/crypto.csproj b/crypto/crypto.csproj
index 74aac8b6e..fdd5c152b 100644
--- a/crypto/crypto.csproj
+++ b/crypto/crypto.csproj
@@ -4344,6 +4344,11 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\crypto\tls\BasicTlsPskIdentity.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\crypto\tls\ByteQueue.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -4649,6 +4654,11 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\crypto\tls\PskTlsServer.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\crypto\tls\RecordStream.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -4904,6 +4914,11 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\crypto\tls\TlsPskIdentityManager.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\crypto\tls\TlsRsaKeyExchange.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
diff --git a/crypto/src/AssemblyInfo.cs b/crypto/src/AssemblyInfo.cs
index 7dd625878..4a813bc5a 100644
--- a/crypto/src/AssemblyInfo.cs
+++ b/crypto/src/AssemblyInfo.cs
@@ -14,7 +14,7 @@ using System.Runtime.InteropServices;
 [assembly: AssemblyConfiguration("")]
 [assembly: AssemblyCompany("The Legion of the Bouncy Castle Inc.")]
 [assembly: AssemblyProduct("Bouncy Castle for .NET")]
-[assembly: AssemblyCopyright("Copyright (C) 2000-2014")]
+[assembly: AssemblyCopyright("Copyright (C) 2000-2015")]
 [assembly: AssemblyTrademark("")]
 [assembly: AssemblyCulture("")]
 
diff --git a/crypto/src/crypto/tls/BasicTlsPskIdentity.cs b/crypto/src/crypto/tls/BasicTlsPskIdentity.cs
new file mode 100644
index 000000000..db5954422
--- /dev/null
+++ b/crypto/src/crypto/tls/BasicTlsPskIdentity.cs
@@ -0,0 +1,43 @@
+using System;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+    public class BasicTlsPskIdentity
+        : TlsPskIdentity
+    {
+        protected byte[] mIdentity;
+        protected byte[] mPsk;
+
+        public BasicTlsPskIdentity(byte[] identity, byte[] psk)
+        {
+            this.mIdentity = Arrays.Clone(identity);
+            this.mPsk = Arrays.Clone(psk);
+        }
+
+        public BasicTlsPskIdentity(string identity, byte[] psk)
+        {
+            this.mIdentity = Strings.ToUtf8ByteArray(identity);
+            this.mPsk = Arrays.Clone(psk);
+        }
+
+        public virtual void SkipIdentityHint()
+        {
+        }
+
+        public virtual void NotifyIdentityHint(byte[] psk_identity_hint)
+        {
+        }
+
+        public virtual byte[] GetPskIdentity()
+        {
+            return mIdentity;
+        }
+
+        public virtual byte[] GetPsk()
+        {
+            return mPsk;
+        }
+    }
+}
diff --git a/crypto/src/crypto/tls/DtlsClientProtocol.cs b/crypto/src/crypto/tls/DtlsClientProtocol.cs
index 67c49f890..2aa4df692 100644
--- a/crypto/src/crypto/tls/DtlsClientProtocol.cs
+++ b/crypto/src/crypto/tls/DtlsClientProtocol.cs
@@ -374,6 +374,7 @@ namespace Org.BouncyCastle.Crypto.Tls
                     .SetCompressionAlgorithm(securityParameters.compressionAlgorithm)
                     .SetMasterSecret(securityParameters.masterSecret)
                     .SetPeerCertificate(serverCertificate)
+                    .SetPskIdentity(securityParameters.pskIdentity)
                     .Build();
 
                 state.tlsSession = TlsUtilities.ImportSession(state.tlsSession.SessionID, state.sessionParameters);
diff --git a/crypto/src/crypto/tls/PskTlsClient.cs b/crypto/src/crypto/tls/PskTlsClient.cs
index 6063572a0..1f4b0865c 100644
--- a/crypto/src/crypto/tls/PskTlsClient.cs
+++ b/crypto/src/crypto/tls/PskTlsClient.cs
@@ -3,7 +3,7 @@ using System.Collections;
 
 namespace Org.BouncyCastle.Crypto.Tls
 {
-    public abstract class PskTlsClient
+    public class PskTlsClient
         :   AbstractTlsClient
     {
         protected TlsPskIdentity mPskIdentity;
@@ -25,8 +25,8 @@ namespace Org.BouncyCastle.Crypto.Tls
             {
                 CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256,
                 CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA,
-                CipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA256,
-                CipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA
+                CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA256,
+                CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA
             };
         }
 
@@ -124,6 +124,15 @@ namespace Org.BouncyCastle.Crypto.Tls
             }
         }
 
+        public override TlsAuthentication GetAuthentication()
+        {
+            /*
+             * Note: This method is not called unless a server certificate is sent, which may be the
+             * case e.g. for RSA_PSK key exchange.
+             */
+            throw new TlsFatalAlert(AlertDescription.internal_error);
+        }
+
         public override TlsCipher GetCipher()
         {
             switch (mSelectedCipherSuite)
@@ -254,7 +263,7 @@ namespace Org.BouncyCastle.Crypto.Tls
 
         protected virtual TlsKeyExchange CreatePskKeyExchange(int keyExchange)
         {
-            return new TlsPskKeyExchange(keyExchange, mSupportedSignatureAlgorithms, mPskIdentity, null, mNamedCurves,
+            return new TlsPskKeyExchange(keyExchange, mSupportedSignatureAlgorithms, mPskIdentity, null, null, mNamedCurves,
                 mClientECPointFormats, mServerECPointFormats);
         }
     }
diff --git a/crypto/src/crypto/tls/PskTlsServer.cs b/crypto/src/crypto/tls/PskTlsServer.cs
new file mode 100644
index 000000000..bdb8b74a5
--- /dev/null
+++ b/crypto/src/crypto/tls/PskTlsServer.cs
@@ -0,0 +1,347 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Agreement;
+using Org.BouncyCastle.Crypto.Parameters;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+    public class PskTlsServer
+        :   AbstractTlsServer
+    {
+        protected TlsPskIdentityManager mPskIdentityManager;
+
+        public PskTlsServer(TlsPskIdentityManager pskIdentityManager)
+            :   this(new DefaultTlsCipherFactory(), pskIdentityManager)
+        {
+        }
+
+        public PskTlsServer(TlsCipherFactory cipherFactory, TlsPskIdentityManager pskIdentityManager)
+            :   base(cipherFactory)
+        {
+            this.mPskIdentityManager = pskIdentityManager;
+        }
+
+        protected virtual TlsEncryptionCredentials GetRsaEncryptionCredentials()
+        {
+            throw new TlsFatalAlert(AlertDescription.internal_error);
+        }
+
+        protected virtual DHParameters GetDHParameters()
+        {
+            return DHStandardGroups.rfc5114_1024_160;
+        }
+
+        protected override int[] GetCipherSuites()
+        {
+            return new int[]
+            {
+                CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256,
+                CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA,
+                CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA256,
+                CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA
+            };
+        }
+
+        public override TlsCredentials GetCredentials()
+        {
+            switch (mSelectedCipherSuite)
+            {
+            case CipherSuite.TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA:
+            case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA:
+            case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA256:
+            case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CCM:
+            case CipherSuite.TLS_DHE_PSK_WITH_AES_128_GCM_SHA256:
+            case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA:
+            case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA384:
+            case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CCM:
+            case CipherSuite.TLS_DHE_PSK_WITH_AES_256_GCM_SHA384:
+            case CipherSuite.TLS_DHE_PSK_WITH_ESTREAM_SALSA20_SHA1:
+            case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA:
+            case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA256:
+            case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA384:
+            case CipherSuite.TLS_DHE_PSK_WITH_RC4_128_SHA:
+            case CipherSuite.TLS_DHE_PSK_WITH_SALSA20_SHA1:
+            case CipherSuite.TLS_PSK_DHE_WITH_AES_128_CCM_8:
+            case CipherSuite.TLS_PSK_DHE_WITH_AES_256_CCM_8:
+
+            case CipherSuite.TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA:
+            case CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA:
+            case CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256:
+            case CipherSuite.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA:
+            case CipherSuite.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384:
+            case CipherSuite.TLS_ECDHE_PSK_WITH_ESTREAM_SALSA20_SHA1:
+            case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA:
+            case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA256:
+            case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA384:
+            case CipherSuite.TLS_ECDHE_PSK_WITH_RC4_128_SHA:
+            case CipherSuite.TLS_ECDHE_PSK_WITH_SALSA20_SHA1:
+
+            case CipherSuite.TLS_PSK_WITH_3DES_EDE_CBC_SHA:
+            case CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA:
+            case CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA256:
+            case CipherSuite.TLS_PSK_WITH_AES_128_CCM:
+            case CipherSuite.TLS_PSK_WITH_AES_128_CCM_8:
+            case CipherSuite.TLS_PSK_WITH_AES_128_GCM_SHA256:
+            case CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA:
+            case CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA384:
+            case CipherSuite.TLS_PSK_WITH_AES_256_CCM:
+            case CipherSuite.TLS_PSK_WITH_AES_256_CCM_8:
+            case CipherSuite.TLS_PSK_WITH_AES_256_GCM_SHA384:
+            case CipherSuite.TLS_PSK_WITH_ESTREAM_SALSA20_SHA1:
+            case CipherSuite.TLS_PSK_WITH_NULL_SHA:
+            case CipherSuite.TLS_PSK_WITH_NULL_SHA256:
+            case CipherSuite.TLS_PSK_WITH_NULL_SHA384:
+            case CipherSuite.TLS_PSK_WITH_RC4_128_SHA:
+            case CipherSuite.TLS_PSK_WITH_SALSA20_SHA1:
+                return null;
+
+            case CipherSuite.TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA:
+            case CipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA:
+            case CipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA256:
+            case CipherSuite.TLS_RSA_PSK_WITH_AES_128_GCM_SHA256:
+            case CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA:
+            case CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA384:
+            case CipherSuite.TLS_RSA_PSK_WITH_AES_256_GCM_SHA384:
+            case CipherSuite.TLS_RSA_PSK_WITH_ESTREAM_SALSA20_SHA1:
+            case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA:
+            case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA256:
+            case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA384:
+            case CipherSuite.TLS_RSA_PSK_WITH_RC4_128_SHA:
+            case CipherSuite.TLS_RSA_PSK_WITH_SALSA20_SHA1:
+                return GetRsaEncryptionCredentials();
+
+            default:
+                /* Note: internal error here; selected a key exchange we don't implement! */
+                throw new TlsFatalAlert(AlertDescription.internal_error);
+            }
+        }
+
+        public override TlsKeyExchange GetKeyExchange()
+        {
+            switch (mSelectedCipherSuite)
+            {
+            case CipherSuite.TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA:
+            case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA:
+            case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA256:
+            case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CCM:
+            case CipherSuite.TLS_DHE_PSK_WITH_AES_128_GCM_SHA256:
+            case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA:
+            case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA384:
+            case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CCM:
+            case CipherSuite.TLS_DHE_PSK_WITH_AES_256_GCM_SHA384:
+            case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256:
+            case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256:
+            case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384:
+            case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384:
+            case CipherSuite.TLS_DHE_PSK_WITH_ESTREAM_SALSA20_SHA1:
+            case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA:
+            case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA256:
+            case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA384:
+            case CipherSuite.TLS_DHE_PSK_WITH_RC4_128_SHA:
+            case CipherSuite.TLS_DHE_PSK_WITH_SALSA20_SHA1:
+            case CipherSuite.TLS_PSK_DHE_WITH_AES_128_CCM_8:
+            case CipherSuite.TLS_PSK_DHE_WITH_AES_256_CCM_8:
+                return CreatePskKeyExchange(KeyExchangeAlgorithm.DHE_PSK);
+
+            case CipherSuite.TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA:
+            case CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA:
+            case CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256:
+            case CipherSuite.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA:
+            case CipherSuite.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384:
+            case CipherSuite.TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256:
+            case CipherSuite.TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384:
+            case CipherSuite.TLS_ECDHE_PSK_WITH_ESTREAM_SALSA20_SHA1:
+            case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA:
+            case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA256:
+            case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA384:
+            case CipherSuite.TLS_ECDHE_PSK_WITH_RC4_128_SHA:
+            case CipherSuite.TLS_ECDHE_PSK_WITH_SALSA20_SHA1:
+                return CreatePskKeyExchange(KeyExchangeAlgorithm.ECDHE_PSK);
+
+            case CipherSuite.TLS_PSK_WITH_3DES_EDE_CBC_SHA:
+            case CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA:
+            case CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA256:
+            case CipherSuite.TLS_PSK_WITH_AES_128_CCM:
+            case CipherSuite.TLS_PSK_WITH_AES_128_CCM_8:
+            case CipherSuite.TLS_PSK_WITH_AES_128_GCM_SHA256:
+            case CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA:
+            case CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA384:
+            case CipherSuite.TLS_PSK_WITH_AES_256_CCM:
+            case CipherSuite.TLS_PSK_WITH_AES_256_CCM_8:
+            case CipherSuite.TLS_PSK_WITH_AES_256_GCM_SHA384:
+            case CipherSuite.TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256:
+            case CipherSuite.TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256:
+            case CipherSuite.TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384:
+            case CipherSuite.TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384:
+            case CipherSuite.TLS_PSK_WITH_ESTREAM_SALSA20_SHA1:
+            case CipherSuite.TLS_PSK_WITH_NULL_SHA:
+            case CipherSuite.TLS_PSK_WITH_NULL_SHA256:
+            case CipherSuite.TLS_PSK_WITH_NULL_SHA384:
+            case CipherSuite.TLS_PSK_WITH_RC4_128_SHA:
+            case CipherSuite.TLS_PSK_WITH_SALSA20_SHA1:
+                return CreatePskKeyExchange(KeyExchangeAlgorithm.PSK);
+
+            case CipherSuite.TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA:
+            case CipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA:
+            case CipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA256:
+            case CipherSuite.TLS_RSA_PSK_WITH_AES_128_GCM_SHA256:
+            case CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA:
+            case CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA384:
+            case CipherSuite.TLS_RSA_PSK_WITH_AES_256_GCM_SHA384:
+            case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256:
+            case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256:
+            case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384:
+            case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384:
+            case CipherSuite.TLS_RSA_PSK_WITH_ESTREAM_SALSA20_SHA1:
+            case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA:
+            case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA256:
+            case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA384:
+            case CipherSuite.TLS_RSA_PSK_WITH_RC4_128_SHA:
+            case CipherSuite.TLS_RSA_PSK_WITH_SALSA20_SHA1:
+                return CreatePskKeyExchange(KeyExchangeAlgorithm.RSA_PSK);
+
+            default:
+                /*
+                 * Note: internal error here; the TlsProtocol implementation verifies that the
+                 * server-selected cipher suite was in the list of client-offered cipher suites, so if
+                 * we now can't produce an implementation, we shouldn't have offered it!
+                 */
+                throw new TlsFatalAlert(AlertDescription.internal_error);
+            }
+        }
+
+        public override TlsCipher GetCipher()
+        {
+            switch (mSelectedCipherSuite)
+            {
+            case CipherSuite.TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA:
+            case CipherSuite.TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA:
+            case CipherSuite.TLS_PSK_WITH_3DES_EDE_CBC_SHA:
+            case CipherSuite.TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA:
+                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.cls_3DES_EDE_CBC, MacAlgorithm.hmac_sha1);
+
+            case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA:
+            case CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA:
+            case CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA:
+            case CipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA:
+                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_128_CBC, MacAlgorithm.hmac_sha1);
+
+            case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA256:
+            case CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256:
+            case CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA256:
+            case CipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA256:
+                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_128_CBC, MacAlgorithm.hmac_sha256);
+
+            case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CCM:
+            case CipherSuite.TLS_PSK_WITH_AES_128_CCM:
+                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_128_CCM, MacAlgorithm.cls_null);
+
+            case CipherSuite.TLS_PSK_DHE_WITH_AES_128_CCM_8:
+            case CipherSuite.TLS_PSK_WITH_AES_128_CCM_8:
+                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_128_CCM_8, MacAlgorithm.cls_null);
+
+            case CipherSuite.TLS_DHE_PSK_WITH_AES_128_GCM_SHA256:
+            case CipherSuite.TLS_PSK_WITH_AES_128_GCM_SHA256:
+            case CipherSuite.TLS_RSA_PSK_WITH_AES_128_GCM_SHA256:
+                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_128_GCM, MacAlgorithm.cls_null);
+
+            case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA:
+            case CipherSuite.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA:
+            case CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA:
+            case CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA:
+                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_256_CBC, MacAlgorithm.hmac_sha1);
+
+            case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA384:
+            case CipherSuite.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384:
+            case CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA384:
+            case CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA384:
+                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_256_CBC, MacAlgorithm.hmac_sha384);
+
+            case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CCM:
+            case CipherSuite.TLS_PSK_WITH_AES_256_CCM:
+                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_256_CCM, MacAlgorithm.cls_null);
+
+            case CipherSuite.TLS_PSK_DHE_WITH_AES_256_CCM_8:
+            case CipherSuite.TLS_PSK_WITH_AES_256_CCM_8:
+                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_256_CCM_8, MacAlgorithm.cls_null);
+
+            case CipherSuite.TLS_DHE_PSK_WITH_AES_256_GCM_SHA384:
+            case CipherSuite.TLS_PSK_WITH_AES_256_GCM_SHA384:
+            case CipherSuite.TLS_RSA_PSK_WITH_AES_256_GCM_SHA384:
+                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_256_GCM, MacAlgorithm.cls_null);
+
+            case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256:
+            case CipherSuite.TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256:
+            case CipherSuite.TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256:
+            case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256:
+                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.CAMELLIA_128_CBC, MacAlgorithm.hmac_sha256);
+
+            case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256:
+            case CipherSuite.TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256:
+            case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256:
+                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.CAMELLIA_128_GCM, MacAlgorithm.cls_null);
+
+            case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384:
+            case CipherSuite.TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384:
+            case CipherSuite.TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384:
+            case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384:
+                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.CAMELLIA_256_CBC, MacAlgorithm.hmac_sha384);
+
+            case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384:
+            case CipherSuite.TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384:
+            case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384:
+                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.CAMELLIA_256_GCM, MacAlgorithm.cls_null);
+
+            case CipherSuite.TLS_DHE_PSK_WITH_ESTREAM_SALSA20_SHA1:
+            case CipherSuite.TLS_ECDHE_PSK_WITH_ESTREAM_SALSA20_SHA1:
+            case CipherSuite.TLS_PSK_WITH_ESTREAM_SALSA20_SHA1:
+            case CipherSuite.TLS_RSA_PSK_WITH_ESTREAM_SALSA20_SHA1:
+                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.ESTREAM_SALSA20, MacAlgorithm.hmac_sha1);
+
+            case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA:
+            case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA:
+            case CipherSuite.TLS_PSK_WITH_NULL_SHA:
+            case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA:
+                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.NULL, MacAlgorithm.hmac_sha1);
+                    
+            case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA256:
+            case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA256:
+            case CipherSuite.TLS_PSK_WITH_NULL_SHA256:
+            case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA256:
+                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.NULL, MacAlgorithm.hmac_sha256);
+
+            case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA384:
+            case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA384:
+            case CipherSuite.TLS_PSK_WITH_NULL_SHA384:
+            case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA384:
+                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.NULL, MacAlgorithm.hmac_sha384);
+
+            case CipherSuite.TLS_DHE_PSK_WITH_RC4_128_SHA:
+            case CipherSuite.TLS_ECDHE_PSK_WITH_RC4_128_SHA:
+            case CipherSuite.TLS_PSK_WITH_RC4_128_SHA:
+            case CipherSuite.TLS_RSA_PSK_WITH_RC4_128_SHA:
+                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.RC4_128, MacAlgorithm.hmac_sha1);
+
+            case CipherSuite.TLS_DHE_PSK_WITH_SALSA20_SHA1:
+            case CipherSuite.TLS_ECDHE_PSK_WITH_SALSA20_SHA1:
+            case CipherSuite.TLS_PSK_WITH_SALSA20_SHA1:
+            case CipherSuite.TLS_RSA_PSK_WITH_SALSA20_SHA1:
+                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.SALSA20, MacAlgorithm.hmac_sha1);
+
+            default:
+                /*
+                 * Note: internal error here; the TlsProtocol implementation verifies that the
+                 * server-selected cipher suite was in the list of client-offered cipher suites, so if
+                 * we now can't produce an implementation, we shouldn't have offered it!
+                 */
+                throw new TlsFatalAlert(AlertDescription.internal_error);
+            }
+        }
+
+        protected TlsKeyExchange CreatePskKeyExchange(int keyExchange)
+        {
+            return new TlsPskKeyExchange(keyExchange, mSupportedSignatureAlgorithms, null, mPskIdentityManager,
+                GetDHParameters(), mNamedCurves, mClientECPointFormats, mServerECPointFormats);
+        }
+    }
+}
diff --git a/crypto/src/crypto/tls/SecurityParameters.cs b/crypto/src/crypto/tls/SecurityParameters.cs
index 12bb59f22..0f48ee23e 100644
--- a/crypto/src/crypto/tls/SecurityParameters.cs
+++ b/crypto/src/crypto/tls/SecurityParameters.cs
@@ -15,6 +15,7 @@ namespace Org.BouncyCastle.Crypto.Tls
         internal byte[] clientRandom = null;
         internal byte[] serverRandom = null;
         internal byte[] sessionHash = null;
+        internal byte[] pskIdentity = null;
 
         // TODO Keep these internal, since it's maybe not the ideal place for them
         internal short maxFragmentLength = -1;
@@ -87,5 +88,10 @@ namespace Org.BouncyCastle.Crypto.Tls
         {
             get { return sessionHash; }
         }
+
+        public virtual byte[] PskIdentity
+        {
+            get { return pskIdentity; }
+        }
     }
 }
diff --git a/crypto/src/crypto/tls/ServerDHParams.cs b/crypto/src/crypto/tls/ServerDHParams.cs
index 381858854..b09262771 100644
--- a/crypto/src/crypto/tls/ServerDHParams.cs
+++ b/crypto/src/crypto/tls/ServerDHParams.cs
@@ -54,7 +54,8 @@ namespace Org.BouncyCastle.Crypto.Tls
             BigInteger g = TlsDHUtilities.ReadDHParameter(input);
             BigInteger Ys = TlsDHUtilities.ReadDHParameter(input);
 
-            return new ServerDHParams(new DHPublicKeyParameters(Ys, new DHParameters(p, g)));
+            return new ServerDHParams(
+                TlsDHUtilities.ValidateDHPublicKey(new DHPublicKeyParameters(Ys, new DHParameters(p, g))));
         }
     }
 }
diff --git a/crypto/src/crypto/tls/SessionParameters.cs b/crypto/src/crypto/tls/SessionParameters.cs
index c4616ac71..b17e931d7 100644
--- a/crypto/src/crypto/tls/SessionParameters.cs
+++ b/crypto/src/crypto/tls/SessionParameters.cs
@@ -14,6 +14,7 @@ namespace Org.BouncyCastle.Crypto.Tls
             private short mCompressionAlgorithm = -1;
             private byte[] mMasterSecret = null;
             private Certificate mPeerCertificate = null;
+            private byte[] mPskIdentity = null;
             private byte[] mEncodedServerExtensions = null;
 
             public Builder()
@@ -26,7 +27,7 @@ namespace Org.BouncyCastle.Crypto.Tls
                 Validate(this.mCompressionAlgorithm >= 0, "compressionAlgorithm");
                 Validate(this.mMasterSecret != null, "masterSecret");
                 return new SessionParameters(mCipherSuite, (byte)mCompressionAlgorithm, mMasterSecret, mPeerCertificate,
-                    mEncodedServerExtensions);
+                    mPskIdentity, mEncodedServerExtensions);
             }
 
             public Builder SetCipherSuite(int cipherSuite)
@@ -53,6 +54,12 @@ namespace Org.BouncyCastle.Crypto.Tls
                 return this;
             }
 
+            public Builder SetPskIdentity(byte[] pskIdentity)
+            {
+                this.mPskIdentity = pskIdentity;
+                return this;
+            }
+
             public Builder SetServerExtensions(IDictionary serverExtensions)
             {
                 if (serverExtensions == null)
@@ -79,15 +86,17 @@ namespace Org.BouncyCastle.Crypto.Tls
         private byte mCompressionAlgorithm;
         private byte[] mMasterSecret;
         private Certificate mPeerCertificate;
+        private byte[] mPskIdentity;
         private byte[] mEncodedServerExtensions;
 
         private SessionParameters(int cipherSuite, byte compressionAlgorithm, byte[] masterSecret,
-            Certificate peerCertificate, byte[] encodedServerExtensions)
+            Certificate peerCertificate, byte[] pskIdentity, byte[] encodedServerExtensions)
         {
             this.mCipherSuite = cipherSuite;
             this.mCompressionAlgorithm = compressionAlgorithm;
             this.mMasterSecret = Arrays.Clone(masterSecret);
             this.mPeerCertificate = peerCertificate;
+            this.mPskIdentity = Arrays.Clone(pskIdentity);
             this.mEncodedServerExtensions = encodedServerExtensions;
         }
 
@@ -102,7 +111,7 @@ namespace Org.BouncyCastle.Crypto.Tls
         public SessionParameters Copy()
         {
             return new SessionParameters(mCipherSuite, mCompressionAlgorithm, mMasterSecret, mPeerCertificate,
-                mEncodedServerExtensions);
+                mPskIdentity, mEncodedServerExtensions);
         }
 
         public int CipherSuite
@@ -125,6 +134,11 @@ namespace Org.BouncyCastle.Crypto.Tls
             get { return mPeerCertificate; }
         }
 
+        public byte[] PskIdentity
+        {
+            get { return mPskIdentity; }
+        }
+
         public IDictionary ReadServerExtensions()
         {
             if (mEncodedServerExtensions == null)
diff --git a/crypto/src/crypto/tls/TlsECDheKeyExchange.cs b/crypto/src/crypto/tls/TlsECDheKeyExchange.cs
index 0644bd44d..b99db0c18 100644
--- a/crypto/src/crypto/tls/TlsECDheKeyExchange.cs
+++ b/crypto/src/crypto/tls/TlsECDheKeyExchange.cs
@@ -34,73 +34,10 @@ namespace Org.BouncyCastle.Crypto.Tls
 
         public override byte[] GenerateServerKeyExchange()
         {
-            /*
-             * First we try to find a supported named curve from the client's list.
-             */
-            int namedCurve = -1;
-            if (mNamedCurves == null)
-            {
-                // TODO Let the peer choose the default named curve
-                namedCurve = NamedCurve.secp256r1;
-            }
-            else
-            {
-                for (int i = 0; i < mNamedCurves.Length; ++i)
-                {
-                    int entry = mNamedCurves[i];
-                    if (NamedCurve.IsValid(entry) && TlsEccUtilities.IsSupportedNamedCurve(entry))
-                    {
-                        namedCurve = entry;
-                        break;
-                    }
-                }
-            }
-
-            ECDomainParameters curve_params = null;
-            if (namedCurve >= 0)
-            {
-                curve_params = TlsEccUtilities.GetParametersForNamedCurve(namedCurve);
-            }
-            else
-            {
-                /*
-                 * If no named curves are suitable, check if the client supports explicit curves.
-                 */
-                if (Arrays.Contains(mNamedCurves, NamedCurve.arbitrary_explicit_prime_curves))
-                {
-                    curve_params = TlsEccUtilities.GetParametersForNamedCurve(NamedCurve.secp256r1);
-                }
-                else if (Arrays.Contains(mNamedCurves, NamedCurve.arbitrary_explicit_char2_curves))
-                {
-                    curve_params = TlsEccUtilities.GetParametersForNamedCurve(NamedCurve.sect283r1);
-                }
-            }
-
-            if (curve_params == null)
-            {
-                /*
-                 * NOTE: We shouldn't have negotiated ECDHE key exchange since we apparently can't find
-                 * a suitable curve.
-                 */
-                throw new TlsFatalAlert(AlertDescription.internal_error);
-            }
-
-            AsymmetricCipherKeyPair kp = TlsEccUtilities.GenerateECKeyPair(context.SecureRandom, curve_params);
-            this.mECAgreePrivateKey = (ECPrivateKeyParameters)kp.Private;
-
             DigestInputBuffer buf = new DigestInputBuffer();
 
-            if (namedCurve < 0)
-            {
-                TlsEccUtilities.WriteExplicitECParameters(mClientECPointFormats, curve_params, buf);
-            }
-            else
-            {
-                TlsEccUtilities.WriteNamedECParameters(namedCurve, buf);
-            }
-
-            ECPublicKeyParameters ecPublicKey = (ECPublicKeyParameters)kp.Public;
-            TlsEccUtilities.WriteECPoint(mClientECPointFormats, ecPublicKey.Q, buf);
+            this.mECAgreePrivateKey = TlsEccUtilities.GenerateEphemeralServerKeyExchange(context.SecureRandom, mNamedCurves,
+                mClientECPointFormats, buf);
 
             /*
              * RFC 5246 4.7. digitally-signed element needs SignatureAndHashAlgorithm from TLS 1.2
diff --git a/crypto/src/crypto/tls/TlsEccUtilities.cs b/crypto/src/crypto/tls/TlsEccUtilities.cs
index 34f0f57ba..e938b1685 100644
--- a/crypto/src/crypto/tls/TlsEccUtilities.cs
+++ b/crypto/src/crypto/tls/TlsEccUtilities.cs
@@ -435,6 +435,69 @@ namespace Org.BouncyCastle.Crypto.Tls
             return (ECPrivateKeyParameters)kp.Private;
         }
 
+        // TODO Refactor around ServerECDHParams before making this public
+        internal static ECPrivateKeyParameters GenerateEphemeralServerKeyExchange(SecureRandom random, int[] namedCurves,
+            byte[] ecPointFormats, Stream output)
+        {
+            /* First we try to find a supported named curve from the client's list. */
+            int namedCurve = -1;
+            if (namedCurves == null)
+            {
+                // TODO Let the peer choose the default named curve
+                namedCurve = NamedCurve.secp256r1;
+            }
+            else
+            {
+                for (int i = 0; i < namedCurves.Length; ++i)
+                {
+                    int entry = namedCurves[i];
+                    if (NamedCurve.IsValid(entry) && IsSupportedNamedCurve(entry))
+                    {
+                        namedCurve = entry;
+                        break;
+                    }
+                }
+            }
+
+            ECDomainParameters ecParams = null;
+            if (namedCurve >= 0)
+            {
+                ecParams = GetParametersForNamedCurve(namedCurve);
+            }
+            else
+            {
+                /* If no named curves are suitable, check if the client supports explicit curves. */
+                if (Arrays.Contains(namedCurves, NamedCurve.arbitrary_explicit_prime_curves))
+                {
+                    ecParams = GetParametersForNamedCurve(NamedCurve.secp256r1);
+                }
+                else if (Arrays.Contains(namedCurves, NamedCurve.arbitrary_explicit_char2_curves))
+                {
+                    ecParams = GetParametersForNamedCurve(NamedCurve.sect283r1);
+                }
+            }
+
+            if (ecParams == null)
+            {
+                /*
+                 * NOTE: We shouldn't have negotiated ECDHE key exchange since we apparently can't find
+                 * a suitable curve.
+                 */
+                throw new TlsFatalAlert(AlertDescription.internal_error);
+            }
+
+            if (namedCurve < 0)
+            {
+                WriteExplicitECParameters(ecPointFormats, ecParams, output);
+            }
+            else
+            {
+                WriteNamedECParameters(namedCurve, output);
+            }
+
+            return GenerateEphemeralClientKeyExchange(random, ecPointFormats, ecParams, output);
+        }
+
         public static ECPublicKeyParameters ValidateECPublicKey(ECPublicKeyParameters key)
         {
             // TODO Check RFC 4492 for validation
diff --git a/crypto/src/crypto/tls/TlsPskIdentityManager.cs b/crypto/src/crypto/tls/TlsPskIdentityManager.cs
new file mode 100644
index 000000000..a72c2299c
--- /dev/null
+++ b/crypto/src/crypto/tls/TlsPskIdentityManager.cs
@@ -0,0 +1,11 @@
+using System;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+    public interface TlsPskIdentityManager
+    {
+        byte[] GetHint();
+
+        byte[] GetPsk(byte[] identity);
+    }
+}
diff --git a/crypto/src/crypto/tls/TlsPskKeyExchange.cs b/crypto/src/crypto/tls/TlsPskKeyExchange.cs
index cd13e3438..a8d0867ef 100644
--- a/crypto/src/crypto/tls/TlsPskKeyExchange.cs
+++ b/crypto/src/crypto/tls/TlsPskKeyExchange.cs
@@ -4,7 +4,10 @@ using System.IO;
 
 using Org.BouncyCastle.Asn1.X509;
 using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
 using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.IO;
 
 namespace Org.BouncyCastle.Crypto.Tls
 {
@@ -13,22 +16,29 @@ namespace Org.BouncyCastle.Crypto.Tls
         :   AbstractTlsKeyExchange
     {
         protected TlsPskIdentity mPskIdentity;
+        protected TlsPskIdentityManager mPskIdentityManager;
+
         protected DHParameters mDHParameters;
         protected int[] mNamedCurves;
         protected byte[] mClientECPointFormats, mServerECPointFormats;
 
         protected byte[] mPskIdentityHint = null;
+        protected byte[] mPsk = null;
 
         protected DHPrivateKeyParameters mDHAgreePrivateKey = null;
         protected DHPublicKeyParameters mDHAgreePublicKey = null;
 
+        protected ECPrivateKeyParameters mECAgreePrivateKey = null;
+        protected ECPublicKeyParameters mECAgreePublicKey = null;
+
         protected AsymmetricKeyParameter mServerPublicKey = null;
         protected RsaKeyParameters mRsaServerPublicKey = null;
         protected TlsEncryptionCredentials mServerCredentials = null;
         protected byte[] mPremasterSecret;
 
         public TlsPskKeyExchange(int keyExchange, IList supportedSignatureAlgorithms, TlsPskIdentity pskIdentity,
-            DHParameters dhParameters, int[] namedCurves, byte[] clientECPointFormats, byte[] serverECPointFormats)
+            TlsPskIdentityManager pskIdentityManager, DHParameters dhParameters, int[] namedCurves,
+            byte[] clientECPointFormats, byte[] serverECPointFormats)
             :   base(keyExchange, supportedSignatureAlgorithms)
         {
             switch (keyExchange)
@@ -43,6 +53,7 @@ namespace Org.BouncyCastle.Crypto.Tls
             }
 
             this.mPskIdentity = pskIdentity;
+            this.mPskIdentityManager = pskIdentityManager;
             this.mDHParameters = dhParameters;
             this.mNamedCurves = namedCurves;
             this.mClientECPointFormats = clientECPointFormats;
@@ -67,8 +78,7 @@ namespace Org.BouncyCastle.Crypto.Tls
 
         public override byte[] GenerateServerKeyExchange()
         {
-            // TODO[RFC 4279] Need a server-side PSK API to determine hint and resolve identities to keys
-            this.mPskIdentityHint = null;
+            this.mPskIdentityHint = mPskIdentityManager.GetHint();
 
             if (this.mPskIdentityHint == null && !RequiresServerKeyExchange)
                 return null;
@@ -94,7 +104,8 @@ namespace Org.BouncyCastle.Crypto.Tls
             }
             else if (this.mKeyExchange == KeyExchangeAlgorithm.ECDHE_PSK)
             {
-                // TODO[RFC 5489]
+                this.mECAgreePrivateKey = TlsEccUtilities.GenerateEphemeralServerKeyExchange(context.SecureRandom,
+                    mNamedCurves, mClientECPointFormats, buf);
             }
 
             return buf.ToArray();
@@ -157,7 +168,12 @@ namespace Org.BouncyCastle.Crypto.Tls
             }
             else if (this.mKeyExchange == KeyExchangeAlgorithm.ECDHE_PSK)
             {
-                // TODO[RFC 5489]
+                ECDomainParameters ecParams = TlsEccUtilities.ReadECParameters(mNamedCurves, mClientECPointFormats, input);
+
+                byte[] point = TlsUtilities.ReadOpaque8(input);
+
+                this.mECAgreePublicKey = TlsEccUtilities.ValidateECPublicKey(TlsEccUtilities.DeserializeECPublicKey(
+                    mClientECPointFormats, ecParams, point));
             }
         }
 
@@ -183,9 +199,17 @@ namespace Org.BouncyCastle.Crypto.Tls
             }
 
             byte[] psk_identity = mPskIdentity.GetPskIdentity();
+            if (psk_identity == null)
+                throw new TlsFatalAlert(AlertDescription.internal_error);
+
+            this.mPsk = mPskIdentity.GetPsk();
+            if (mPsk == null)
+                throw new TlsFatalAlert(AlertDescription.internal_error);
 
             TlsUtilities.WriteOpaque16(psk_identity, output);
 
+            context.SecurityParameters.pskIdentity = psk_identity;
+
             if (this.mKeyExchange == KeyExchangeAlgorithm.DHE_PSK)
             {
                 this.mDHAgreePrivateKey = TlsDHUtilities.GenerateEphemeralClientKeyExchange(context.SecureRandom,
@@ -193,8 +217,8 @@ namespace Org.BouncyCastle.Crypto.Tls
             }
             else if (this.mKeyExchange == KeyExchangeAlgorithm.ECDHE_PSK)
             {
-                // TODO[RFC 5489]
-                throw new TlsFatalAlert(AlertDescription.internal_error);
+                this.mECAgreePrivateKey = TlsEccUtilities.GenerateEphemeralClientKeyExchange(context.SecureRandom,
+                    mServerECPointFormats, mECAgreePublicKey.Parameters, output);
             }
             else if (this.mKeyExchange == KeyExchangeAlgorithm.RSA_PSK)
             {
@@ -203,14 +227,59 @@ namespace Org.BouncyCastle.Crypto.Tls
             }
         }
 
+        public override void ProcessClientKeyExchange(Stream input)
+        {
+            byte[] psk_identity = TlsUtilities.ReadOpaque16(input);
+
+            this.mPsk = mPskIdentityManager.GetPsk(psk_identity);
+            if (mPsk == null)
+                throw new TlsFatalAlert(AlertDescription.unknown_psk_identity);
+
+            context.SecurityParameters.pskIdentity = psk_identity;
+
+            if (this.mKeyExchange == KeyExchangeAlgorithm.DHE_PSK)
+            {
+                BigInteger Yc = TlsDHUtilities.ReadDHParameter(input);
+
+                this.mDHAgreePublicKey = TlsDHUtilities.ValidateDHPublicKey(new DHPublicKeyParameters(Yc, mDHParameters));
+            }
+            else if (this.mKeyExchange == KeyExchangeAlgorithm.ECDHE_PSK)
+            {
+                byte[] point = TlsUtilities.ReadOpaque8(input);
+
+                ECDomainParameters curve_params = this.mECAgreePrivateKey.Parameters;
+
+                this.mECAgreePublicKey = TlsEccUtilities.ValidateECPublicKey(TlsEccUtilities.DeserializeECPublicKey(
+                    mServerECPointFormats, curve_params, point));
+            }
+            else if (this.mKeyExchange == KeyExchangeAlgorithm.RSA_PSK)
+            {
+                byte[] encryptedPreMasterSecret;
+                if (TlsUtilities.IsSsl(context))
+                {
+                    // TODO Do any SSLv3 clients actually include the length?
+                    encryptedPreMasterSecret = Streams.ReadAll(input);
+                }
+                else
+                {
+                    encryptedPreMasterSecret = TlsUtilities.ReadOpaque16(input);
+                }
+
+                this.mPremasterSecret = mServerCredentials.DecryptPreMasterSecret(encryptedPreMasterSecret);
+            }
+        }
+
         public override byte[] GeneratePremasterSecret()
         {
-            byte[] psk = mPskIdentity.GetPsk();
-            byte[] other_secret = GenerateOtherSecret(psk.Length);
+            byte[] other_secret = GenerateOtherSecret(mPsk.Length);
 
-            MemoryStream buf = new MemoryStream(4 + other_secret.Length + psk.Length);
+            MemoryStream buf = new MemoryStream(4 + other_secret.Length + mPsk.Length);
             TlsUtilities.WriteOpaque16(other_secret, buf);
-            TlsUtilities.WriteOpaque16(psk, buf);
+            TlsUtilities.WriteOpaque16(mPsk, buf);
+
+            Arrays.Fill(mPsk, (byte)0);
+            this.mPsk = null;
+
             return buf.ToArray();
         }
 
@@ -228,7 +297,11 @@ namespace Org.BouncyCastle.Crypto.Tls
 
             if (this.mKeyExchange == KeyExchangeAlgorithm.ECDHE_PSK)
             {
-                // TODO[RFC 5489]
+                if (mECAgreePrivateKey != null)
+                {
+                    return TlsEccUtilities.CalculateECDHBasicAgreement(mECAgreePublicKey, mECAgreePrivateKey);
+                }
+
                 throw new TlsFatalAlert(AlertDescription.internal_error);
             }
 
diff --git a/crypto/src/math/ec/ECAlgorithms.cs b/crypto/src/math/ec/ECAlgorithms.cs
index 3c911b173..a1349a9e0 100644
--- a/crypto/src/math/ec/ECAlgorithms.cs
+++ b/crypto/src/math/ec/ECAlgorithms.cs
@@ -117,6 +117,11 @@ namespace Org.BouncyCastle.Math.EC
 
         public static void MontgomeryTrick(ECFieldElement[] zs, int off, int len)
         {
+            MontgomeryTrick(zs, off, len, null);
+        }
+
+        public static void MontgomeryTrick(ECFieldElement[] zs, int off, int len, ECFieldElement scale)
+        {
             /*
              * Uses the "Montgomery Trick" to invert many field elements, with only a single actual
              * field inversion. See e.g. the paper:
@@ -133,7 +138,14 @@ namespace Org.BouncyCastle.Math.EC
                 c[i] = c[i - 1].Multiply(zs[off + i]);
             }
 
-            ECFieldElement u = c[--i].Invert();
+            --i;
+
+            if (scale != null)
+            {
+                c[i] = c[i].Multiply(scale);
+            }
+
+            ECFieldElement u = c[i].Invert();
 
             while (i > 0)
             {
diff --git a/crypto/src/math/ec/ECCurve.cs b/crypto/src/math/ec/ECCurve.cs
index eaa3e0c3d..339d37f7c 100644
--- a/crypto/src/math/ec/ECCurve.cs
+++ b/crypto/src/math/ec/ECCurve.cs
@@ -221,26 +221,56 @@ namespace Org.BouncyCastle.Math.EC
          */
         public virtual void NormalizeAll(ECPoint[] points)
         {
-            CheckPoints(points);
+            NormalizeAll(points, 0, points.Length, null);
+        }
+
+        /**
+         * Normalization ensures that any projective coordinate is 1, and therefore that the x, y
+         * coordinates reflect those of the equivalent point in an affine coordinate system. Where more
+         * than one point is to be normalized, this method will generally be more efficient than
+         * normalizing each point separately. An (optional) z-scaling factor can be applied; effectively
+         * each z coordinate is scaled by this value prior to normalization (but only one
+         * actual multiplication is needed).
+         * 
+         * @param points
+         *            An array of points that will be updated in place with their normalized versions,
+         *            where necessary
+         * @param off
+         *            The start of the range of points to normalize
+         * @param len
+         *            The length of the range of points to normalize
+         * @param iso
+         *            The (optional) z-scaling factor - can be null
+         */
+        public virtual void NormalizeAll(ECPoint[] points, int off, int len, ECFieldElement iso)
+        {
+            CheckPoints(points, off, len);
 
-            if (this.CoordinateSystem == ECCurve.COORD_AFFINE)
+            switch (this.CoordinateSystem)
             {
-                return;
+                case ECCurve.COORD_AFFINE:
+                case ECCurve.COORD_LAMBDA_AFFINE:
+                {
+                    if (iso != null)
+                        throw new ArgumentException("not valid for affine coordinates", "iso");
+
+                    return;
+                }
             }
 
             /*
              * Figure out which of the points actually need to be normalized
              */
-            ECFieldElement[] zs = new ECFieldElement[points.Length];
-            int[] indices = new int[points.Length];
+            ECFieldElement[] zs = new ECFieldElement[len];
+            int[] indices = new int[len];
             int count = 0;
-            for (int i = 0; i < points.Length; ++i)
+            for (int i = 0; i < len; ++i)
             {
-                ECPoint p = points[i];
-                if (null != p && !p.IsNormalized())
+                ECPoint p = points[off + i];
+                if (null != p && (iso != null || !p.IsNormalized()))
                 {
                     zs[count] = p.GetZCoord(0);
-                    indices[count++] = i;
+                    indices[count++] = off + i;
                 }
             }
 
@@ -249,7 +279,7 @@ namespace Org.BouncyCastle.Math.EC
                 return;
             }
 
-            ECAlgorithms.MontgomeryTrick(zs, 0, count);
+            ECAlgorithms.MontgomeryTrick(zs, 0, count, iso);
 
             for (int j = 0; j < count; ++j)
             {
@@ -298,12 +328,19 @@ namespace Org.BouncyCastle.Math.EC
 
         protected virtual void CheckPoints(ECPoint[] points)
         {
+            CheckPoints(points, 0, points.Length);
+        }
+
+        protected virtual void CheckPoints(ECPoint[] points, int off, int len)
+        {
             if (points == null)
                 throw new ArgumentNullException("points");
+            if (off < 0 || len < 0 || (off > (points.Length - len)))
+                throw new ArgumentException("invalid range specified", "points");
 
-            for (int i = 0; i < points.Length; ++i)
+            for (int i = 0; i < len; ++i)
             {
-                ECPoint point = points[i];
+                ECPoint point = points[off + i];
                 if (null != point && this != point.Curve)
                     throw new ArgumentException("entries must be null or on this curve", "points");
             }
diff --git a/crypto/src/math/ec/multiplier/WNafUtilities.cs b/crypto/src/math/ec/multiplier/WNafUtilities.cs
index 865b9073e..5491297d7 100644
--- a/crypto/src/math/ec/multiplier/WNafUtilities.cs
+++ b/crypto/src/math/ec/multiplier/WNafUtilities.cs
@@ -10,6 +10,7 @@ namespace Org.BouncyCastle.Math.EC.Multiplier
 
         private static readonly byte[] EMPTY_BYTES = new byte[0];
         private static readonly int[] EMPTY_INTS = new int[0];
+        private static readonly ECPoint[] EMPTY_POINTS = new ECPoint[0];
 
         public static int[] GenerateCompactNaf(BigInteger k)
         {
@@ -368,46 +369,100 @@ namespace Org.BouncyCastle.Math.EC.Multiplier
         {
             ECCurve c = p.Curve;
             WNafPreCompInfo wnafPreCompInfo = GetWNafPreCompInfo(c.GetPreCompInfo(p, PRECOMP_NAME));
-            
+
+            int iniPreCompLen = 0, reqPreCompLen = 1 << System.Math.Max(0, width - 2);
+
             ECPoint[] preComp = wnafPreCompInfo.PreComp;
             if (preComp == null)
             {
-                preComp = new ECPoint[]{ p };
+                preComp = EMPTY_POINTS;
+            }
+            else
+            {
+                iniPreCompLen = preComp.Length;
             }
 
-            int preCompLen = preComp.Length;
-            int reqPreCompLen = 1 << System.Math.Max(0, width - 2);
-
-            if (preCompLen < reqPreCompLen)
+            if (iniPreCompLen < reqPreCompLen)
             {
                 preComp = ResizeTable(preComp, reqPreCompLen);
-                if (reqPreCompLen == 2)
+
+                if (reqPreCompLen == 1)
                 {
-                    preComp[1] = preComp[0].ThreeTimes();
+                    preComp[0] = p.Normalize();
                 }
                 else
                 {
-                    ECPoint twiceP = wnafPreCompInfo.Twice;
-                    if (twiceP == null)
+                    int curPreCompLen = iniPreCompLen;
+                    if (curPreCompLen == 0)
                     {
-                        twiceP = preComp[0].Twice();
-                        wnafPreCompInfo.Twice = twiceP;
+                        preComp[0] = p;
+                        curPreCompLen = 1;
                     }
 
-                    for (int i = preCompLen; i < reqPreCompLen; i++)
+                    ECFieldElement iso = null;
+
+                    if (reqPreCompLen == 2)
                     {
-                        /*
-                         * Compute the new ECPoints for the precomputation array. The values 1, 3, 5, ...,
-                         * 2^(width-1)-1 times p are computed
-                         */
-                        preComp[i] = twiceP.Add(preComp[i - 1]);
+                        preComp[1] = p.ThreeTimes();
+                    }
+                    else
+                    {
+                        ECPoint twiceP = wnafPreCompInfo.Twice, last = preComp[curPreCompLen - 1];
+                        if (twiceP == null)
+                        {
+                            twiceP = preComp[0].Twice();
+                            wnafPreCompInfo.Twice = twiceP;
+
+                            /*
+                             * For Fp curves with Jacobian projective coordinates, use a (quasi-)isomorphism
+                             * where 'twiceP' is "affine", so that the subsequent additions are cheaper. This
+                             * also requires scaling the initial point's X, Y coordinates, and reversing the
+                             * isomorphism as part of the subsequent normalization.
+                             * 
+                             *  NOTE: The correctness of this optimization depends on:
+                             *      1) additions do not use the curve's A, B coefficients.
+                             *      2) no special cases (i.e. Q +/- Q) when calculating 1P, 3P, 5P, ...
+                             */
+                            if (ECAlgorithms.IsFpCurve(c) && c.FieldSize >= 64)
+                            {
+                                switch (c.CoordinateSystem)
+                                {
+                                    case ECCurve.COORD_JACOBIAN:
+                                    case ECCurve.COORD_JACOBIAN_CHUDNOVSKY:
+                                    case ECCurve.COORD_JACOBIAN_MODIFIED:
+                                    {
+                                        iso = twiceP.GetZCoord(0);
+                                        twiceP = c.CreatePoint(twiceP.XCoord.ToBigInteger(),
+                                            twiceP.YCoord.ToBigInteger());
+
+                                        ECFieldElement iso2 = iso.Square(), iso3 = iso2.Multiply(iso);
+                                        last = last.ScaleX(iso2).ScaleY(iso3);
+
+                                        if (iniPreCompLen == 0)
+                                        {
+                                            preComp[0] = last;
+                                        }
+                                        break;
+                                    }
+                                }
+                            }
+                        }
+
+                        while (curPreCompLen < reqPreCompLen)
+                        {
+                            /*
+                             * Compute the new ECPoints for the precomputation array. The values 1, 3,
+                             * 5, ..., 2^(width-1)-1 times p are computed
+                             */
+                            preComp[curPreCompLen++] = last = last.Add(twiceP);
+                        }
                     }
-                }
 
-                /*
-                 * Having oft-used operands in affine form makes operations faster.
-                 */
-                c.NormalizeAll(preComp);
+                    /*
+                     * Having oft-used operands in affine form makes operations faster.
+                     */
+                    c.NormalizeAll(preComp, iniPreCompLen, reqPreCompLen - iniPreCompLen, iso);
+                }
             }
 
             wnafPreCompInfo.PreComp = preComp;
diff --git a/crypto/src/security/SignerUtilities.cs b/crypto/src/security/SignerUtilities.cs
index 0cf113f65..c1aea50d6 100644
--- a/crypto/src/security/SignerUtilities.cs
+++ b/crypto/src/security/SignerUtilities.cs
@@ -261,10 +261,10 @@ namespace Org.BouncyCastle.Security
         }
 
         /// <summary>
-        /// Returns a ObjectIdentifier for a give encoding.
+        /// Returns an ObjectIdentifier for a given encoding.
         /// </summary>
         /// <param name="mechanism">A string representation of the encoding.</param>
-        /// <returns>A DerObjectIdentifier, null if the Oid is not available.</returns>
+        /// <returns>A DerObjectIdentifier, null if the OID is not available.</returns>
         // TODO Don't really want to support this
         public static DerObjectIdentifier GetObjectIdentifier(
             string mechanism)