summary refs log tree commit diff
diff options
context:
space:
mode:
authorPeter Dettman <peter.dettman@bouncycastle.org>2023-07-08 23:19:24 +0700
committerPeter Dettman <peter.dettman@bouncycastle.org>2023-07-08 23:19:24 +0700
commit2d71de0765cbc92f064a29e03aab8ff88725deb4 (patch)
treed7ed01c72c460299c04d1cd3517ba676133dbd11
parentRefactoring in DTLS (diff)
downloadBouncyCastle.NET-ed25519-2d71de0765cbc92f064a29e03aab8ff88725deb4.tar.xz
DTLS: refactoring around extended_master_secret
- especially the interaction with session resumption and the methods relating to use of EMS.
-rw-r--r--crypto/src/tls/DtlsClientProtocol.cs243
-rw-r--r--crypto/src/tls/DtlsServerProtocol.cs286
-rw-r--r--crypto/src/tls/TlsClientProtocol.cs12
-rw-r--r--crypto/src/tls/TlsServerProtocol.cs2
4 files changed, 317 insertions, 226 deletions
diff --git a/crypto/src/tls/DtlsClientProtocol.cs b/crypto/src/tls/DtlsClientProtocol.cs
index eec920c4a..da39320ea 100644
--- a/crypto/src/tls/DtlsClientProtocol.cs
+++ b/crypto/src/tls/DtlsClientProtocol.cs
@@ -35,27 +35,6 @@ namespace Org.BouncyCastle.Tls
             SecurityParameters securityParameters = clientContext.SecurityParameters;
             securityParameters.m_extendedPadding = client.ShouldUseExtendedPadding();
 
-            TlsSession sessionToResume = client.GetSessionToResume();
-            if (sessionToResume != null && sessionToResume.IsResumable)
-            {
-                SessionParameters sessionParameters = sessionToResume.ExportSessionParameters();
-
-                if (sessionParameters != null
-                    && (sessionParameters.IsExtendedMasterSecret
-                        || (!client.RequiresExtendedMasterSecret() && client.AllowLegacyResumption())))
-                {
-                    TlsCrypto crypto = clientContext.Crypto;
-                    TlsSecret sessionMasterSecret = TlsUtilities.GetSessionMasterSecret(crypto,
-                        sessionParameters.MasterSecret);
-                    if (sessionMasterSecret != null)
-                    {
-                        state.tlsSession = sessionToResume;
-                        state.sessionParameters = sessionParameters;
-                        state.sessionMasterSecret = sessionMasterSecret;
-                    }
-                }
-            }
-
             DtlsRecordLayer recordLayer = new DtlsRecordLayer(clientContext, client, transport);
             client.NotifyCloseHandle(recordLayer);
 
@@ -129,6 +108,8 @@ namespace Org.BouncyCastle.Tls
                 recordLayer.SetWriteVersion(recordLayerVersion);
 
                 ProcessServerHello(state, serverMessage.Body);
+
+                ApplyMaxFragmentLengthExtension(recordLayer, securityParameters.MaxFragmentLength);
             }
             else
             {
@@ -137,8 +118,6 @@ namespace Org.BouncyCastle.Tls
 
             handshake.HandshakeHash.NotifyPrfDetermined();
 
-            ApplyMaxFragmentLengthExtension(recordLayer, securityParameters.MaxFragmentLength);
-
             if (securityParameters.IsResumedSession)
             {
                 securityParameters.m_masterSecret = state.sessionMasterSecret;
@@ -412,7 +391,7 @@ namespace Org.BouncyCastle.Tls
 
             ProtocolVersion[] supportedVersions = client.GetProtocolVersions();
 
-            //ProtocolVersion earliestVersion = ProtocolVersion.GetEarliestDtls(supportedVersions);
+            ProtocolVersion earliestVersion = ProtocolVersion.GetEarliestDtls(supportedVersions);
             ProtocolVersion latestVersion = ProtocolVersion.GetLatestDtls(supportedVersions);
 
             if (!ProtocolVersion.IsSupportedDtlsVersionClient(latestVersion))
@@ -421,7 +400,7 @@ namespace Org.BouncyCastle.Tls
             clientContext.SetClientVersion(latestVersion);
             clientContext.SetClientSupportedVersions(supportedVersions);
 
-            //bool offeringDtlsV12Minus = ProtocolVersion.DTLSv12.IsEqualOrLaterVersionOf(earliestVersion);
+            bool offeringDtlsV12Minus = ProtocolVersion.DTLSv12.IsEqualOrLaterVersionOf(earliestVersion);
             bool offeringDtlsV13Plus = ProtocolVersion.DTLSv13.IsEqualOrEarlierVersionOf(latestVersion);
 
             {
@@ -430,12 +409,18 @@ namespace Org.BouncyCastle.Tls
                 securityParameters.m_clientRandom = TlsProtocol.CreateRandomBlock(useGmtUnixTime, clientContext);
             }
 
+            TlsSession sessionToResume = offeringDtlsV12Minus ? client.GetSessionToResume() : null;
+
             bool fallback = client.IsFallback();
 
             state.offeredCipherSuites = client.GetCipherSuites();
 
             state.clientExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(client.GetClientExtensions());
 
+            bool shouldUseEms = client.ShouldUseExtendedMasterSecret();
+
+            EstablishSession(state, sessionToResume);
+
             byte[] legacy_session_id = TlsUtilities.GetSessionID(state.tlsSession);
 
             if (legacy_session_id.Length > 0)
@@ -457,7 +442,31 @@ namespace Org.BouncyCastle.Tls
                 }
             }
 
-            client.NotifySessionToResume(legacy_session_id.Length < 1 ? null : state.tlsSession);
+            if (legacy_session_id.Length > 0 && TlsUtilities.IsExtendedMasterSecretOptional(sessionVersion))
+            {
+                if (shouldUseEms)
+                {
+                    if (!state.sessionParameters.IsExtendedMasterSecret &&
+                        !client.AllowLegacyResumption())
+                    {
+                        legacy_session_id = TlsUtilities.EmptyBytes;
+                    }
+                }
+                else
+                {
+                    if (state.sessionParameters.IsExtendedMasterSecret)
+                    {
+                        legacy_session_id = TlsUtilities.EmptyBytes;
+                    }
+                }
+            }
+
+            if (legacy_session_id.Length < 1)
+            {
+                CancelSession(state);
+            }
+
+            client.NotifySessionToResume(state.tlsSession);
 
             ProtocolVersion legacy_version = latestVersion;
             if (offeringDtlsV13Plus)
@@ -493,15 +502,13 @@ namespace Org.BouncyCastle.Tls
             state.clientAgreements = TlsUtilities.AddKeyShareToClientHello(clientContext, client,
                 state.clientExtensions);
 
-            if (TlsUtilities.IsExtendedMasterSecretOptional(supportedVersions)
-                && client.ShouldUseExtendedMasterSecret())
+            if (shouldUseEms && TlsUtilities.IsExtendedMasterSecretOptional(supportedVersions))
             {
                 TlsExtensionsUtilities.AddExtendedMasterSecretExtension(state.clientExtensions);
             }
-            else if (!offeringDtlsV13Plus
-                && client.RequiresExtendedMasterSecret())
+            else
             {
-                throw new TlsFatalAlert(AlertDescription.internal_error);
+                state.clientExtensions.Remove(ExtensionType.extended_master_secret);
             }
 
             // Cipher Suites (and SCSV)
@@ -572,7 +579,7 @@ namespace Org.BouncyCastle.Tls
             return buf.ToArray();
         }
 
-        protected virtual void InvalidateSession(ClientHandshakeState state)
+        protected virtual void CancelSession(ClientHandshakeState state)
         {
             if (state.sessionMasterSecret != null)
             {
@@ -586,11 +593,53 @@ namespace Org.BouncyCastle.Tls
                 state.sessionParameters = null;
             }
 
+            state.tlsSession = null;
+        }
+
+        protected virtual bool EstablishSession(ClientHandshakeState state, TlsSession sessionToResume)
+        {
+            state.tlsSession = null;
+            state.sessionParameters = null;
+            state.sessionMasterSecret = null;
+
+            if (null == sessionToResume || !sessionToResume.IsResumable)
+                return false;
+
+            SessionParameters sessionParameters = sessionToResume.ExportSessionParameters();
+            if (null == sessionParameters)
+                return false;
+
+            ProtocolVersion sessionVersion = sessionParameters.NegotiatedVersion;
+            if (null == sessionVersion || !sessionVersion.IsDtls)
+                return false;
+
+            bool isEms = sessionParameters.IsExtendedMasterSecret;
+            if (!TlsUtilities.IsExtendedMasterSecretOptional(sessionVersion))
+            {
+                if (!isEms)
+                    return false;
+            }
+
+            TlsCrypto crypto = state.clientContext.Crypto;
+            TlsSecret sessionMasterSecret = TlsUtilities.GetSessionMasterSecret(crypto, sessionParameters.MasterSecret);
+            if (null == sessionMasterSecret)
+                return false;
+
+            state.tlsSession = sessionToResume;
+            state.sessionParameters = sessionParameters;
+            state.sessionMasterSecret = sessionMasterSecret;
+
+            return true;
+        }
+
+        protected virtual void InvalidateSession(ClientHandshakeState state)
+        {
             if (state.tlsSession != null)
             {
                 state.tlsSession.Invalidate();
-                state.tlsSession = null;
             }
+
+            CancelSession(state);
         }
 
         /// <exception cref="IOException"/>
@@ -685,11 +734,11 @@ namespace Org.BouncyCastle.Tls
             MemoryStream buf = new MemoryStream(body, false);
             ServerHello serverHello = ServerHello.Parse(buf);
 
-            state.serverExtensions = serverHello.Extensions;
+            var serverHelloExtensions = serverHello.Extensions;
 
             ProtocolVersion legacy_version = serverHello.Version;
             ProtocolVersion supported_version = TlsExtensionsUtilities.GetSupportedVersionsExtensionServer(
-                state.serverExtensions);
+                serverHelloExtensions);
 
             ProtocolVersion server_version;
             if (null == supported_version)
@@ -774,63 +823,15 @@ namespace Org.BouncyCastle.Tls
             }
 
             /*
-             * RFC3546 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
-             * possibility that the extended server hello message could "break" existing TLS 1.0
-             * clients.
-             */
-
-            /*
-             * TODO RFC 3546 2.3 If [...] the older session is resumed, then the server MUST ignore
-             * extensions appearing in the client hello, and send a server hello containing no
-             * extensions.
-             */
-
-            /*
-             * RFC 7627 4. Clients and servers SHOULD NOT accept handshakes that do not use the extended
-             * master secret [..]. (and see 5.2, 5.3)
-             * 
-             * RFC 8446 Appendix D. Because TLS 1.3 always hashes in the transcript up to the server
-             * Finished, implementations which support both TLS 1.3 and earlier versions SHOULD indicate
-             * the use of the Extended Master Secret extension in their APIs whenever TLS 1.3 is used.
-             */
-            if (TlsUtilities.IsTlsV13(server_version))
-            {
-                securityParameters.m_extendedMasterSecret = true;
-            }
-            else
-            {
-                bool acceptedExtendedMasterSecret = TlsExtensionsUtilities.HasExtendedMasterSecretExtension(
-                    state.serverExtensions);
-
-                if (acceptedExtendedMasterSecret)
-                {
-                    if (!securityParameters.IsResumedSession && !client.ShouldUseExtendedMasterSecret())
-                        throw new TlsFatalAlert(AlertDescription.handshake_failure);
-                }
-                else
-                {
-                    if (client.RequiresExtendedMasterSecret()
-                        || (securityParameters.IsResumedSession && !client.AllowLegacyResumption()))
-                    {
-                        throw new TlsFatalAlert(AlertDescription.handshake_failure);
-                    }
-                }
-
-                securityParameters.m_extendedMasterSecret = acceptedExtendedMasterSecret;
-            }
-
-            /*
              * 
              * 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.
              */
-            if (state.serverExtensions != null)
+            state.serverExtensions = serverHelloExtensions;
+            if (serverHelloExtensions != null)
             {
-                foreach (int extType in state.serverExtensions.Keys)
+                foreach (int extType in serverHelloExtensions.Keys)
                 {
                     /*
                      * RFC 5746 3.6. Note that sending a "renegotiation_info" extension in response to a
@@ -867,17 +868,30 @@ namespace Org.BouncyCastle.Tls
                 }
             }
 
-            /*
-             * RFC 5746 3.4. Client Behavior: Initial Handshake
-             */
+            byte[] renegExtData = TlsUtilities.GetExtensionData(serverHelloExtensions,
+                ExtensionType.renegotiation_info);
+
+            // NOT renegotiating
             {
                 /*
+                 * RFC 5746 3.4. Client Behavior: Initial Handshake (both full and session-resumption)
+                 */
+
+                /*
                  * When a ServerHello is received, the client MUST check if it includes the
                  * "renegotiation_info" extension:
                  */
-                byte[] renegExtData = TlsUtilities.GetExtensionData(state.serverExtensions,
-                    ExtensionType.renegotiation_info);
-                if (renegExtData != null)
+                if (renegExtData == null)
+                {
+                    /*
+                     * If the extension is not present, the server does not support secure
+                     * renegotiation; set secure_renegotiation flag to FALSE. In this case, some clients
+                     * may want to terminate the handshake instead of continuing; see Section 4.1 for
+                     * discussion.
+                     */
+                    securityParameters.m_secureRenegotiation = false;
+                }
+                else
                 {
                     /*
                      * If the extension is present, set the secure_renegotiation flag to TRUE. The
@@ -898,13 +912,50 @@ namespace Org.BouncyCastle.Tls
             // TODO[compat-gnutls] GnuTLS test server fails to send renegotiation_info extension when resuming
             client.NotifySecureRenegotiation(securityParameters.IsSecureRenegotiation);
 
+            // extended_master_secret
+            {
+                bool negotiatedEms = false;
+
+                if (TlsExtensionsUtilities.HasExtendedMasterSecretExtension(state.clientExtensions))
+                {
+                    negotiatedEms = TlsExtensionsUtilities.HasExtendedMasterSecretExtension(serverHelloExtensions);
+
+                    if (TlsUtilities.IsExtendedMasterSecretOptional(server_version))
+                    {
+                        if (!negotiatedEms &&
+                            client.RequiresExtendedMasterSecret())
+                        {
+                            throw new TlsFatalAlert(AlertDescription.handshake_failure,
+                                "Extended Master Secret extension is required");
+                        }
+                    }
+                    else
+                    {
+                        if (negotiatedEms)
+                        {
+                            throw new TlsFatalAlert(AlertDescription.illegal_parameter,
+                                "Server sent an unexpected extended_master_secret extension negotiating " + server_version);
+                        }
+                    }
+                }
+
+                securityParameters.m_extendedMasterSecret = negotiatedEms;
+            }
+
+            if (securityParameters.IsResumedSession &&
+                securityParameters.IsExtendedMasterSecret != state.sessionParameters.IsExtendedMasterSecret)
+            {
+                throw new TlsFatalAlert(AlertDescription.handshake_failure,
+                    "Server resumed session with mismatched extended_master_secret negotiation");
+            }
+
             /*
              * RFC 7301 3.1. When session resumption or session tickets [...] are used, the previous
              * contents of this extension are irrelevant, and only the values in the new handshake
              * messages are considered.
              */
             securityParameters.m_applicationProtocol = TlsExtensionsUtilities.GetAlpnExtensionServer(
-                state.serverExtensions);
+                serverHelloExtensions);
             securityParameters.m_applicationProtocolSet = true;
 
             // Connection ID
@@ -914,7 +965,7 @@ namespace Org.BouncyCastle.Tls
                  * RFC 9146 3. When a DTLS session is resumed or renegotiated, the "connection_id" extension is
                  * negotiated afresh.
                  */
-                var serverConnectionID = TlsExtensionsUtilities.GetConnectionIDExtension(state.serverExtensions);
+                var serverConnectionID = TlsExtensionsUtilities.GetConnectionIDExtension(serverHelloExtensions);
                 if (serverConnectionID != null)
                 {
                     var clientConnectionID = TlsExtensionsUtilities.GetConnectionIDExtension(state.clientExtensions)
@@ -928,7 +979,7 @@ namespace Org.BouncyCastle.Tls
             // Heartbeats
             {
                 HeartbeatExtension heartbeatExtension = TlsExtensionsUtilities.GetHeartbeatExtension(
-                    state.serverExtensions);
+                    serverHelloExtensions);
                 if (null == heartbeatExtension)
                 {
                     state.heartbeat = null;
@@ -941,7 +992,7 @@ namespace Org.BouncyCastle.Tls
             }
 
             var sessionClientExtensions = state.clientExtensions;
-            var sessionServerExtensions = state.serverExtensions;
+            var sessionServerExtensions = serverHelloExtensions;
 
             if (securityParameters.IsResumedSession)
             {
diff --git a/crypto/src/tls/DtlsServerProtocol.cs b/crypto/src/tls/DtlsServerProtocol.cs
index e3f2d7564..0116bb9e6 100644
--- a/crypto/src/tls/DtlsServerProtocol.cs
+++ b/crypto/src/tls/DtlsServerProtocol.cs
@@ -1,4 +1,5 @@
 using System;
+using System.Collections;
 using System.Collections.Generic;
 using System.IO;
 
@@ -119,25 +120,6 @@ namespace Org.BouncyCastle.Tls
                 ProcessClientHello(state, request.ClientHello);
             }
 
-            /*
-             * NOTE: Currently no server support for session resumption
-             * 
-             * If adding support, ensure securityParameters.tlsUnique is set to the localVerifyData, but
-             * ONLY when extended_master_secret has been negotiated (otherwise NULL).
-             */
-            {
-                // TODO[resumption]
-
-                state.tlsSession = TlsUtilities.ImportSession(TlsUtilities.EmptyBytes, null);
-                state.sessionParameters = null;
-                state.sessionMasterSecret = null;
-            }
-
-            securityParameters.m_resumedSession = false;
-            securityParameters.m_sessionID = state.tlsSession.SessionID;
-
-            server.NotifySession(state.tlsSession);
-
             {
                 byte[] serverHelloBody = GenerateServerHello(state, recordLayer);
 
@@ -379,7 +361,7 @@ namespace Org.BouncyCastle.Tls
                 .SetServerExtensions(state.serverExtensions)
                 .Build();
 
-            state.tlsSession = TlsUtilities.ImportSession(state.tlsSession.SessionID, state.sessionParameters);
+            state.tlsSession = TlsUtilities.ImportSession(securityParameters.SessionID, state.sessionParameters);
 
             securityParameters.m_tlsUnique = securityParameters.PeerVerifyData;
 
@@ -425,9 +407,14 @@ namespace Org.BouncyCastle.Tls
             TlsServerContextImpl serverContext = state.serverContext;
             SecurityParameters securityParameters = serverContext.SecurityParameters;
 
-            ProtocolVersion server_version = server.GetServerVersion();
+            // TODO[dtls13] Negotiate cipher suite first?
+
+            ProtocolVersion serverVersion;
+
+            // NOT renegotiating
             {
-                if (!ProtocolVersion.Contains(serverContext.ClientSupportedVersions, server_version))
+                serverVersion = server.GetServerVersion();
+                if (!ProtocolVersion.Contains(serverContext.ClientSupportedVersions, serverVersion))
                     throw new TlsFatalAlert(AlertDescription.internal_error);
 
                 // TODO[dtls13] Read draft/RFC for guidance on the legacy_record_version field
@@ -436,24 +423,116 @@ namespace Org.BouncyCastle.Tls
                 //    : server_version;
 
                 //recordLayer.SetWriteVersion(legacy_record_version);
-                securityParameters.m_negotiatedVersion = server_version;
-
-                TlsUtilities.NegotiatedVersionDtlsServer(serverContext);
+                securityParameters.m_negotiatedVersion = serverVersion;
             }
 
+            // TODO[dtls13]
+            //if (ProtocolVersion.DTLSv13.IsEqualOrEarlierVersionOf(serverVersion))
+            //{
+            //    // See RFC 8446 D.4.
+            //    recordStream.SetIgnoreChangeCipherSpec(true);
+
+            //    recordStream.SetWriteVersion(ProtocolVersion.DTLSv12);
+
+            //    return Generate13ServerHello(clientHello, clientHelloMessage, false);
+            //}
+
+            //recordStream.setWriteVersion(serverVersion);
+
             {
-                bool useGmtUnixTime = ProtocolVersion.DTLSv12.IsEqualOrLaterVersionOf(server_version)
-                    && server.ShouldUseGmtUnixTime();
+                bool useGmtUnixTime = server.ShouldUseGmtUnixTime();
 
                 securityParameters.m_serverRandom = TlsProtocol.CreateRandomBlock(useGmtUnixTime, serverContext);
 
-                if (!server_version.Equals(ProtocolVersion.GetLatestDtls(server.GetProtocolVersions())))
+                if (!serverVersion.Equals(ProtocolVersion.GetLatestDtls(server.GetProtocolVersions())))
+                {
+                    TlsUtilities.WriteDowngradeMarker(serverVersion, securityParameters.ServerRandom);
+                }
+            }
+
+            /*
+             * NOTE: Currently no server support for session resumption
+             * 
+             * If adding support, ensure securityParameters.tlsUnique is set to the localVerifyData, but
+             * ONLY when extended_master_secret has been negotiated (otherwise NULL).
+             */
+            // TODO[resumption]
+            {
+                state.tlsSession = null;
+                state.sessionParameters = null;
+                state.sessionMasterSecret = null;
+            }
+
+            bool resumedSession = false;
+
+            if (resumedSession && !serverVersion.Equals(state.sessionParameters.NegotiatedVersion))
+            {
+                resumedSession = false;
+            }
+
+            // TODO Check the session cipher suite is selectable by the same rules that GetSelectedCipherSuite uses
+
+            // TODO Check the resumed session has a peer certificate if we NEED client-auth
+
+            // extended_master_secret
+            {
+                bool negotiateEms = false;
+
+                if (TlsUtilities.IsExtendedMasterSecretOptional(serverVersion) &&
+                    server.ShouldUseExtendedMasterSecret())
+                {
+                    if (TlsExtensionsUtilities.HasExtendedMasterSecretExtension(state.clientExtensions))
+                    {
+                        negotiateEms = true;
+                    }
+                    else if (server.RequiresExtendedMasterSecret())
+                    {
+                        throw new TlsFatalAlert(AlertDescription.handshake_failure,
+                            "Extended Master Secret extension is required");
+                    }
+                    else if (resumedSession)
+                    {
+                        if (state.sessionParameters.IsExtendedMasterSecret)
+                        {
+                            throw new TlsFatalAlert(AlertDescription.handshake_failure,
+                                "Extended Master Secret extension is required for EMS session resumption");
+                        }
+
+                        if (!server.AllowLegacyResumption())
+                        {
+                            throw new TlsFatalAlert(AlertDescription.handshake_failure,
+                                "Extended Master Secret extension is required for legacy session resumption");
+                        }
+                    }
+                }
+
+                if (resumedSession && negotiateEms != state.sessionParameters.IsExtendedMasterSecret)
+                {
+                    resumedSession = false;
+                }
+
+                securityParameters.m_extendedMasterSecret = negotiateEms;
+            }
+
+            if (!resumedSession)
+            {
+                CancelSession(state);
+
+                byte[] newSessionID = server.GetNewSessionID();
+                if (null == newSessionID)
                 {
-                    TlsUtilities.WriteDowngradeMarker(server_version, securityParameters.ServerRandom);
+                    newSessionID = TlsUtilities.EmptyBytes;
                 }
+
+                state.tlsSession = TlsUtilities.ImportSession(newSessionID, null);
             }
 
-            bool resumedSession = securityParameters.IsResumedSession;
+            securityParameters.m_resumedSession = resumedSession;
+            securityParameters.m_sessionID = state.tlsSession.SessionID;
+
+            server.NotifySession(state.tlsSession);
+
+            TlsUtilities.NegotiatedVersionDtlsServer(serverContext);
 
             {
                 int cipherSuite = ValidateSelectedCipherSuite(server.GetSelectedCipherSuite(),
@@ -468,76 +547,54 @@ namespace Org.BouncyCastle.Tls
                 TlsUtilities.NegotiatedCipherSuite(securityParameters, cipherSuite);
             }
 
-            state.serverExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(
-                server.GetServerExtensions());
-
-            server.GetServerExtensionsForConnection(state.serverExtensions);
-
-            ProtocolVersion legacy_version = server_version;
-            if (server_version.IsLaterVersionOf(ProtocolVersion.DTLSv12))
             {
-                legacy_version = ProtocolVersion.DTLSv12;
+                IDictionary<int, byte[]> sessionServerExtensions = resumedSession
+                    ?   state.sessionParameters.ReadServerExtensions()
+                    :   server.GetServerExtensions();
 
-                TlsExtensionsUtilities.AddSupportedVersionsExtensionServer(state.serverExtensions, server_version);
+                state.serverExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(sessionServerExtensions);
             }
 
-            /*
-             * RFC 5746 3.6. Server Behavior: Initial Handshake 
-             */
-            if (securityParameters.IsSecureRenegotiation)
-            {
-                byte[] renegExtData = TlsUtilities.GetExtensionData(state.serverExtensions,
-                    ExtensionType.renegotiation_info);
-                bool noRenegExt = (null == renegExtData);
+            server.GetServerExtensionsForConnection(state.serverExtensions);
 
-                if (noRenegExt)
+            // NOT renegotiating
+            {
+                /*
+                 * RFC 5746 3.6. Server Behavior: Initial Handshake (both full and session-resumption)
+                 */
+                if (securityParameters.IsSecureRenegotiation)
                 {
-                    /*
-                     * Note that sending a "renegotiation_info" extension in response to a ClientHello
-                     * containing only the SCSV is an explicit exception to the prohibition in RFC 5246,
-                     * Section 7.4.1.4, on the server sending unsolicited extensions and is only allowed
-                     * because the client is signaling its willingness to receive the extension via the
-                     * TLS_EMPTY_RENEGOTIATION_INFO_SCSV SCSV.
-                     */
+                    byte[] serverRenegExtData = TlsUtilities.GetExtensionData(state.serverExtensions,
+                        ExtensionType.renegotiation_info);
+                    bool noRenegExt = (null == serverRenegExtData);
 
-                    /*
-                     * If the secure_renegotiation flag is set to TRUE, the server MUST include an empty
-                     * "renegotiation_info" extension in the ServerHello message.
-                     */
-                    state.serverExtensions[ExtensionType.renegotiation_info] = TlsProtocol.CreateRenegotiationInfo(
-                        TlsUtilities.EmptyBytes);
+                    if (noRenegExt)
+                    {
+                        /*
+                         * Note that sending a "renegotiation_info" extension in response to a ClientHello
+                         * containing only the SCSV is an explicit exception to the prohibition in RFC 5246,
+                         * Section 7.4.1.4, on the server sending unsolicited extensions and is only allowed
+                         * because the client is signaling its willingness to receive the extension via the
+                         * TLS_EMPTY_RENEGOTIATION_INFO_SCSV SCSV.
+                         */
+
+                        /*
+                         * If the secure_renegotiation flag is set to TRUE, the server MUST include an empty
+                         * "renegotiation_info" extension in the ServerHello message.
+                         */
+                        state.serverExtensions[ExtensionType.renegotiation_info] = TlsProtocol.CreateRenegotiationInfo(
+                            TlsUtilities.EmptyBytes);
+                    }
                 }
             }
 
-            /*
-             * RFC 7627 4. Clients and servers SHOULD NOT accept handshakes that do not use the extended
-             * master secret [..]. (and see 5.2, 5.3)
-             * 
-             * RFC 8446 Appendix D. Because TLS 1.3 always hashes in the transcript up to the server
-             * Finished, implementations which support both TLS 1.3 and earlier versions SHOULD indicate
-             * the use of the Extended Master Secret extension in their APIs whenever TLS 1.3 is used.
-             */
-            if (TlsUtilities.IsTlsV13(server_version))
+            if (securityParameters.IsExtendedMasterSecret)
             {
-                securityParameters.m_extendedMasterSecret = true;
+                TlsExtensionsUtilities.AddExtendedMasterSecretExtension(state.serverExtensions);
             }
             else
             {
-                securityParameters.m_extendedMasterSecret = state.offeredExtendedMasterSecret
-                    && server.ShouldUseExtendedMasterSecret();
-
-                if (securityParameters.IsExtendedMasterSecret)
-                {
-                    TlsExtensionsUtilities.AddExtendedMasterSecretExtension(state.serverExtensions);
-                }
-                else if (server.RequiresExtendedMasterSecret())
-                {
-                    throw new TlsFatalAlert(AlertDescription.handshake_failure);
-                }
-                else if (resumedSession && !server.AllowLegacyResumption())
-                {
-                    throw new TlsFatalAlert(AlertDescription.internal_error);
-                }
+                state.serverExtensions.Remove(ExtensionType.extended_master_secret);
             }
 
             // Heartbeats
@@ -547,13 +604,6 @@ namespace Org.BouncyCastle.Tls
                     new HeartbeatExtension(state.heartbeatPolicy));
             }
 
-
-
-            /*
-             * RFC 7301 3.1. When session resumption or session tickets [...] are used, the previous
-             * contents of this extension are irrelevant, and only the values in the new handshake
-             * messages are considered.
-             */
             securityParameters.m_applicationProtocol = TlsExtensionsUtilities.GetAlpnExtensionServer(
                 state.serverExtensions);
             securityParameters.m_applicationProtocolSet = true;
@@ -576,11 +626,6 @@ namespace Org.BouncyCastle.Tls
                 }
             }
 
-            /*
-             * TODO RFC 3546 2.3 If [...] the older session is resumed, then the server MUST ignore
-             * extensions appearing in the client hello, and send a server hello containing no
-             * extensions.
-             */
             if (state.serverExtensions.Count > 0)
             {
                 securityParameters.m_encryptThenMac = TlsExtensionsUtilities.HasEncryptThenMacExtension(
@@ -593,10 +638,6 @@ namespace Org.BouncyCastle.Tls
                 securityParameters.m_truncatedHmac = TlsExtensionsUtilities.HasTruncatedHmacExtension(
                     state.serverExtensions);
 
-                /*
-                 * TODO It's surprising that there's no provision to allow a 'fresh' CertificateStatus to be sent in
-                 * a session resumption handshake.
-                 */
                 if (!resumedSession)
                 {
                     // TODO[tls13] See RFC 8446 4.4.2.1
@@ -618,17 +659,15 @@ namespace Org.BouncyCastle.Tls
 
             ApplyMaxFragmentLengthExtension(recordLayer, securityParameters.MaxFragmentLength);
 
-
-
-            ServerHello serverHello = new ServerHello(legacy_version, securityParameters.ServerRandom,
-                state.tlsSession.SessionID, securityParameters.CipherSuite, state.serverExtensions);
+            ServerHello serverHello = new ServerHello(serverVersion, securityParameters.ServerRandom,
+                securityParameters.SessionID, securityParameters.CipherSuite, state.serverExtensions);
 
             MemoryStream buf = new MemoryStream();
             serverHello.Encode(serverContext, buf);
             return buf.ToArray();
         }
 
-        protected virtual void InvalidateSession(ServerHandshakeState state)
+        protected virtual void CancelSession(ServerHandshakeState state)
         {
             if (state.sessionMasterSecret != null)
             {
@@ -642,11 +681,17 @@ namespace Org.BouncyCastle.Tls
                 state.sessionParameters = null;
             }
 
+            state.tlsSession = null;
+        }
+
+        protected virtual void InvalidateSession(ServerHandshakeState state)
+        {
             if (state.tlsSession != null)
             {
                 state.tlsSession.Invalidate();
-                state.tlsSession = null;
             }
+
+            CancelSession(state);
         }
 
         /// <exception cref="IOException"/>
@@ -764,11 +809,16 @@ namespace Org.BouncyCastle.Tls
              * TODO[resumption] Check RFC 7627 5.4. for required behaviour 
              */
 
-            /*
-             * RFC 5746 3.6. Server Behavior: Initial Handshake
-             */
+            byte[] clientRenegExtData = TlsUtilities.GetExtensionData(state.clientExtensions,
+                ExtensionType.renegotiation_info);
+
+            // NOT renegotiatiing
             {
                 /*
+                 * RFC 5746 3.6. Server Behavior: Initial Handshake (both full and session-resumption)
+                 */
+
+                /*
                  * RFC 5746 3.4. The client MUST include either an empty "renegotiation_info" extension,
                  * or the TLS_EMPTY_RENEGOTIATION_INFO_SCSV signaling cipher suite value in the
                  * ClientHello. Including both is NOT RECOMMENDED.
@@ -784,13 +834,7 @@ namespace Org.BouncyCastle.Tls
                     securityParameters.m_secureRenegotiation = true;
                 }
 
-                /*
-                 * The server MUST check if the "renegotiation_info" extension is included in the
-                 * ClientHello.
-                 */
-                byte[] renegExtData = TlsUtilities.GetExtensionData(state.clientExtensions,
-                    ExtensionType.renegotiation_info);
-                if (renegExtData != null)
+                if (clientRenegExtData != null)
                 {
                     /*
                      * If the extension is present, set secure_renegotiation flag to TRUE. The
@@ -799,7 +843,7 @@ namespace Org.BouncyCastle.Tls
                      */
                     securityParameters.m_secureRenegotiation = true;
 
-                    if (!Arrays.FixedTimeEquals(renegExtData,
+                    if (!Arrays.FixedTimeEquals(clientRenegExtData,
                         TlsProtocol.CreateRenegotiationInfo(TlsUtilities.EmptyBytes)))
                     {
                         throw new TlsFatalAlert(AlertDescription.handshake_failure);
@@ -809,9 +853,6 @@ namespace Org.BouncyCastle.Tls
 
             server.NotifySecureRenegotiation(securityParameters.IsSecureRenegotiation);
 
-            state.offeredExtendedMasterSecret = TlsExtensionsUtilities.HasExtendedMasterSecretExtension(
-                state.clientExtensions);
-
             if (state.clientExtensions != null)
             {
                 // NOTE: Validates the padding extension data, if present
@@ -889,7 +930,6 @@ namespace Org.BouncyCastle.Tls
             internal int[] offeredCipherSuites = null;
             internal IDictionary<int, byte[]> clientExtensions = null;
             internal IDictionary<int, byte[]> serverExtensions = null;
-            internal bool offeredExtendedMasterSecret = false;
             internal bool expectSessionTicket = false;
             internal TlsKeyExchange keyExchange = null;
             internal TlsCredentials serverCredentials = null;
diff --git a/crypto/src/tls/TlsClientProtocol.cs b/crypto/src/tls/TlsClientProtocol.cs
index b117a4025..30ad67fbe 100644
--- a/crypto/src/tls/TlsClientProtocol.cs
+++ b/crypto/src/tls/TlsClientProtocol.cs
@@ -1150,9 +1150,9 @@ namespace Org.BouncyCastle.Tls
              * Hello is always allowed.
              */
             this.m_serverExtensions = serverHelloExtensions;
-            if (m_serverExtensions != null)
+            if (serverHelloExtensions != null)
             {
-                foreach (int extType in m_serverExtensions.Keys)
+                foreach (int extType in serverHelloExtensions.Keys)
                 {
                     /*
                      * RFC 5746 3.6. Note that sending a "renegotiation_info" extension in response to a
@@ -1189,7 +1189,7 @@ namespace Org.BouncyCastle.Tls
                 }
             }
 
-            byte[] renegExtData = TlsUtilities.GetExtensionData(m_serverExtensions, ExtensionType.renegotiation_info);
+            byte[] renegExtData = TlsUtilities.GetExtensionData(serverHelloExtensions, ExtensionType.renegotiation_info);
 
             // NOT renegotiating
             {
@@ -1235,7 +1235,7 @@ namespace Org.BouncyCastle.Tls
 
                 if (TlsExtensionsUtilities.HasExtendedMasterSecretExtension(m_clientExtensions))
                 {
-                    negotiatedEms = TlsExtensionsUtilities.HasExtendedMasterSecretExtension(m_serverExtensions);
+                    negotiatedEms = TlsExtensionsUtilities.HasExtendedMasterSecretExtension(serverHelloExtensions);
 
                     if (TlsUtilities.IsExtendedMasterSecretOptional(server_version))
                     {
@@ -1272,11 +1272,11 @@ namespace Org.BouncyCastle.Tls
              * messages are considered.
              */
             securityParameters.m_applicationProtocol = TlsExtensionsUtilities.GetAlpnExtensionServer(
-                m_serverExtensions);
+                serverHelloExtensions);
             securityParameters.m_applicationProtocolSet = true;
 
             var sessionClientExtensions = m_clientExtensions;
-            var sessionServerExtensions = m_serverExtensions;
+            var sessionServerExtensions = serverHelloExtensions;
             if (securityParameters.IsResumedSession)
             {
                 if (securityParameters.CipherSuite != m_sessionParameters.CipherSuite
diff --git a/crypto/src/tls/TlsServerProtocol.cs b/crypto/src/tls/TlsServerProtocol.cs
index def8ae49b..efe055f1b 100644
--- a/crypto/src/tls/TlsServerProtocol.cs
+++ b/crypto/src/tls/TlsServerProtocol.cs
@@ -718,7 +718,7 @@ namespace Org.BouncyCastle.Tls
 
             ApplyMaxFragmentLengthExtension(securityParameters.MaxFragmentLength);
 
-            return new ServerHello(serverVersion, securityParameters.ServerRandom, m_tlsSession.SessionID,
+            return new ServerHello(serverVersion, securityParameters.ServerRandom, securityParameters.SessionID,
                 securityParameters.CipherSuite, m_serverExtensions);
         }