summary refs log tree commit diff
diff options
context:
space:
mode:
authorPeter Dettman <peter.dettman@bouncycastle.org>2018-11-06 16:55:58 +0700
committerPeter Dettman <peter.dettman@bouncycastle.org>2018-11-06 16:55:58 +0700
commitae809eddc05da059ff401787e17fb3e96cd4fe64 (patch)
tree8b38a1e0a42be0647205079c893c25b8621f2b24
parentUpdate versions and release notes for 1.8.4 (diff)
downloadBouncyCastle.NET-ed25519-ae809eddc05da059ff401787e17fb3e96cd4fe64.tar.xz
TLS: Update to RFC 7627 from draft-ietf-tls-session-hash-04
-rw-r--r--crypto/src/crypto/tls/AbstractTlsContext.cs20
-rw-r--r--crypto/src/crypto/tls/AbstractTlsPeer.cs5
-rw-r--r--crypto/src/crypto/tls/DtlsClientProtocol.cs79
-rw-r--r--crypto/src/crypto/tls/DtlsServerProtocol.cs40
-rw-r--r--crypto/src/crypto/tls/ExporterLabel.cs2
-rw-r--r--crypto/src/crypto/tls/SecurityParameters.cs7
-rw-r--r--crypto/src/crypto/tls/SessionParameters.cs21
-rw-r--r--crypto/src/crypto/tls/TlsClientProtocol.cs45
-rw-r--r--crypto/src/crypto/tls/TlsPeer.cs15
-rw-r--r--crypto/src/crypto/tls/TlsProtocol.cs1
-rw-r--r--crypto/src/crypto/tls/TlsServerProtocol.cs26
-rw-r--r--crypto/src/crypto/tls/TlsSessionImpl.cs21
-rw-r--r--crypto/src/crypto/tls/TlsUtilities.cs4
-rw-r--r--crypto/test/src/crypto/tls/test/MockDtlsClient.cs1
-rw-r--r--crypto/test/src/crypto/tls/test/MockTlsClient.cs1
15 files changed, 183 insertions, 105 deletions
diff --git a/crypto/src/crypto/tls/AbstractTlsContext.cs b/crypto/src/crypto/tls/AbstractTlsContext.cs
index ae7efc64d..4c484fe64 100644
--- a/crypto/src/crypto/tls/AbstractTlsContext.cs
+++ b/crypto/src/crypto/tls/AbstractTlsContext.cs
@@ -107,19 +107,21 @@ namespace Org.BouncyCastle.Crypto.Tls
 
         public virtual byte[] ExportKeyingMaterial(string asciiLabel, byte[] context_value, int length)
         {
-            /*
-             * TODO[session-hash]
-             * 
-             * draft-ietf-tls-session-hash-04 5.4. If a client or server chooses to continue with a full
-             * handshake without the extended master secret extension, [..] the client or server MUST
-             * NOT export any key material based on the new master secret for any subsequent
-             * application-level authentication. In particular, it MUST disable [RFC5705] [..].
-             */
-
             if (context_value != null && !TlsUtilities.IsValidUint16(context_value.Length))
                 throw new ArgumentException("must have length less than 2^16 (or be null)", "context_value");
 
             SecurityParameters sp = SecurityParameters;
+            if (!sp.IsExtendedMasterSecret)
+            {
+                /*
+                 * RFC 7627 5.4. If a client or server chooses to continue with a full handshake without
+                 * the extended master secret extension, [..] the client or server MUST NOT export any
+                 * key material based on the new master secret for any subsequent application-level
+                 * authentication. In particular, it MUST disable [RFC5705] [..].
+                 */
+                throw new InvalidOperationException("cannot export keying material without extended_master_secret");
+            }
+
             byte[] cr = sp.ClientRandom, sr = sp.ServerRandom;
 
             int seedLength = cr.Length + sr.Length;
diff --git a/crypto/src/crypto/tls/AbstractTlsPeer.cs b/crypto/src/crypto/tls/AbstractTlsPeer.cs
index 81a53386c..1bbea68c8 100644
--- a/crypto/src/crypto/tls/AbstractTlsPeer.cs
+++ b/crypto/src/crypto/tls/AbstractTlsPeer.cs
@@ -6,6 +6,11 @@ namespace Org.BouncyCastle.Crypto.Tls
     public abstract class AbstractTlsPeer
         :   TlsPeer
     {
+        public virtual bool RequiresExtendedMasterSecret()
+        {
+            return false;
+        }
+
         public virtual bool ShouldUseGmtUnixTime()
         {
             /*
diff --git a/crypto/src/crypto/tls/DtlsClientProtocol.cs b/crypto/src/crypto/tls/DtlsClientProtocol.cs
index ae6e6a573..ce0c4c767 100644
--- a/crypto/src/crypto/tls/DtlsClientProtocol.cs
+++ b/crypto/src/crypto/tls/DtlsClientProtocol.cs
@@ -40,7 +40,7 @@ namespace Org.BouncyCastle.Crypto.Tls
             if (sessionToResume != null && sessionToResume.IsResumable)
             {
                 SessionParameters sessionParameters = sessionToResume.ExportSessionParameters();
-                if (sessionParameters != null)
+                if (sessionParameters != null && sessionParameters.IsExtendedMasterSecret)
                 {
                     state.tlsSession = sessionToResume;
                     state.sessionParameters = sessionParameters;
@@ -356,6 +356,7 @@ namespace Org.BouncyCastle.Crypto.Tls
                 state.sessionParameters = new SessionParameters.Builder()
                     .SetCipherSuite(securityParameters.CipherSuite)
                     .SetCompressionAlgorithm(securityParameters.CompressionAlgorithm)
+                    .SetExtendedMasterSecret(securityParameters.IsExtendedMasterSecret)
                     .SetMasterSecret(securityParameters.MasterSecret)
                     .SetPeerCertificate(serverCertificate)
                     .SetPskIdentity(securityParameters.PskIdentity)
@@ -383,8 +384,6 @@ namespace Org.BouncyCastle.Crypto.Tls
 
         protected virtual byte[] GenerateClientHello(ClientHandshakeState state, TlsClient client)
         {
-            MemoryStream buf = new MemoryStream();
-
             ProtocolVersion client_version = client.ClientVersion;
             if (!client_version.IsDtls)
                 throw new TlsFatalAlert(AlertDescription.internal_error);
@@ -392,10 +391,8 @@ namespace Org.BouncyCastle.Crypto.Tls
             TlsClientContextImpl context = state.clientContext;
 
             context.SetClientVersion(client_version);
-            TlsUtilities.WriteVersion(client_version, buf);
 
             SecurityParameters securityParameters = context.SecurityParameters;
-            buf.Write(securityParameters.ClientRandom, 0, securityParameters.ClientRandom.Length);
 
             // Session ID
             byte[] session_id = TlsUtilities.EmptyBytes;
@@ -407,20 +404,35 @@ namespace Org.BouncyCastle.Crypto.Tls
                     session_id = TlsUtilities.EmptyBytes;
                 }
             }
-            TlsUtilities.WriteOpaque8(session_id, buf);
-
-            // Cookie
-            TlsUtilities.WriteOpaque8(TlsUtilities.EmptyBytes, buf);
 
             bool fallback = client.IsFallback;
 
-            /*
-             * Cipher suites
-             */
             state.offeredCipherSuites = client.GetCipherSuites();
 
-            // Integer -> byte[]
-            state.clientExtensions = client.GetClientExtensions();
+            if (session_id.Length > 0 && state.sessionParameters != null)
+            {
+                if (!state.sessionParameters.IsExtendedMasterSecret
+                    || !Arrays.Contains(state.offeredCipherSuites, state.sessionParameters.CipherSuite)
+                    || CompressionMethod.cls_null != state.sessionParameters.CompressionAlgorithm)
+                {
+                    session_id = TlsUtilities.EmptyBytes;
+                }
+            }
+
+            state.clientExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(client.GetClientExtensions());
+
+            TlsExtensionsUtilities.AddExtendedMasterSecretExtension(state.clientExtensions);
+
+            MemoryStream buf = new MemoryStream();
+
+            TlsUtilities.WriteVersion(client_version, buf);
+
+            buf.Write(securityParameters.ClientRandom, 0, securityParameters.ClientRandom.Length);
+
+            TlsUtilities.WriteOpaque8(session_id, buf);
+
+            // Cookie
+            TlsUtilities.WriteOpaque8(TlsUtilities.EmptyBytes, buf);
 
             // Cipher Suites (and SCSV)
             {
@@ -455,18 +467,9 @@ namespace Org.BouncyCastle.Crypto.Tls
                 TlsUtilities.WriteUint16ArrayWithUint16Length(state.offeredCipherSuites, buf);
             }
 
-            // TODO Add support for compression
-            // Compression methods
-            // state.offeredCompressionMethods = client.getCompressionMethods();
-            state.offeredCompressionMethods = new byte[]{ CompressionMethod.cls_null };
+            TlsUtilities.WriteUint8ArrayWithUint8Length(new byte[]{ CompressionMethod.cls_null }, buf);
 
-            TlsUtilities.WriteUint8ArrayWithUint8Length(state.offeredCompressionMethods, buf);
-
-            // Extensions
-            if (state.clientExtensions != null)
-            {
-                TlsProtocol.WriteExtensions(buf, state.clientExtensions);
-            }
+            TlsProtocol.WriteExtensions(buf, state.clientExtensions);
 
             return buf.ToArray();
         }
@@ -616,7 +619,7 @@ namespace Org.BouncyCastle.Crypto.Tls
             state.client.NotifySelectedCipherSuite(selectedCipherSuite);
 
             byte selectedCompressionMethod = TlsUtilities.ReadUint8(buf);
-            if (!Arrays.Contains(state.offeredCompressionMethods, selectedCompressionMethod))
+            if (CompressionMethod.cls_null != selectedCompressionMethod)
                 throw new TlsFatalAlert(AlertDescription.illegal_parameter);
             state.client.NotifySelectedCompressionMethod(selectedCompressionMethod);
 
@@ -639,6 +642,18 @@ namespace Org.BouncyCastle.Crypto.Tls
             state.serverExtensions = TlsProtocol.ReadExtensions(buf);
 
             /*
+             * RFC 7627 4. Clients and servers SHOULD NOT accept handshakes that do not use the extended
+             * master secret [..]. (and see 5.2, 5.3)
+             */
+            securityParameters.extendedMasterSecret = TlsExtensionsUtilities.HasExtendedMasterSecretExtension(state.serverExtensions);
+
+            if (!securityParameters.IsExtendedMasterSecret
+                && (state.resumedSession || state.client.RequiresExtendedMasterSecret()))
+            {
+                throw new TlsFatalAlert(AlertDescription.handshake_failure);
+            }
+
+            /*
              * RFC 3546 2.2 Note that the extended server hello message is only sent in response to an
              * extended client hello message. However, see RFC 5746 exception below. We always include
              * the SCSV, so an Extended Server Hello is always allowed.
@@ -725,7 +740,7 @@ namespace Org.BouncyCastle.Crypto.Tls
             securityParameters.cipherSuite = selectedCipherSuite;
             securityParameters.compressionAlgorithm = selectedCompressionMethod;
 
-            if (sessionServerExtensions != null)
+            if (sessionServerExtensions != null && sessionServerExtensions.Count > 0)
             {
                 {
                     /*
@@ -740,8 +755,6 @@ namespace Org.BouncyCastle.Crypto.Tls
                     securityParameters.encryptThenMac = serverSentEncryptThenMAC;
                 }
 
-                securityParameters.extendedMasterSecret = TlsExtensionsUtilities.HasExtendedMasterSecretExtension(sessionServerExtensions);
-
                 securityParameters.maxFragmentLength = EvaluateMaxFragmentLengthExtension(state.resumedSession,
                     sessionClientExtensions, sessionServerExtensions, AlertDescription.illegal_parameter);
 
@@ -760,13 +773,6 @@ namespace Org.BouncyCastle.Crypto.Tls
                         AlertDescription.illegal_parameter);
             }
 
-            /*
-             * TODO[session-hash]
-             * 
-             * draft-ietf-tls-session-hash-04 4. Clients and servers SHOULD NOT accept handshakes
-             * that do not use the extended master secret [..]. (and see 5.2, 5.3)
-             */
-
             if (sessionClientExtensions != null)
             {
                 state.client.ProcessServerExtensions(sessionServerExtensions);
@@ -839,7 +845,6 @@ namespace Org.BouncyCastle.Crypto.Tls
             internal SessionParameters sessionParameters = null;
             internal SessionParameters.Builder sessionParametersBuilder = null;
             internal int[] offeredCipherSuites = null;
-            internal byte[] offeredCompressionMethods = null;
             internal IDictionary clientExtensions = null;
             internal IDictionary serverExtensions = null;
             internal byte[] selectedSessionID = null;
diff --git a/crypto/src/crypto/tls/DtlsServerProtocol.cs b/crypto/src/crypto/tls/DtlsServerProtocol.cs
index 3032269d1..1095014cd 100644
--- a/crypto/src/crypto/tls/DtlsServerProtocol.cs
+++ b/crypto/src/crypto/tls/DtlsServerProtocol.cs
@@ -268,6 +268,24 @@ namespace Org.BouncyCastle.Crypto.Tls
 
             handshake.Finish();
 
+            //{
+            //    state.sessionParameters = new SessionParameters.Builder()
+            //        .SetCipherSuite(securityParameters.CipherSuite)
+            //        .SetCompressionAlgorithm(securityParameters.CompressionAlgorithm)
+            //        .SetExtendedMasterSecret(securityParameters.IsExtendedMasterSecret)
+            //        .SetMasterSecret(securityParameters.MasterSecret)
+            //        .SetPeerCertificate(state.clientCertificate)
+            //        .SetPskIdentity(securityParameters.PskIdentity)
+            //        .SetSrpIdentity(securityParameters.SrpIdentity)
+            //        // TODO Consider filtering extensions that aren't relevant to resumed sessions
+            //        .SetServerExtensions(state.serverExtensions)
+            //        .Build();
+
+            //    state.tlsSession = TlsUtilities.ImportSession(state.tlsSession.SessionID, state.sessionParameters);
+
+            //    state.serverContext.SetResumableSession(state.tlsSession);
+            //}
+
             state.server.NotifyHandshakeComplete();
 
             return new DtlsTransport(recordLayer);
@@ -356,7 +374,7 @@ namespace Org.BouncyCastle.Crypto.Tls
             TlsUtilities.WriteUint16(selectedCipherSuite, buf);
             TlsUtilities.WriteUint8(selectedCompressionMethod, buf);
 
-            state.serverExtensions = state.server.GetServerExtensions();
+            state.serverExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(state.server.GetServerExtensions());
 
             /*
              * RFC 5746 3.6. Server Behavior: Initial Handshake
@@ -380,14 +398,12 @@ namespace Org.BouncyCastle.Crypto.Tls
                      * If the secure_renegotiation flag is set to TRUE, the server MUST include an empty
                      * "renegotiation_info" extension in the ServerHello message.
                      */
-                    state.serverExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(state.serverExtensions);
                     state.serverExtensions[ExtensionType.renegotiation_info] = TlsProtocol.CreateRenegotiationInfo(TlsUtilities.EmptyBytes);
                 }
             }
 
-            if (securityParameters.extendedMasterSecret)
+            if (securityParameters.IsExtendedMasterSecret)
             {
-                state.serverExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(state.serverExtensions);
                 TlsExtensionsUtilities.AddExtendedMasterSecretExtension(state.serverExtensions);
             }
 
@@ -397,7 +413,7 @@ namespace Org.BouncyCastle.Crypto.Tls
              * extensions.
              */
 
-            if (state.serverExtensions != null)
+            if (state.serverExtensions.Count > 0)
             {
                 securityParameters.encryptThenMac = TlsExtensionsUtilities.HasEncryptThenMacExtension(state.serverExtensions);
 
@@ -583,12 +599,18 @@ namespace Org.BouncyCastle.Crypto.Tls
             SecurityParameters securityParameters = context.SecurityParameters;
 
             /*
-             * TODO[session-hash]
-             * 
-             * draft-ietf-tls-session-hash-04 4. Clients and servers SHOULD NOT accept handshakes
-             * that do not use the extended master secret [..]. (and see 5.2, 5.3)
+             * TODO[resumption] Check RFC 7627 5.4. for required behaviour 
+             */
+
+            /*
+             * RFC 7627 4. Clients and servers SHOULD NOT accept handshakes that do not use the extended
+             * master secret [..]. (and see 5.2, 5.3)
              */
             securityParameters.extendedMasterSecret = TlsExtensionsUtilities.HasExtendedMasterSecretExtension(state.clientExtensions);
+            if (!securityParameters.IsExtendedMasterSecret && state.server.RequiresExtendedMasterSecret())
+            {
+                throw new TlsFatalAlert(AlertDescription.handshake_failure);
+            }
 
             context.SetClientVersion(client_version);
 
diff --git a/crypto/src/crypto/tls/ExporterLabel.cs b/crypto/src/crypto/tls/ExporterLabel.cs
index 5970769d7..12603f3ff 100644
--- a/crypto/src/crypto/tls/ExporterLabel.cs
+++ b/crypto/src/crypto/tls/ExporterLabel.cs
@@ -30,7 +30,7 @@ namespace Org.BouncyCastle.Crypto.Tls
         public const string dtls_srtp = "EXTRACTOR-dtls_srtp";
 
         /*
-         * draft-ietf-tls-session-hash-04
+         * RFC 7627
          */
         public static readonly string extended_master_secret = "extended master secret";
     }
diff --git a/crypto/src/crypto/tls/SecurityParameters.cs b/crypto/src/crypto/tls/SecurityParameters.cs
index 3b851587d..f3ec7011e 100644
--- a/crypto/src/crypto/tls/SecurityParameters.cs
+++ b/crypto/src/crypto/tls/SecurityParameters.cs
@@ -52,7 +52,7 @@ namespace Org.BouncyCastle.Crypto.Tls
         /**
          * @return {@link CompressionMethod}
          */
-        public byte CompressionAlgorithm
+        public virtual byte CompressionAlgorithm
         {
             get { return compressionAlgorithm; }
         }
@@ -99,5 +99,10 @@ namespace Org.BouncyCastle.Crypto.Tls
         {
             get { return srpIdentity; }
         }
+
+        public virtual bool IsExtendedMasterSecret
+        {
+            get { return extendedMasterSecret; }
+        }
     }
 }
diff --git a/crypto/src/crypto/tls/SessionParameters.cs b/crypto/src/crypto/tls/SessionParameters.cs
index a1eb5f27c..e827172ea 100644
--- a/crypto/src/crypto/tls/SessionParameters.cs
+++ b/crypto/src/crypto/tls/SessionParameters.cs
@@ -17,6 +17,7 @@ namespace Org.BouncyCastle.Crypto.Tls
             private byte[] mPskIdentity = null;
             private byte[] mSrpIdentity = null;
             private byte[] mEncodedServerExtensions = null;
+            private bool mExtendedMasterSecret = false;
 
             public Builder()
             {
@@ -28,7 +29,7 @@ namespace Org.BouncyCastle.Crypto.Tls
                 Validate(this.mCompressionAlgorithm >= 0, "compressionAlgorithm");
                 Validate(this.mMasterSecret != null, "masterSecret");
                 return new SessionParameters(mCipherSuite, (byte)mCompressionAlgorithm, mMasterSecret, mPeerCertificate,
-                    mPskIdentity, mSrpIdentity, mEncodedServerExtensions);
+                    mPskIdentity, mSrpIdentity, mEncodedServerExtensions, mExtendedMasterSecret);
             }
 
             public Builder SetCipherSuite(int cipherSuite)
@@ -43,6 +44,12 @@ namespace Org.BouncyCastle.Crypto.Tls
                 return this;
             }
 
+            public Builder SetExtendedMasterSecret(bool extendedMasterSecret)
+            {
+                this.mExtendedMasterSecret = extendedMasterSecret;
+                return this;
+            }
+
             public Builder SetMasterSecret(byte[] masterSecret)
             {
                 this.mMasterSecret = masterSecret;
@@ -96,9 +103,11 @@ namespace Org.BouncyCastle.Crypto.Tls
         private byte[] mPskIdentity;
         private byte[] mSrpIdentity;
         private byte[] mEncodedServerExtensions;
+        private bool mExtendedMasterSecret;
 
         private SessionParameters(int cipherSuite, byte compressionAlgorithm, byte[] masterSecret,
-            Certificate peerCertificate, byte[] pskIdentity, byte[] srpIdentity, byte[] encodedServerExtensions)
+            Certificate peerCertificate, byte[] pskIdentity, byte[] srpIdentity, byte[] encodedServerExtensions,
+            bool extendedMasterSecret)
         {
             this.mCipherSuite = cipherSuite;
             this.mCompressionAlgorithm = compressionAlgorithm;
@@ -107,6 +116,7 @@ namespace Org.BouncyCastle.Crypto.Tls
             this.mPskIdentity = Arrays.Clone(pskIdentity);
             this.mSrpIdentity = Arrays.Clone(srpIdentity);
             this.mEncodedServerExtensions = encodedServerExtensions;
+            this.mExtendedMasterSecret = extendedMasterSecret;
         }
 
         public void Clear()
@@ -120,7 +130,7 @@ namespace Org.BouncyCastle.Crypto.Tls
         public SessionParameters Copy()
         {
             return new SessionParameters(mCipherSuite, mCompressionAlgorithm, mMasterSecret, mPeerCertificate,
-                mPskIdentity, mSrpIdentity, mEncodedServerExtensions);
+                mPskIdentity, mSrpIdentity, mEncodedServerExtensions, mExtendedMasterSecret);
         }
 
         public int CipherSuite
@@ -133,6 +143,11 @@ namespace Org.BouncyCastle.Crypto.Tls
             get { return mCompressionAlgorithm; }
         }
 
+        public bool IsExtendedMasterSecret
+        {
+            get { return mExtendedMasterSecret; }
+        }
+
         public byte[] MasterSecret
         {
             get { return mMasterSecret; }
diff --git a/crypto/src/crypto/tls/TlsClientProtocol.cs b/crypto/src/crypto/tls/TlsClientProtocol.cs
index 8de76c2f8..17b756693 100644
--- a/crypto/src/crypto/tls/TlsClientProtocol.cs
+++ b/crypto/src/crypto/tls/TlsClientProtocol.cs
@@ -96,7 +96,7 @@ namespace Org.BouncyCastle.Crypto.Tls
             if (sessionToResume != null && sessionToResume.IsResumable)
             {
                 SessionParameters sessionParameters = sessionToResume.ExportSessionParameters();
-                if (sessionParameters != null)
+                if (sessionParameters != null && sessionParameters.IsExtendedMasterSecret)
                 {
                     this.mTlsSession = sessionToResume;
                     this.mSessionParameters = sessionParameters;
@@ -640,7 +640,7 @@ namespace Org.BouncyCastle.Crypto.Tls
             this.mTlsClient.NotifySelectedCompressionMethod(selectedCompressionMethod);
 
             /*
-             * RFC3546 2.2 The extended server hello message format MAY be sent in place of the server
+             * RFC 3546 2.2 The extended server hello message format MAY be sent in place of the server
              * hello message when the client has requested extended functionality via the extended
              * client hello message specified in Section 2.1. ... Note that the extended server hello
              * message is only sent in response to an extended client hello message. This prevents the
@@ -650,6 +650,19 @@ namespace Org.BouncyCastle.Crypto.Tls
             this.mServerExtensions = ReadExtensions(buf);
 
             /*
+             * RFC 7627 4. Clients and servers SHOULD NOT accept handshakes that do not use the extended
+             * master secret [..]. (and see 5.2, 5.3)
+             */
+            this.mSecurityParameters.extendedMasterSecret = !TlsUtilities.IsSsl(mTlsClientContext)
+                && TlsExtensionsUtilities.HasExtendedMasterSecretExtension(mServerExtensions);
+
+            if (!mSecurityParameters.IsExtendedMasterSecret
+                && (mResumedSession || mTlsClient.RequiresExtendedMasterSecret()))
+            {
+                throw new TlsFatalAlert(AlertDescription.handshake_failure);
+            }
+
+            /*
              * RFC 3546 2.2 Note that the extended server hello message is only sent in response to an
              * extended client hello message.
              * 
@@ -738,7 +751,7 @@ namespace Org.BouncyCastle.Crypto.Tls
             this.mSecurityParameters.cipherSuite = selectedCipherSuite;
             this.mSecurityParameters.compressionAlgorithm = selectedCompressionMethod;
 
-            if (sessionServerExtensions != null)
+            if (sessionServerExtensions != null && sessionServerExtensions.Count > 0)
             {
                 {
                     /*
@@ -754,8 +767,6 @@ namespace Org.BouncyCastle.Crypto.Tls
                     this.mSecurityParameters.encryptThenMac = serverSentEncryptThenMAC;
                 }
 
-                this.mSecurityParameters.extendedMasterSecret = TlsExtensionsUtilities.HasExtendedMasterSecretExtension(sessionServerExtensions);
-
                 this.mSecurityParameters.maxFragmentLength = ProcessMaxFragmentLengthExtension(sessionClientExtensions,
                     sessionServerExtensions, AlertDescription.illegal_parameter);
 
@@ -774,13 +785,6 @@ namespace Org.BouncyCastle.Crypto.Tls
                         AlertDescription.illegal_parameter);
             }
 
-            /*
-             * TODO[session-hash]
-             * 
-             * draft-ietf-tls-session-hash-04 4. Clients and servers SHOULD NOT accept handshakes
-             * that do not use the extended master secret [..]. (and see 5.2, 5.3)
-             */
-
             if (sessionClientExtensions != null)
             {
                 this.mTlsClient.ProcessServerExtensions(sessionServerExtensions);
@@ -837,14 +841,20 @@ namespace Org.BouncyCastle.Crypto.Tls
 
             if (session_id.Length > 0 && this.mSessionParameters != null)
             {
-                if (!Arrays.Contains(this.mOfferedCipherSuites, mSessionParameters.CipherSuite)
+                if (!mSessionParameters.IsExtendedMasterSecret
+                    || !Arrays.Contains(this.mOfferedCipherSuites, mSessionParameters.CipherSuite)
                     || !Arrays.Contains(this.mOfferedCompressionMethods, mSessionParameters.CompressionAlgorithm))
                 {
                     session_id = TlsUtilities.EmptyBytes;
                 }
             }
 
-            this.mClientExtensions = this.mTlsClient.GetClientExtensions();
+            this.mClientExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(this.mTlsClient.GetClientExtensions());
+
+            if (!client_version.IsSsl)
+            {
+                TlsExtensionsUtilities.AddExtendedMasterSecretExtension(this.mClientExtensions);
+            }
 
             HandshakeMessage message = new HandshakeMessage(HandshakeType.client_hello);
 
@@ -869,8 +879,6 @@ namespace Org.BouncyCastle.Crypto.Tls
                 if (noRenegExt && noRenegScsv)
                 {
                     // TODO Consider whether to default to a client extension instead
-    //                this.mClientExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(this.mClientExtensions);
-    //                this.mClientExtensions[ExtensionType.renegotiation_info] = CreateRenegotiationInfo(TlsUtilities.EmptyBytes);
                     this.mOfferedCipherSuites = Arrays.Append(mOfferedCipherSuites, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV);
                 }
 
@@ -891,10 +899,7 @@ namespace Org.BouncyCastle.Crypto.Tls
 
             TlsUtilities.WriteUint8ArrayWithUint8Length(mOfferedCompressionMethods, message);
 
-            if (mClientExtensions != null)
-            {
-                WriteExtensions(message, mClientExtensions);
-            }
+            WriteExtensions(message, mClientExtensions);
 
             message.WriteToRecordStream(this);
         }
diff --git a/crypto/src/crypto/tls/TlsPeer.cs b/crypto/src/crypto/tls/TlsPeer.cs
index 1ae41a41a..993fdf93f 100644
--- a/crypto/src/crypto/tls/TlsPeer.cs
+++ b/crypto/src/crypto/tls/TlsPeer.cs
@@ -6,6 +6,21 @@ namespace Org.BouncyCastle.Crypto.Tls
     public interface TlsPeer
     {
         /// <summary>
+        /// This implementation supports RFC 7627 and will always negotiate the extended_master_secret
+        /// extension where possible.
+        /// </summary>
+        /// <remarks>
+        /// When connecting to a peer that does not offer/accept this extension, it is recommended to
+        /// abort the handshake. This option is provided for interoperability with legacy peers,
+        /// although some TLS features will be disabled in that case (see RFC 7627 5.4).
+        /// </remarks>
+        /// <returns>
+        /// <code>true</code> if the handshake should be aborted when the peer does not negotiate the
+        /// extended_master_secret extension, or <code>false</code> to support legacy interoperability.
+        /// </returns>
+        bool RequiresExtendedMasterSecret();
+
+        /// <summary>
         /// draft-mathewson-no-gmtunixtime-00 2. "If existing users of a TLS implementation may rely on
         /// gmt_unix_time containing the current time, we recommend that implementors MAY provide the
         /// ability to set gmt_unix_time as an option only, off by default."
diff --git a/crypto/src/crypto/tls/TlsProtocol.cs b/crypto/src/crypto/tls/TlsProtocol.cs
index bbb76d53c..394967c37 100644
--- a/crypto/src/crypto/tls/TlsProtocol.cs
+++ b/crypto/src/crypto/tls/TlsProtocol.cs
@@ -288,6 +288,7 @@ namespace Org.BouncyCastle.Crypto.Tls
                         this.mSessionParameters = new SessionParameters.Builder()
                             .SetCipherSuite(this.mSecurityParameters.CipherSuite)
                             .SetCompressionAlgorithm(this.mSecurityParameters.CompressionAlgorithm)
+                            .SetExtendedMasterSecret(this.mSecurityParameters.IsExtendedMasterSecret)
                             .SetMasterSecret(this.mSecurityParameters.MasterSecret)
                             .SetPeerCertificate(this.mPeerCertificate)
                             .SetPskIdentity(this.mSecurityParameters.PskIdentity)
diff --git a/crypto/src/crypto/tls/TlsServerProtocol.cs b/crypto/src/crypto/tls/TlsServerProtocol.cs
index f5285d80b..e610b5950 100644
--- a/crypto/src/crypto/tls/TlsServerProtocol.cs
+++ b/crypto/src/crypto/tls/TlsServerProtocol.cs
@@ -560,12 +560,18 @@ namespace Org.BouncyCastle.Crypto.Tls
             this.mClientExtensions = ReadExtensions(buf);
 
             /*
-             * TODO[session-hash]
-             * 
-             * draft-ietf-tls-session-hash-04 4. Clients and servers SHOULD NOT accept handshakes
-             * that do not use the extended master secret [..]. (and see 5.2, 5.3)
+             * TODO[resumption] Check RFC 7627 5.4. for required behaviour 
+             */
+
+            /*
+             * RFC 7627 4. Clients and servers SHOULD NOT accept handshakes that do not use the extended
+             * master secret [..]. (and see 5.2, 5.3)
              */
             this.mSecurityParameters.extendedMasterSecret = TlsExtensionsUtilities.HasExtendedMasterSecretExtension(mClientExtensions);
+            if (!mSecurityParameters.IsExtendedMasterSecret && mTlsServer.RequiresExtendedMasterSecret())
+            {
+                throw new TlsFatalAlert(AlertDescription.handshake_failure);
+            }
 
             ContextAdmin.SetClientVersion(client_version);
 
@@ -724,7 +730,7 @@ namespace Org.BouncyCastle.Crypto.Tls
             TlsUtilities.WriteUint16(selectedCipherSuite, message);
             TlsUtilities.WriteUint8(selectedCompressionMethod, message);
 
-            this.mServerExtensions = mTlsServer.GetServerExtensions();
+            this.mServerExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(mTlsServer.GetServerExtensions());
 
             /*
              * RFC 5746 3.6. Server Behavior: Initial Handshake
@@ -748,14 +754,16 @@ namespace Org.BouncyCastle.Crypto.Tls
                      * If the secure_renegotiation flag is set to TRUE, the server MUST include an empty
                      * "renegotiation_info" extension in the ServerHello message.
                      */
-                    this.mServerExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(mServerExtensions);
                     this.mServerExtensions[ExtensionType.renegotiation_info] = CreateRenegotiationInfo(TlsUtilities.EmptyBytes);
                 }
             }
 
-            if (mSecurityParameters.extendedMasterSecret)
+            if (TlsUtilities.IsSsl(mTlsServerContext))
+            {
+                mSecurityParameters.extendedMasterSecret = false;
+            }
+            else if (mSecurityParameters.IsExtendedMasterSecret)
             {
-                this.mServerExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(mServerExtensions);
                 TlsExtensionsUtilities.AddExtendedMasterSecretExtension(mServerExtensions);
             }
 
@@ -765,7 +773,7 @@ namespace Org.BouncyCastle.Crypto.Tls
              * extensions.
              */
 
-            if (this.mServerExtensions != null)
+            if (this.mServerExtensions.Count > 0)
             {
                 this.mSecurityParameters.encryptThenMac = TlsExtensionsUtilities.HasEncryptThenMacExtension(mServerExtensions);
 
diff --git a/crypto/src/crypto/tls/TlsSessionImpl.cs b/crypto/src/crypto/tls/TlsSessionImpl.cs
index 866392623..4f0ff819e 100644
--- a/crypto/src/crypto/tls/TlsSessionImpl.cs
+++ b/crypto/src/crypto/tls/TlsSessionImpl.cs
@@ -8,17 +8,21 @@ namespace Org.BouncyCastle.Crypto.Tls
         :   TlsSession
     {
         internal readonly byte[] mSessionID;
-        internal SessionParameters mSessionParameters;
+        internal readonly SessionParameters mSessionParameters;
+        internal bool mResumable;
 
         internal TlsSessionImpl(byte[] sessionID, SessionParameters sessionParameters)
         {
             if (sessionID == null)
                 throw new ArgumentNullException("sessionID");
-            if (sessionID.Length < 1 || sessionID.Length > 32)
-                throw new ArgumentException("must have length between 1 and 32 bytes, inclusive", "sessionID");
+            if (sessionID.Length > 32)
+                throw new ArgumentException("cannot be longer than 32 bytes", "sessionID");
 
             this.mSessionID = Arrays.Clone(sessionID);
             this.mSessionParameters = sessionParameters;
+            this.mResumable = sessionID.Length > 0
+                && null != sessionParameters
+                && sessionParameters.IsExtendedMasterSecret;
         }
 
         public virtual SessionParameters ExportSessionParameters()
@@ -36,19 +40,12 @@ namespace Org.BouncyCastle.Crypto.Tls
 
         public virtual void Invalidate()
         {
-            lock (this)
-            {
-                if (this.mSessionParameters != null)
-                {
-                    this.mSessionParameters.Clear();
-                    this.mSessionParameters = null;
-                }
-            }
+            lock (this) this.mResumable = false;
         }
 
         public virtual bool IsResumable
         {
-            get { lock (this) return this.mSessionParameters != null; }
+            get { lock (this) return mResumable; }
         }
     }
 }
diff --git a/crypto/src/crypto/tls/TlsUtilities.cs b/crypto/src/crypto/tls/TlsUtilities.cs
index 698bf6da6..e6bd253aa 100644
--- a/crypto/src/crypto/tls/TlsUtilities.cs
+++ b/crypto/src/crypto/tls/TlsUtilities.cs
@@ -963,14 +963,14 @@ namespace Org.BouncyCastle.Crypto.Tls
         {
             SecurityParameters securityParameters = context.SecurityParameters;
 
-            byte[] seed = securityParameters.extendedMasterSecret
+            byte[] seed = securityParameters.IsExtendedMasterSecret
                 ?   securityParameters.SessionHash
                 :   Concat(securityParameters.ClientRandom, securityParameters.ServerRandom);
 
             if (IsSsl(context))
                 return CalculateMasterSecret_Ssl(pre_master_secret, seed);
 
-            string asciiLabel = securityParameters.extendedMasterSecret
+            string asciiLabel = securityParameters.IsExtendedMasterSecret
                 ?   ExporterLabel.extended_master_secret
                 :   ExporterLabel.master_secret;
 
diff --git a/crypto/test/src/crypto/tls/test/MockDtlsClient.cs b/crypto/test/src/crypto/tls/test/MockDtlsClient.cs
index 8d76c97b2..51493fae1 100644
--- a/crypto/test/src/crypto/tls/test/MockDtlsClient.cs
+++ b/crypto/test/src/crypto/tls/test/MockDtlsClient.cs
@@ -68,7 +68,6 @@ namespace Org.BouncyCastle.Crypto.Tls.Tests
         {
             IDictionary clientExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(base.GetClientExtensions());
             TlsExtensionsUtilities.AddEncryptThenMacExtension(clientExtensions);
-            TlsExtensionsUtilities.AddExtendedMasterSecretExtension(clientExtensions);
             {
                 /*
                  * NOTE: If you are copying test code, do not blindly set these extensions in your own client.
diff --git a/crypto/test/src/crypto/tls/test/MockTlsClient.cs b/crypto/test/src/crypto/tls/test/MockTlsClient.cs
index d8deabf96..f28236f0b 100644
--- a/crypto/test/src/crypto/tls/test/MockTlsClient.cs
+++ b/crypto/test/src/crypto/tls/test/MockTlsClient.cs
@@ -58,7 +58,6 @@ namespace Org.BouncyCastle.Crypto.Tls.Tests
         {
             IDictionary clientExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(base.GetClientExtensions());
             TlsExtensionsUtilities.AddEncryptThenMacExtension(clientExtensions);
-            TlsExtensionsUtilities.AddExtendedMasterSecretExtension(clientExtensions);
             {
                 /*
                  * NOTE: If you are copying test code, do not blindly set these extensions in your own client.