summary refs log tree commit diff
diff options
context:
space:
mode:
authorPeter Dettman <peter.dettman@bouncycastle.org>2014-07-23 16:53:51 +0700
committerPeter Dettman <peter.dettman@bouncycastle.org>2014-07-23 16:53:51 +0700
commite5db546809a6485a82427c105c1d4f3a3b25fa5a (patch)
tree94e4823bc84bb04c366c296e24409b1dc817bc4c
parentAdd automatic EC point validation for decoded points and for multiplier outputs (diff)
downloadBouncyCastle.NET-ed25519-e5db546809a6485a82427c105c1d4f3a3b25fa5a.tar.xz
Port of new TlsEccUtilities class from Java
Various support methods in TlsUtilities and Arrays
Fix short->byte for several enumeration types
-rw-r--r--crypto/crypto.csproj5
-rw-r--r--crypto/src/crypto/tls/CertChainType.cs6
-rw-r--r--crypto/src/crypto/tls/CertificateStatusType.cs2
-rw-r--r--crypto/src/crypto/tls/ChangeCipherSpec.cs2
-rw-r--r--crypto/src/crypto/tls/ClientAuthenticationType.cs6
-rw-r--r--crypto/src/crypto/tls/ECBasisType.cs6
-rw-r--r--crypto/src/crypto/tls/HeartbeatMessageType.cs6
-rw-r--r--crypto/src/crypto/tls/HeartbeatMode.cs6
-rw-r--r--crypto/src/crypto/tls/MaxFragmentLength.cs10
-rw-r--r--crypto/src/crypto/tls/NameType.cs2
-rw-r--r--crypto/src/crypto/tls/NamedCurve.cs23
-rw-r--r--crypto/src/crypto/tls/TlsECDheKeyExchange.cs2
-rw-r--r--crypto/src/crypto/tls/TlsEccUtilities.cs644
-rw-r--r--crypto/src/crypto/tls/TlsProtocolHandler.cs2
-rw-r--r--crypto/src/crypto/tls/TlsUtilities.cs146
-rw-r--r--crypto/src/crypto/tls/UserMappingType.cs2
-rw-r--r--crypto/src/util/Arrays.cs102
17 files changed, 901 insertions, 71 deletions
diff --git a/crypto/crypto.csproj b/crypto/crypto.csproj
index 126f5f1d4..83d0305c2 100644
--- a/crypto/crypto.csproj
+++ b/crypto/crypto.csproj
@@ -4604,6 +4604,11 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\crypto\tls\TlsEccUtilities.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\crypto\tls\TlsECDheKeyExchange.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
diff --git a/crypto/src/crypto/tls/CertChainType.cs b/crypto/src/crypto/tls/CertChainType.cs
index b526a79a7..cbb183441 100644
--- a/crypto/src/crypto/tls/CertChainType.cs
+++ b/crypto/src/crypto/tls/CertChainType.cs
@@ -7,10 +7,10 @@ namespace Org.BouncyCastle.Crypto.Tls
      */
     public abstract class CertChainType
     {
-        public const short individual_certs = 0;
-        public const short pkipath = 1;
+        public const byte individual_certs = 0;
+        public const byte pkipath = 1;
 
-        public static bool IsValid(short certChainType)
+        public static bool IsValid(byte certChainType)
         {
             return certChainType >= individual_certs && certChainType <= pkipath;
         }
diff --git a/crypto/src/crypto/tls/CertificateStatusType.cs b/crypto/src/crypto/tls/CertificateStatusType.cs
index 222d31635..54b741b42 100644
--- a/crypto/src/crypto/tls/CertificateStatusType.cs
+++ b/crypto/src/crypto/tls/CertificateStatusType.cs
@@ -7,6 +7,6 @@ namespace Org.BouncyCastle.Crypto.Tls
         /*
          *  RFC 3546 3.6
          */
-        public const short ocsp = 1;
+        public const byte ocsp = 1;
     }
 }
diff --git a/crypto/src/crypto/tls/ChangeCipherSpec.cs b/crypto/src/crypto/tls/ChangeCipherSpec.cs
index 2ef4c3b34..323de9162 100644
--- a/crypto/src/crypto/tls/ChangeCipherSpec.cs
+++ b/crypto/src/crypto/tls/ChangeCipherSpec.cs
@@ -4,6 +4,6 @@ namespace Org.BouncyCastle.Crypto.Tls
 {
     public abstract class ChangeCipherSpec
     {
-        public const short change_cipher_spec = 1;
+        public const byte change_cipher_spec = 1;
     }
 }
diff --git a/crypto/src/crypto/tls/ClientAuthenticationType.cs b/crypto/src/crypto/tls/ClientAuthenticationType.cs
index 51e6e5005..dd248f3df 100644
--- a/crypto/src/crypto/tls/ClientAuthenticationType.cs
+++ b/crypto/src/crypto/tls/ClientAuthenticationType.cs
@@ -7,8 +7,8 @@ namespace Org.BouncyCastle.Crypto.Tls
         /*
          * RFC 5077 4
          */
-        public const short anonymous = 0;
-        public const short certificate_based = 1;
-        public const short psk = 2;
+        public const byte anonymous = 0;
+        public const byte certificate_based = 1;
+        public const byte psk = 2;
     }
 }
diff --git a/crypto/src/crypto/tls/ECBasisType.cs b/crypto/src/crypto/tls/ECBasisType.cs
index b7c9c6bd7..5416e17c0 100644
--- a/crypto/src/crypto/tls/ECBasisType.cs
+++ b/crypto/src/crypto/tls/ECBasisType.cs
@@ -5,10 +5,10 @@ namespace Org.BouncyCastle.Crypto.Tls
     /// <summary>RFC 4492 5.4. (Errata ID: 2389)</summary>
     public abstract class ECBasisType
     {
-        public const short ec_basis_trinomial = 1;
-        public const short ec_basis_pentanomial = 2;
+        public const byte ec_basis_trinomial = 1;
+        public const byte ec_basis_pentanomial = 2;
 
-        public static bool IsValid(short ecBasisType)
+        public static bool IsValid(byte ecBasisType)
         {
             return ecBasisType >= ec_basis_trinomial && ecBasisType <= ec_basis_pentanomial;
         }
diff --git a/crypto/src/crypto/tls/HeartbeatMessageType.cs b/crypto/src/crypto/tls/HeartbeatMessageType.cs
index 9e3ad213c..57a4b86be 100644
--- a/crypto/src/crypto/tls/HeartbeatMessageType.cs
+++ b/crypto/src/crypto/tls/HeartbeatMessageType.cs
@@ -7,10 +7,10 @@ namespace Org.BouncyCastle.Crypto.Tls
      */
     public abstract class HeartbeatMessageType
     {
-        public const short heartbeat_request = 1;
-        public const short heartbeat_response = 2;
+        public const byte heartbeat_request = 1;
+        public const byte heartbeat_response = 2;
 
-        public static bool IsValid(short heartbeatMessageType)
+        public static bool IsValid(byte heartbeatMessageType)
         {
             return heartbeatMessageType >= heartbeat_request && heartbeatMessageType <= heartbeat_response;
         }
diff --git a/crypto/src/crypto/tls/HeartbeatMode.cs b/crypto/src/crypto/tls/HeartbeatMode.cs
index 0968f6e10..f1570a84d 100644
--- a/crypto/src/crypto/tls/HeartbeatMode.cs
+++ b/crypto/src/crypto/tls/HeartbeatMode.cs
@@ -7,10 +7,10 @@ namespace Org.BouncyCastle.Crypto.Tls
      */
     public abstract class HeartbeatMode
     {
-        public const short peer_allowed_to_send = 1;
-        public const short peer_not_allowed_to_send = 2;
+        public const byte peer_allowed_to_send = 1;
+        public const byte peer_not_allowed_to_send = 2;
 
-        public static bool IsValid(short heartbeatMode)
+        public static bool IsValid(byte heartbeatMode)
         {
             return heartbeatMode >= peer_allowed_to_send && heartbeatMode <= peer_not_allowed_to_send;
         }
diff --git a/crypto/src/crypto/tls/MaxFragmentLength.cs b/crypto/src/crypto/tls/MaxFragmentLength.cs
index adb6d129c..5b10b35dd 100644
--- a/crypto/src/crypto/tls/MaxFragmentLength.cs
+++ b/crypto/src/crypto/tls/MaxFragmentLength.cs
@@ -7,12 +7,12 @@ namespace Org.BouncyCastle.Crypto.Tls
         /*
          * RFC 3546 3.2.
          */
-        public const short pow2_9 = 1;
-        public const short pow2_10 = 2;
-        public const short pow2_11 = 3;
-        public const short pow2_12 = 4;
+        public const byte pow2_9 = 1;
+        public const byte pow2_10 = 2;
+        public const byte pow2_11 = 3;
+        public const byte pow2_12 = 4;
 
-        public static bool IsValid(short maxFragmentLength)
+        public static bool IsValid(byte maxFragmentLength)
         {
             return maxFragmentLength >= pow2_9 && maxFragmentLength <= pow2_12;
         }
diff --git a/crypto/src/crypto/tls/NameType.cs b/crypto/src/crypto/tls/NameType.cs
index ffcb639d0..25f6046fc 100644
--- a/crypto/src/crypto/tls/NameType.cs
+++ b/crypto/src/crypto/tls/NameType.cs
@@ -7,6 +7,6 @@ namespace Org.BouncyCastle.Crypto.Tls
         /*
          * RFC 3546 3.1.
          */
-        public const short host_name = 0;
+        public const byte host_name = 0;
     }
 }
diff --git a/crypto/src/crypto/tls/NamedCurve.cs b/crypto/src/crypto/tls/NamedCurve.cs
index 8ef395069..b8aa0ecde 100644
--- a/crypto/src/crypto/tls/NamedCurve.cs
+++ b/crypto/src/crypto/tls/NamedCurve.cs
@@ -58,7 +58,8 @@ namespace Org.BouncyCastle.Crypto.Tls
 
         public static bool IsValid(int namedCurve)
         {
-            return namedCurve >= sect163k1 && namedCurve <= brainpoolP512r1;
+            return (namedCurve >= sect163k1 && namedCurve <= brainpoolP512r1)
+                || (namedCurve >= arbitrary_explicit_prime_curves && namedCurve <= arbitrary_explicit_char2_curves);
         }
 
         public static bool RefersToASpecificNamedCurve(int namedCurve)
@@ -73,24 +74,4 @@ namespace Org.BouncyCastle.Crypto.Tls
             }
         }
     }
-
-    internal class NamedCurveHelper
-    {
-        internal static ECDomainParameters GetECParameters(int namedCurve)
-        {
-            if (!NamedCurve.IsValid(namedCurve))
-                return null;
-
-            string curveName = namedCurve.ToString();
-
-            // Lazily created the first time a particular curve is accessed
-            X9ECParameters ecP = SecNamedCurves.GetByName(curveName);
-
-            if (ecP == null)
-                return null;
-
-            // It's a bit inefficient to do this conversion every time
-            return new ECDomainParameters(ecP.Curve, ecP.G, ecP.N, ecP.H, ecP.GetSeed());
-        }
-    }
 }
diff --git a/crypto/src/crypto/tls/TlsECDheKeyExchange.cs b/crypto/src/crypto/tls/TlsECDheKeyExchange.cs
index a671ebfbe..a36bff75b 100644
--- a/crypto/src/crypto/tls/TlsECDheKeyExchange.cs
+++ b/crypto/src/crypto/tls/TlsECDheKeyExchange.cs
@@ -40,7 +40,7 @@ namespace Org.BouncyCastle.Crypto.Tls
 
                 // TODO Check namedCurve is one we offered?
 
-                curve_params = NamedCurveHelper.GetECParameters(namedCurve);
+                curve_params = TlsEccUtilities.GetParametersForNamedCurve(namedCurve);
             }
             else
             {
diff --git a/crypto/src/crypto/tls/TlsEccUtilities.cs b/crypto/src/crypto/tls/TlsEccUtilities.cs
new file mode 100644
index 000000000..a306fdb45
--- /dev/null
+++ b/crypto/src/crypto/tls/TlsEccUtilities.cs
@@ -0,0 +1,644 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Asn1.X9;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Agreement;
+using Org.BouncyCastle.Crypto.EC;
+using Org.BouncyCastle.Crypto.Generators;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Math.EC;
+using Org.BouncyCastle.Math.Field;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+    public class TlsEccUtilities
+    {
+        private static readonly string[] curveNames = new string[] { "sect163k1", "sect163r1", "sect163r2", "sect193r1",
+            "sect193r2", "sect233k1", "sect233r1", "sect239k1", "sect283k1", "sect283r1", "sect409k1", "sect409r1",
+            "sect571k1", "sect571r1", "secp160k1", "secp160r1", "secp160r2", "secp192k1", "secp192r1", "secp224k1",
+            "secp224r1", "secp256k1", "secp256r1", "secp384r1", "secp521r1",
+            "brainpoolP256r1", "brainpoolP384r1", "brainpoolP512r1"};
+
+        public static void AddSupportedEllipticCurvesExtension(Hashtable extensions, int[] namedCurves)
+        {
+            extensions[ExtensionType.elliptic_curves] = CreateSupportedEllipticCurvesExtension(namedCurves);
+        }
+
+        public static void AddSupportedPointFormatsExtension(Hashtable extensions, byte[] ecPointFormats)
+        {
+            extensions[ExtensionType.ec_point_formats] = CreateSupportedPointFormatsExtension(ecPointFormats);
+        }
+
+        public static int[] GetSupportedEllipticCurvesExtension(Hashtable extensions)
+        {
+            byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.elliptic_curves);
+            return extensionData == null ? null : ReadSupportedEllipticCurvesExtension(extensionData);
+        }
+
+        public static byte[] GetSupportedPointFormatsExtension(Hashtable extensions)
+        {
+            byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.ec_point_formats);
+            return extensionData == null ? null : ReadSupportedPointFormatsExtension(extensionData);
+        }
+
+        public static byte[] CreateSupportedEllipticCurvesExtension(int[] namedCurves)
+        {
+            if (namedCurves == null || namedCurves.Length < 1)
+                throw new TlsFatalAlert(AlertDescription.internal_error);
+
+            return TlsUtilities.EncodeUint16ArrayWithUint16Length(namedCurves);
+        }
+
+        public static byte[] CreateSupportedPointFormatsExtension(byte[] ecPointFormats)
+        {
+            if (ecPointFormats == null || !Arrays.Contains(ecPointFormats, ECPointFormat.uncompressed))
+            {
+                /*
+                 * RFC 4492 5.1. If the Supported Point Formats Extension is indeed sent, it MUST
+                 * contain the value 0 (uncompressed) as one of the items in the list of point formats.
+                 */
+
+                // NOTE: We add it at the end (lowest preference)
+                ecPointFormats = Arrays.Append(ecPointFormats, ECPointFormat.uncompressed);
+            }
+
+            return TlsUtilities.EncodeUint8ArrayWithUint8Length(ecPointFormats);
+        }
+
+        public static int[] ReadSupportedEllipticCurvesExtension(byte[] extensionData)
+        {
+            if (extensionData == null)
+                throw new ArgumentNullException("extensionData");
+
+            MemoryStream buf = new MemoryStream(extensionData, false);
+
+            int length = TlsUtilities.ReadUint16(buf);
+            if (length < 2 || (length & 1) != 0)
+                throw new TlsFatalAlert(AlertDescription.decode_error);
+
+            int[] namedCurves = TlsUtilities.ReadUint16Array(length / 2, buf);
+
+            TlsProtocolHandler.AssertEmpty(buf);
+
+            return namedCurves;
+        }
+
+        public static byte[] ReadSupportedPointFormatsExtension(byte[] extensionData)
+        {
+            if (extensionData == null)
+                throw new ArgumentNullException("extensionData");
+
+            MemoryStream buf = new MemoryStream(extensionData, false);
+
+            byte length = TlsUtilities.ReadUint8(buf);
+            if (length < 1)
+                throw new TlsFatalAlert(AlertDescription.decode_error);
+
+            byte[] ecPointFormats = TlsUtilities.ReadUint8Array(length, buf);
+
+            TlsProtocolHandler.AssertEmpty(buf);
+
+            if (!Arrays.Contains(ecPointFormats, ECPointFormat.uncompressed))
+            {
+                /*
+                 * RFC 4492 5.1. If the Supported Point Formats Extension is indeed sent, it MUST
+                 * contain the value 0 (uncompressed) as one of the items in the list of point formats.
+                 */
+                throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+            }
+
+            return ecPointFormats;
+        }
+
+        public static string GetNameOfNamedCurve(int namedCurve)
+        {
+            return IsSupportedNamedCurve(namedCurve) ? curveNames[namedCurve - 1] : null;
+        }
+
+        public static ECDomainParameters GetParametersForNamedCurve(int namedCurve)
+        {
+            string curveName = GetNameOfNamedCurve(namedCurve);
+            if (curveName == null)
+                return null;
+
+            // Parameters are lazily created the first time a particular curve is accessed
+
+            X9ECParameters ecP = CustomNamedCurves.GetByName(curveName);
+            if (ecP == null)
+            {
+                ecP = ECNamedCurveTable.GetByName(curveName);
+                if (ecP == null)
+                    return null;
+            }
+
+            // It's a bit inefficient to do this conversion every time
+            return new ECDomainParameters(ecP.Curve, ecP.G, ecP.N, ecP.H, ecP.GetSeed());
+        }
+
+        public static bool HasAnySupportedNamedCurves()
+        {
+            return curveNames.Length > 0;
+        }
+
+        public static bool ContainsEccCipherSuites(int[] cipherSuites)
+        {
+            for (int i = 0; i < cipherSuites.Length; ++i)
+            {
+                if (IsEccCipherSuite(cipherSuites[i]))
+                    return true;
+            }
+            return false;
+        }
+
+        public static bool IsEccCipherSuite(int cipherSuite)
+        {
+            switch (cipherSuite)
+            {
+            /*
+             * RFC 4492
+             */
+            case CipherSuite.TLS_ECDH_ECDSA_WITH_NULL_SHA:
+            case CipherSuite.TLS_ECDH_ECDSA_WITH_RC4_128_SHA:
+            case CipherSuite.TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA:
+            case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA:
+            case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA:
+            case CipherSuite.TLS_ECDHE_ECDSA_WITH_NULL_SHA:
+            case CipherSuite.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA:
+            case CipherSuite.TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA:
+            case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA:
+            case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA:
+            case CipherSuite.TLS_ECDH_RSA_WITH_NULL_SHA:
+            case CipherSuite.TLS_ECDH_RSA_WITH_RC4_128_SHA:
+            case CipherSuite.TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA:
+            case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA:
+            case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA:
+            case CipherSuite.TLS_ECDHE_RSA_WITH_NULL_SHA:
+            case CipherSuite.TLS_ECDHE_RSA_WITH_RC4_128_SHA:
+            case CipherSuite.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA:
+            case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA:
+            case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
+            case CipherSuite.TLS_ECDH_anon_WITH_NULL_SHA:
+            case CipherSuite.TLS_ECDH_anon_WITH_RC4_128_SHA:
+            case CipherSuite.TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA:
+            case CipherSuite.TLS_ECDH_anon_WITH_AES_128_CBC_SHA:
+            case CipherSuite.TLS_ECDH_anon_WITH_AES_256_CBC_SHA:
+
+            /*
+             * RFC 5289
+             */
+            case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256:
+            case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384:
+            case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256:
+            case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384:
+            case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256:
+            case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384:
+            case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256:
+            case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384:
+            case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:
+            case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384:
+            case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256:
+            case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384:
+            case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:
+            case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384:
+            case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256:
+            case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384:
+
+            /*
+             * RFC 5489
+             */
+            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_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:
+
+            ///*
+            // * RFC 6367
+            // */
+            //case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256:
+            //case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384:
+            //case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256:
+            //case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384:
+            //case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+            //case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384:
+            //case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+            //case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384:
+
+            //case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256:
+            //case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384:
+            //case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256:
+            //case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384:
+            //case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+            //case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+            //case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+            //case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+
+            //case CipherSuite.TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256:
+            //case CipherSuite.TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384:
+
+            ///*
+            // * draft-agl-tls-chacha20poly1305-04
+            // */
+            //case CipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256:
+            //case CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256:
+
+            ///*
+            // * draft-josefsson-salsa20-tls-04 
+            // */
+            //case CipherSuite.TLS_ECDHE_ECDSA_WITH_ESTREAM_SALSA20_SHA1:
+            //case CipherSuite.TLS_ECDHE_ECDSA_WITH_SALSA20_SHA1:
+            //case CipherSuite.TLS_ECDHE_PSK_WITH_ESTREAM_SALSA20_SHA1:
+            //case CipherSuite.TLS_ECDHE_PSK_WITH_SALSA20_SHA1:
+            //case CipherSuite.TLS_ECDHE_RSA_WITH_ESTREAM_SALSA20_SHA1:
+            //case CipherSuite.TLS_ECDHE_RSA_WITH_SALSA20_SHA1:
+
+                return true;
+
+            default:
+                return false;
+            }
+        }
+
+        public static bool AreOnSameCurve(ECDomainParameters a, ECDomainParameters b)
+        {
+            // TODO Move to ECDomainParameters.Equals() or other utility method?
+            return a.Curve.Equals(b.Curve) && a.G.Equals(b.G) && a.N.Equals(b.N) && a.H.Equals(b.H);
+        }
+
+        public static bool IsSupportedNamedCurve(int namedCurve)
+        {
+            return (namedCurve > 0 && namedCurve <= curveNames.Length);
+        }
+
+        public static bool IsCompressionPreferred(byte[] ecPointFormats, byte compressionFormat)
+        {
+            if (ecPointFormats == null)
+                return false;
+
+            for (int i = 0; i < ecPointFormats.Length; ++i)
+            {
+                byte ecPointFormat = ecPointFormats[i];
+                if (ecPointFormat == ECPointFormat.uncompressed)
+                    return false;
+                if (ecPointFormat == compressionFormat)
+                    return true;
+            }
+            return false;
+        }
+
+        public static byte[] SerializeECFieldElement(int fieldSize, BigInteger x)
+        {
+            return BigIntegers.AsUnsignedByteArray((fieldSize + 7) / 8, x);
+        }
+
+        public static byte[] SerializeECPoint(byte[] ecPointFormats, ECPoint point)
+        {
+            ECCurve curve = point.Curve;
+
+            /*
+             * RFC 4492 5.7. ...an elliptic curve point in uncompressed or compressed format. Here, the
+             * format MUST conform to what the server has requested through a Supported Point Formats
+             * Extension if this extension was used, and MUST be uncompressed if this extension was not
+             * used.
+             */
+            bool compressed = false;
+            if (ECAlgorithms.IsFpCurve(curve))
+            {
+                compressed = IsCompressionPreferred(ecPointFormats, ECPointFormat.ansiX962_compressed_prime);
+            }
+            else if (ECAlgorithms.IsF2mCurve(curve))
+            {
+                compressed = IsCompressionPreferred(ecPointFormats, ECPointFormat.ansiX962_compressed_char2);
+            }
+            return point.GetEncoded(compressed);
+        }
+
+        public static byte[] SerializeECPublicKey(byte[] ecPointFormats, ECPublicKeyParameters keyParameters)
+        {
+            return SerializeECPoint(ecPointFormats, keyParameters.Q);
+        }
+
+        public static BigInteger DeserializeECFieldElement(int fieldSize, byte[] encoding)
+        {
+            int requiredLength = (fieldSize + 7) / 8;
+            if (encoding.Length != requiredLength)
+                throw new TlsFatalAlert(AlertDescription.decode_error);
+            return new BigInteger(1, encoding);
+        }
+
+        public static ECPoint DeserializeECPoint(byte[] ecPointFormats, ECCurve curve, byte[] encoding)
+        {
+            if (encoding == null || encoding.Length < 1)
+                throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+
+            byte actualFormat;
+            switch (encoding[0])
+            {
+            case 0x02: // compressed
+            case 0x03: // compressed
+            {
+                if (ECAlgorithms.IsF2mCurve(curve))
+                {
+                    actualFormat = ECPointFormat.ansiX962_compressed_char2;
+                }
+                else if (ECAlgorithms.IsFpCurve(curve))
+                {
+                    actualFormat = ECPointFormat.ansiX962_compressed_prime;
+                }
+                else
+                {
+                    throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+                }
+                break;
+            }
+            case 0x04: // uncompressed
+            {
+                actualFormat = ECPointFormat.uncompressed;
+                break;
+            }
+            case 0x00: // infinity
+            case 0x06: // hybrid
+            case 0x07: // hybrid
+            default:
+                throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+            }
+
+            if (!Arrays.Contains(ecPointFormats, actualFormat))
+                throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+
+            return curve.DecodePoint(encoding);
+        }
+
+        public static ECPublicKeyParameters DeserializeECPublicKey(byte[] ecPointFormats, ECDomainParameters curve_params,
+            byte[] encoding)
+        {
+            try
+            {
+                ECPoint Y = DeserializeECPoint(ecPointFormats, curve_params.Curve, encoding);
+                return new ECPublicKeyParameters(Y, curve_params);
+            }
+            catch (Exception)
+            {
+                throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+            }
+        }
+
+        public static byte[] CalculateECDHBasicAgreement(ECPublicKeyParameters publicKey, ECPrivateKeyParameters privateKey)
+        {
+            ECDHBasicAgreement basicAgreement = new ECDHBasicAgreement();
+            basicAgreement.Init(privateKey);
+            BigInteger agreementValue = basicAgreement.CalculateAgreement(publicKey);
+
+            /*
+             * RFC 4492 5.10. Note that this octet string (Z in IEEE 1363 terminology) as output by
+             * FE2OSP, the Field Element to Octet String Conversion Primitive, has constant length for
+             * any given field; leading zeros found in this octet string MUST NOT be truncated.
+             */
+            return BigIntegers.AsUnsignedByteArray(basicAgreement.GetFieldSize(), agreementValue);
+        }
+
+        public static AsymmetricCipherKeyPair GenerateECKeyPair(SecureRandom random, ECDomainParameters ecParams)
+        {
+            ECKeyPairGenerator keyPairGenerator = new ECKeyPairGenerator();
+            keyPairGenerator.Init(new ECKeyGenerationParameters(ecParams, random));
+            return keyPairGenerator.GenerateKeyPair();
+        }
+
+        public static ECPrivateKeyParameters GenerateEphemeralClientKeyExchange(SecureRandom random, byte[] ecPointFormats,
+            ECDomainParameters ecParams, Stream output)
+        {
+            AsymmetricCipherKeyPair kp = TlsEccUtilities.GenerateECKeyPair(random, ecParams);
+
+            ECPublicKeyParameters ecPublicKey = (ECPublicKeyParameters)kp.Public;
+            WriteECPoint(ecPointFormats, ecPublicKey.Q, output);
+
+            return (ECPrivateKeyParameters)kp.Private;
+        }
+
+        public static ECPublicKeyParameters ValidateECPublicKey(ECPublicKeyParameters key)
+        {
+            // TODO Check RFC 4492 for validation
+            return key;
+        }
+
+        public static int ReadECExponent(int fieldSize, Stream input)
+        {
+            BigInteger K = ReadECParameter(input);
+            if (K.BitLength < 32)
+            {
+                int k = K.IntValue;
+                if (k > 0 && k < fieldSize)
+                {
+                    return k;
+                }
+            }
+            throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+        }
+
+        public static BigInteger ReadECFieldElement(int fieldSize, Stream input)
+        {
+            return DeserializeECFieldElement(fieldSize, TlsUtilities.ReadOpaque8(input));
+        }
+
+        public static BigInteger ReadECParameter(Stream input)
+        {
+            // TODO Are leading zeroes okay here?
+            return new BigInteger(1, TlsUtilities.ReadOpaque8(input));
+        }
+
+        public static ECDomainParameters ReadECParameters(int[] namedCurves, byte[] ecPointFormats, Stream input)
+        {
+            try
+            {
+                byte curveType = TlsUtilities.ReadUint8(input);
+
+                switch (curveType)
+                {
+                case ECCurveType.explicit_prime:
+                {
+                    CheckNamedCurve(namedCurves, NamedCurve.arbitrary_explicit_prime_curves);
+
+                    BigInteger prime_p = ReadECParameter(input);
+                    BigInteger a = ReadECFieldElement(prime_p.BitLength, input);
+                    BigInteger b = ReadECFieldElement(prime_p.BitLength, input);
+                    byte[] baseEncoding = TlsUtilities.ReadOpaque8(input);
+                    BigInteger order = ReadECParameter(input);
+                    BigInteger cofactor = ReadECParameter(input);
+                    ECCurve curve = new FpCurve(prime_p, a, b, order, cofactor);
+                    ECPoint basePoint = DeserializeECPoint(ecPointFormats, curve, baseEncoding);
+                    return new ECDomainParameters(curve, basePoint, order, cofactor);
+                }
+                case ECCurveType.explicit_char2:
+                {
+                    CheckNamedCurve(namedCurves, NamedCurve.arbitrary_explicit_char2_curves);
+
+                    int m = TlsUtilities.ReadUint16(input);
+                    byte basis = TlsUtilities.ReadUint8(input);
+                    if (!ECBasisType.IsValid(basis))
+                        throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+
+                    int k1 = ReadECExponent(m, input), k2 = -1, k3 = -1;
+                    if (basis == ECBasisType.ec_basis_pentanomial)
+                    {
+                        k2 = ReadECExponent(m, input);
+                        k3 = ReadECExponent(m, input);
+                    }
+
+                    BigInteger a = ReadECFieldElement(m, input);
+                    BigInteger b = ReadECFieldElement(m, input);
+                    byte[] baseEncoding = TlsUtilities.ReadOpaque8(input);
+                    BigInteger order = ReadECParameter(input);
+                    BigInteger cofactor = ReadECParameter(input);
+
+                    ECCurve curve = (basis == ECBasisType.ec_basis_pentanomial)
+                        ? new F2mCurve(m, k1, k2, k3, a, b, order, cofactor)
+                        : new F2mCurve(m, k1, a, b, order, cofactor);
+
+                    ECPoint basePoint = DeserializeECPoint(ecPointFormats, curve, baseEncoding);
+
+                    return new ECDomainParameters(curve, basePoint, order, cofactor);
+                }
+                case ECCurveType.named_curve:
+                {
+                    int namedCurve = TlsUtilities.ReadUint16(input);
+                    if (!NamedCurve.RefersToASpecificNamedCurve(namedCurve))
+                    {
+                        /*
+                         * RFC 4492 5.4. All those values of NamedCurve are allowed that refer to a
+                         * specific curve. Values of NamedCurve that indicate support for a class of
+                         * explicitly defined curves are not allowed here [...].
+                         */
+                        throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+                    }
+
+                    CheckNamedCurve(namedCurves, namedCurve);
+
+                    return GetParametersForNamedCurve(namedCurve);
+                }
+                default:
+                    throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+                }
+            }
+            catch (Exception)
+            {
+                throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+            }
+        }
+
+        private static void CheckNamedCurve(int[] namedCurves, int namedCurve)
+        {
+            if (namedCurves != null && !Arrays.Contains(namedCurves, namedCurve))
+            {
+                /*
+                 * RFC 4492 4. [...] servers MUST NOT negotiate the use of an ECC cipher suite
+                 * unless they can complete the handshake while respecting the choice of curves
+                 * and compression techniques specified by the client.
+                 */
+                throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+            }
+        }
+
+        public static void WriteECExponent(int k, Stream output)
+        {
+            BigInteger K = BigInteger.ValueOf(k);
+            WriteECParameter(K, output);
+        }
+
+        public static void WriteECFieldElement(ECFieldElement x, Stream output)
+        {
+            TlsUtilities.WriteOpaque8(x.GetEncoded(), output);
+        }
+
+        public static void WriteECFieldElement(int fieldSize, BigInteger x, Stream output)
+        {
+            TlsUtilities.WriteOpaque8(SerializeECFieldElement(fieldSize, x), output);
+        }
+
+        public static void WriteECParameter(BigInteger x, Stream output)
+        {
+            TlsUtilities.WriteOpaque8(BigIntegers.AsUnsignedByteArray(x), output);
+        }
+
+        public static void WriteExplicitECParameters(byte[] ecPointFormats, ECDomainParameters ecParameters,
+            Stream output)
+        {
+            ECCurve curve = ecParameters.Curve;
+
+            if (ECAlgorithms.IsFpCurve(curve))
+            {
+                TlsUtilities.WriteUint8(ECCurveType.explicit_prime, output);
+
+                WriteECParameter(curve.Field.Characteristic, output);
+            }
+            else if (ECAlgorithms.IsF2mCurve(curve))
+            {
+                IPolynomialExtensionField field = (IPolynomialExtensionField)curve.Field;
+                int[] exponents = field.MinimalPolynomial.GetExponentsPresent();
+
+                TlsUtilities.WriteUint8(ECCurveType.explicit_char2, output);
+
+                int m = exponents[exponents.Length - 1];
+                TlsUtilities.CheckUint16(m);
+                TlsUtilities.WriteUint16(m, output);
+
+                if (exponents.Length == 3)
+                {
+                    TlsUtilities.WriteUint8(ECBasisType.ec_basis_trinomial, output);
+                    WriteECExponent(exponents[1], output);
+                }
+                else if (exponents.Length == 5)
+                {
+                    TlsUtilities.WriteUint8(ECBasisType.ec_basis_pentanomial, output);
+                    WriteECExponent(exponents[1], output);
+                    WriteECExponent(exponents[2], output);
+                    WriteECExponent(exponents[3], output);
+                }
+                else
+                {
+                    throw new ArgumentException("Only trinomial and pentomial curves are supported");
+                }
+            }
+            else
+            {
+                throw new ArgumentException("'ecParameters' not a known curve type");
+            }
+
+            WriteECFieldElement(curve.A, output);
+            WriteECFieldElement(curve.B, output);
+            TlsUtilities.WriteOpaque8(SerializeECPoint(ecPointFormats, ecParameters.G), output);
+            WriteECParameter(ecParameters.N, output);
+            WriteECParameter(ecParameters.H, output);
+        }
+
+        public static void WriteECPoint(byte[] ecPointFormats, ECPoint point, Stream output)
+        {
+            TlsUtilities.WriteOpaque8(SerializeECPoint(ecPointFormats, point), output);
+        }
+
+        public static void WriteNamedECParameters(int namedCurve, Stream output)
+        {
+            if (!NamedCurve.RefersToASpecificNamedCurve(namedCurve))
+            {
+                /*
+                 * RFC 4492 5.4. All those values of NamedCurve are allowed that refer to a specific
+                 * curve. Values of NamedCurve that indicate support for a class of explicitly defined
+                 * curves are not allowed here [...].
+                 */
+                throw new TlsFatalAlert(AlertDescription.internal_error);
+            }
+
+            TlsUtilities.WriteUint8(ECCurveType.named_curve, output);
+            TlsUtilities.CheckUint16(namedCurve);
+            TlsUtilities.WriteUint16(namedCurve, output);
+        }
+    }
+}
diff --git a/crypto/src/crypto/tls/TlsProtocolHandler.cs b/crypto/src/crypto/tls/TlsProtocolHandler.cs
index 4707df3b5..d40e179aa 100644
--- a/crypto/src/crypto/tls/TlsProtocolHandler.cs
+++ b/crypto/src/crypto/tls/TlsProtocolHandler.cs
@@ -1166,7 +1166,7 @@ namespace Org.BouncyCastle.Crypto.Tls
         * @param is The Stream to check.
         * @throws IOException If is is not empty.
         */
-        internal void AssertEmpty(
+        protected internal static void AssertEmpty(
             MemoryStream inStr)
         {
             if (inStr.Position < inStr.Length)
diff --git a/crypto/src/crypto/tls/TlsUtilities.cs b/crypto/src/crypto/tls/TlsUtilities.cs
index 2309fc3da..644d079c1 100644
--- a/crypto/src/crypto/tls/TlsUtilities.cs
+++ b/crypto/src/crypto/tls/TlsUtilities.cs
@@ -52,43 +52,43 @@ namespace Org.BouncyCastle.Crypto.Tls
             return (i & 0xFFFFFF) == i;
         }
 
-        internal static void WriteUint8(byte i, Stream os)
+        public static void WriteUint8(byte i, Stream os)
         {
             os.WriteByte(i);
         }
 
-        internal static void WriteUint8(byte i, byte[] buf, int offset)
+        public static void WriteUint8(byte i, byte[] buf, int offset)
         {
             buf[offset] = i;
         }
 
-        internal static void WriteUint16(int i, Stream os)
+        public static void WriteUint16(int i, Stream os)
         {
             os.WriteByte((byte)(i >> 8));
             os.WriteByte((byte)i);
         }
 
-        internal static void WriteUint16(int i, byte[] buf, int offset)
+        public static void WriteUint16(int i, byte[] buf, int offset)
         {
             buf[offset] = (byte)(i >> 8);
             buf[offset + 1] = (byte)i;
         }
 
-        internal static void WriteUint24(int i, Stream os)
+        public static void WriteUint24(int i, Stream os)
         {
             os.WriteByte((byte)(i >> 16));
             os.WriteByte((byte)(i >> 8));
             os.WriteByte((byte)i);
         }
 
-        internal static void WriteUint24(int i, byte[] buf, int offset)
+        public static void WriteUint24(int i, byte[] buf, int offset)
         {
             buf[offset] = (byte)(i >> 16);
             buf[offset + 1] = (byte)(i >> 8);
             buf[offset + 2] = (byte)(i);
         }
 
-        internal static void WriteUint64(long i, Stream os)
+        public static void WriteUint64(long i, Stream os)
         {
             os.WriteByte((byte)(i >> 56));
             os.WriteByte((byte)(i >> 48));
@@ -100,7 +100,7 @@ namespace Org.BouncyCastle.Crypto.Tls
             os.WriteByte((byte)i);
         }
 
-        internal static void WriteUint64(long i, byte[] buf, int offset)
+        public static void WriteUint64(long i, byte[] buf, int offset)
         {
             buf[offset] = (byte)(i >> 56);
             buf[offset + 1] = (byte)(i >> 48);
@@ -112,29 +112,38 @@ namespace Org.BouncyCastle.Crypto.Tls
             buf[offset + 7] = (byte)(i);
         }
 
-        internal static void WriteOpaque8(byte[] buf, Stream os)
+        public static void WriteOpaque8(byte[] buf, Stream os)
         {
             WriteUint8((byte)buf.Length, os);
             os.Write(buf, 0, buf.Length);
         }
 
-        internal static void WriteOpaque16(byte[] buf, Stream os)
+        public static void WriteOpaque16(byte[] buf, Stream os)
         {
             WriteUint16(buf.Length, os);
             os.Write(buf, 0, buf.Length);
         }
 
-        internal static void WriteOpaque24(byte[] buf, Stream os)
+        public static void WriteOpaque24(byte[] buf, Stream os)
         {
             WriteUint24(buf.Length, os);
             os.Write(buf, 0, buf.Length);
         }
 
-        internal static void WriteUint8Array(byte[] uints, Stream os)
+        public static void WriteUint8Array(byte[] uints, Stream os)
         {
             os.Write(uints, 0, uints.Length);
         }
 
+        public static void WriteUint8Array(byte[] uints, byte[] buf, int offset)
+        {
+            for (int i = 0; i < uints.Length; ++i)
+            {
+                WriteUint8(uints[i], buf, offset);
+                ++offset;
+            }
+        }
+
         public static void WriteUint8ArrayWithUint8Length(byte[] uints, Stream output)
         {
             CheckUint8(uints.Length);
@@ -142,7 +151,14 @@ namespace Org.BouncyCastle.Crypto.Tls
             WriteUint8Array(uints, output);
         }
 
-        internal static void WriteUint16Array(int[] uints, Stream os)
+        public static void WriteUint8ArrayWithUint8Length(byte[] uints, byte[] buf, int offset)
+        {
+            CheckUint8(uints.Length);
+            WriteUint8((byte)uints.Length, buf, offset);
+            WriteUint8Array(uints, buf, offset + 1);
+        }
+
+        public static void WriteUint16Array(int[] uints, Stream os)
         {
             for (int i = 0; i < uints.Length; ++i)
             {
@@ -150,7 +166,53 @@ namespace Org.BouncyCastle.Crypto.Tls
             }
         }
 
-        internal static byte ReadUint8(Stream inStr)
+        public static void WriteUint16Array(int[] uints, byte[] buf, int offset)
+        {
+            for (int i = 0; i < uints.Length; ++i)
+            {
+                WriteUint16(uints[i], buf, offset);
+                offset += 2;
+            }
+        }
+
+        public static void WriteUint16ArrayWithUint16Length(int[] uints, Stream output)
+        {
+            int length = 2 * uints.Length;
+            CheckUint16(length);
+            WriteUint16(length, output);
+            WriteUint16Array(uints, output);
+        }
+
+        public static void WriteUint16ArrayWithUint16Length(int[] uints, byte[] buf, int offset)
+        {
+            int length = 2 * uints.Length;
+            CheckUint16(length);
+            WriteUint16(length, buf, offset);
+            WriteUint16Array(uints, buf, offset + 2);
+        }
+
+        public static byte[] EncodeOpaque8(byte[] buf)
+        {
+            CheckUint8(buf.Length);
+            return Arrays.Prepend(buf, (byte)buf.Length);
+        }
+
+        public static byte[] EncodeUint8ArrayWithUint8Length(byte[] uints)
+        {
+            byte[] result = new byte[1 + uints.Length];
+            WriteUint8ArrayWithUint8Length(uints, result, 0);
+            return result;
+        }
+
+        public static byte[] EncodeUint16ArrayWithUint16Length(int[] uints)
+        {
+            int length = 2 * uints.Length;
+            byte[] result = new byte[2 + length];
+            WriteUint16ArrayWithUint16Length(uints, result, 0);
+            return result;
+        }
+
+        public static byte ReadUint8(Stream inStr)
         {
             int i = inStr.ReadByte();
             if (i < 0)
@@ -160,7 +222,7 @@ namespace Org.BouncyCastle.Crypto.Tls
             return (byte)i;
         }
 
-        internal static int ReadUint16(Stream inStr)
+        public static int ReadUint16(Stream inStr)
         {
             int i1 = inStr.ReadByte();
             int i2 = inStr.ReadByte();
@@ -171,7 +233,7 @@ namespace Org.BouncyCastle.Crypto.Tls
             return i1 << 8 | i2;
         }
 
-        internal static int ReadUint24(Stream inStr)
+        public static int ReadUint24(Stream inStr)
         {
             int i1 = inStr.ReadByte();
             int i2 = inStr.ReadByte();
@@ -193,13 +255,13 @@ namespace Org.BouncyCastle.Crypto.Tls
             return buf;
         }
 
-        internal static void ReadFully(byte[] buf, Stream inStr)
+        public static void ReadFully(byte[] buf, Stream inStr)
         {
             if (Streams.ReadFully(inStr, buf, 0, buf.Length) < buf.Length)
                 throw new EndOfStreamException();
         }
 
-        internal static byte[] ReadOpaque8(Stream inStr)
+        public static byte[] ReadOpaque8(Stream inStr)
         {
             byte length = ReadUint8(inStr);
             byte[] bytes = new byte[length];
@@ -207,7 +269,7 @@ namespace Org.BouncyCastle.Crypto.Tls
             return bytes;
         }
 
-        internal static byte[] ReadOpaque16(Stream inStr)
+        public static byte[] ReadOpaque16(Stream inStr)
         {
             int length = ReadUint16(inStr);
             byte[] bytes = new byte[length];
@@ -221,7 +283,27 @@ namespace Org.BouncyCastle.Crypto.Tls
             return ReadFully(length, input);
         }
 
-        internal static void CheckVersion(byte[] readVersion)
+        public static byte[] ReadUint8Array(int count, Stream input)
+        {
+            byte[] uints = new byte[count];
+            for (int i = 0; i < count; ++i)
+            {
+                uints[i] = ReadUint8(input);
+            }
+            return uints;
+        }
+
+        public static int[] ReadUint16Array(int count, Stream input)
+        {
+            int[] uints = new int[count];
+            for (int i = 0; i < count; ++i)
+            {
+                uints[i] = ReadUint16(input);
+            }
+            return uints;
+        }
+
+        public static void CheckVersion(byte[] readVersion)
         {
             if ((readVersion[0] != 3) || (readVersion[1] != 1))
             {
@@ -229,7 +311,7 @@ namespace Org.BouncyCastle.Crypto.Tls
             }
         }
 
-        internal static void CheckVersion(Stream inStr)
+        public static void CheckVersion(Stream inStr)
         {
             int i1 = inStr.ReadByte();
             int i2 = inStr.ReadByte();
@@ -263,7 +345,7 @@ namespace Org.BouncyCastle.Crypto.Tls
             return result;
         }
 
-        internal static void WriteGmtUnixTime(byte[] buf, int offset)
+        public static void WriteGmtUnixTime(byte[] buf, int offset)
         {
             int t = (int)(DateTimeUtilities.CurrentUnixMs() / 1000L);
             buf[offset] = (byte)(t >> 24);
@@ -272,18 +354,34 @@ namespace Org.BouncyCastle.Crypto.Tls
             buf[offset + 3] = (byte)t;
         }
 
-        internal static void WriteVersion(Stream os)
+        public static void WriteVersion(Stream os)
         {
             os.WriteByte(3);
             os.WriteByte(1);
         }
 
-        internal static void WriteVersion(byte[] buf, int offset)
+        public static void WriteVersion(byte[] buf, int offset)
         {
             buf[offset] = 3;
             buf[offset + 1] = 1;
         }
 
+        public static byte[] GetExtensionData(Hashtable extensions, int extensionType)
+        {
+            return extensions == null ? null : (byte[])extensions[extensionType];
+        }
+
+        public static bool HasExpectedEmptyExtensionData(Hashtable extensions, int extensionType,
+            byte alertDescription)
+        {
+            byte[] extension_data = GetExtensionData(extensions, extensionType);
+            if (extension_data == null)
+                return false;
+            if (extension_data.Length != 0)
+                throw new TlsFatalAlert(alertDescription);
+            return true;
+        }
+
         public static void EncodeSupportedSignatureAlgorithms(IList supportedSignatureAlgorithms, bool allowAnonymous,
             Stream output)
         {
diff --git a/crypto/src/crypto/tls/UserMappingType.cs b/crypto/src/crypto/tls/UserMappingType.cs
index 6e6d40a58..6cff51736 100644
--- a/crypto/src/crypto/tls/UserMappingType.cs
+++ b/crypto/src/crypto/tls/UserMappingType.cs
@@ -8,6 +8,6 @@ namespace Org.BouncyCastle.Crypto.Tls
         /*
          * RFC 4681
          */
-        public const short upn_domain_hint = 64;
+        public const byte upn_domain_hint = 64;
     }
 }
diff --git a/crypto/src/util/Arrays.cs b/crypto/src/util/Arrays.cs
index 37da3940f..e629fcf65 100644
--- a/crypto/src/util/Arrays.cs
+++ b/crypto/src/util/Arrays.cs
@@ -366,6 +366,36 @@ namespace Org.BouncyCastle.Utilities
             return existing;
         }
 
+        public static bool Contains(byte[] a, byte n)
+        {
+            for (int i = 0; i < a.Length; ++i)
+            {
+                if (a[i] == n)
+                    return true;
+            }
+            return false;
+        }
+
+        public static bool Contains(short[] a, short n)
+        {
+            for (int i = 0; i < a.Length; ++i)
+            {
+                if (a[i] == n)
+                    return true;
+            }
+            return false;
+        }
+
+        public static bool Contains(int[] a, int n)
+        {
+            for (int i = 0; i < a.Length; ++i)
+            {
+                if (a[i] == n)
+                    return true;
+            }
+            return false;
+        }
+
         public static void Fill(
             byte[]	buf,
             byte	b)
@@ -384,6 +414,42 @@ namespace Org.BouncyCastle.Utilities
             return result;
         }
 
+        public static byte[] Append(byte[] a, byte b)
+        {
+            if (a == null)
+                return new byte[] { b };
+
+            int length = a.Length;
+            byte[] result = new byte[length + 1];
+            Array.Copy(a, 0, result, 0, length);
+            result[length] = b;
+            return result;
+        }
+
+        public static short[] Append(short[] a, short b)
+        {
+            if (a == null)
+                return new short[] { b };
+
+            int length = a.Length;
+            short[] result = new short[length + 1];
+            Array.Copy(a, 0, result, 0, length);
+            result[length] = b;
+            return result;
+        }
+
+        public static int[] Append(int[] a, int b)
+        {
+            if (a == null)
+                return new int[] { b };
+
+            int length = a.Length;
+            int[] result = new int[length + 1];
+            Array.Copy(a, 0, result, 0, length);
+            result[length] = b;
+            return result;
+        }
+
         public static byte[] Concatenate(byte[] a, byte[] b)
         {
             if (a == null)
@@ -397,6 +463,42 @@ namespace Org.BouncyCastle.Utilities
             return rv;
         }
 
+        public static byte[] Prepend(byte[] a, byte b)
+        {
+            if (a == null)
+                return new byte[] { b };
+
+            int length = a.Length;
+            byte[] result = new byte[length + 1];
+            Array.Copy(a, 0, result, 1, length);
+            result[0] = b;
+            return result;
+        }
+
+        public static short[] Prepend(short[] a, short b)
+        {
+            if (a == null)
+                return new short[] { b };
+
+            int length = a.Length;
+            short[] result = new short[length + 1];
+            Array.Copy(a, 0, result, 1, length);
+            result[0] = b;
+            return result;
+        }
+
+        public static int[] Prepend(int[] a, int b)
+        {
+            if (a == null)
+                return new int[] { b };
+
+            int length = a.Length;
+            int[] result = new int[length + 1];
+            Array.Copy(a, 0, result, 1, length);
+            result[0] = b;
+            return result;
+        }
+
         public static byte[] Reverse(byte[] a)
         {
             if (a == null)