summary refs log tree commit diff
path: root/crypto/src/tls/DtlsServerProtocol.cs
diff options
context:
space:
mode:
Diffstat (limited to 'crypto/src/tls/DtlsServerProtocol.cs')
-rw-r--r--crypto/src/tls/DtlsServerProtocol.cs523
1 files changed, 321 insertions, 202 deletions
diff --git a/crypto/src/tls/DtlsServerProtocol.cs b/crypto/src/tls/DtlsServerProtocol.cs
index a4f8f4bc5..a3d04f01e 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;
 
@@ -38,16 +39,19 @@ namespace Org.BouncyCastle.Tls
             if (transport == null)
                 throw new ArgumentNullException("transport");
 
+            TlsServerContextImpl serverContext = new TlsServerContextImpl(server.Crypto);
+
             ServerHandshakeState state = new ServerHandshakeState();
             state.server = server;
-            state.serverContext = new TlsServerContextImpl(server.Crypto);
-            server.Init(state.serverContext);
-            state.serverContext.HandshakeBeginning(server);
+            state.serverContext = serverContext;
+
+            server.Init(serverContext);
+            serverContext.HandshakeBeginning(server);
 
-            SecurityParameters securityParameters = state.serverContext.SecurityParameters;
+            SecurityParameters securityParameters = serverContext.SecurityParameters;
             securityParameters.m_extendedPadding = server.ShouldUseExtendedPadding();
 
-            DtlsRecordLayer recordLayer = new DtlsRecordLayer(state.serverContext, state.server, transport);
+            DtlsRecordLayer recordLayer = new DtlsRecordLayer(serverContext, server, transport);
             server.NotifyCloseHandle(recordLayer);
 
             try
@@ -86,11 +90,12 @@ namespace Org.BouncyCastle.Tls
         internal virtual DtlsTransport ServerHandshake(ServerHandshakeState state, DtlsRecordLayer recordLayer,
             DtlsRequest request)
         {
-            SecurityParameters securityParameters = state.serverContext.SecurityParameters;
+            TlsServer server = state.server;
+            TlsServerContextImpl serverContext = state.serverContext;
+            SecurityParameters securityParameters = serverContext.SecurityParameters;
 
-            DtlsReliableHandshake handshake = new DtlsReliableHandshake(state.serverContext, recordLayer,
-                state.server.GetHandshakeTimeoutMillis(), TlsUtilities.GetHandshakeResendTimeMillis(state.server),
-                request);
+            DtlsReliableHandshake handshake = new DtlsReliableHandshake(serverContext, recordLayer,
+                server.GetHandshakeTimeoutMillis(), TlsUtilities.GetHandshakeResendTimeMillis(server), request);
 
             DtlsReliableHandshake.Message clientMessage = null;
 
@@ -109,36 +114,22 @@ namespace Org.BouncyCastle.Tls
                 {
                     throw new TlsFatalAlert(AlertDescription.unexpected_message);
                 }
+
+                clientMessage = null;
             }
             else
             {
                 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;
+                request = null;
             }
 
-            securityParameters.m_sessionID = state.tlsSession.SessionID;
-
-            state.server.NotifySession(state.tlsSession);
-
             {
                 byte[] serverHelloBody = GenerateServerHello(state, recordLayer);
 
                 // TODO[dtls13] Ideally, move this into GenerateServerHello once legacy_record_version clarified
                 {
-                    ProtocolVersion recordLayerVersion = state.serverContext.ServerVersion;
+                    ProtocolVersion recordLayerVersion = serverContext.ServerVersion;
                     recordLayer.ReadVersion = recordLayerVersion;
                     recordLayer.SetWriteVersion(recordLayerVersion);
                 }
@@ -148,20 +139,55 @@ namespace Org.BouncyCastle.Tls
 
             handshake.HandshakeHash.NotifyPrfDetermined();
 
-            var serverSupplementalData = state.server.GetServerSupplementalData();
+            if (securityParameters.IsResumedSession)
+            {
+                securityParameters.m_masterSecret = state.sessionMasterSecret;
+                recordLayer.InitPendingEpoch(TlsUtilities.InitCipher(serverContext));
+
+                // NOTE: Calculated exclusive of the Finished message itself
+                securityParameters.m_localVerifyData = TlsUtilities.CalculateVerifyData(serverContext,
+                    handshake.HandshakeHash, true);
+                handshake.SendMessage(HandshakeType.finished, securityParameters.LocalVerifyData);
+
+                // NOTE: Calculated exclusive of the actual Finished message from the client
+                securityParameters.m_peerVerifyData = TlsUtilities.CalculateVerifyData(serverContext,
+                    handshake.HandshakeHash, false);
+                ProcessFinished(handshake.ReceiveMessageBody(HandshakeType.finished),
+                    securityParameters.PeerVerifyData);
+
+                handshake.Finish();
+
+                if (securityParameters.IsExtendedMasterSecret)
+                {
+                    securityParameters.m_tlsUnique = securityParameters.LocalVerifyData;
+                }
+
+                securityParameters.m_localCertificate = state.sessionParameters.LocalCertificate;
+                securityParameters.m_peerCertificate = state.sessionParameters.PeerCertificate;
+                securityParameters.m_pskIdentity = state.sessionParameters.PskIdentity;
+                securityParameters.m_srpIdentity = state.sessionParameters.SrpIdentity;
+
+                serverContext.HandshakeComplete(server, state.tlsSession);
+
+                recordLayer.InitHeartbeat(state.heartbeat, HeartbeatMode.peer_allowed_to_send == state.heartbeatPolicy);
+
+                return new DtlsTransport(recordLayer, server.IgnoreCorruptDtlsRecords);
+            }
+
+            var serverSupplementalData = server.GetServerSupplementalData();
             if (serverSupplementalData != null)
             {
                 byte[] supplementalDataBody = GenerateSupplementalData(serverSupplementalData);
                 handshake.SendMessage(HandshakeType.supplemental_data, supplementalDataBody);
             }
 
-            state.keyExchange = TlsUtilities.InitKeyExchangeServer(state.serverContext, state.server);
+            state.keyExchange = TlsUtilities.InitKeyExchangeServer(serverContext, server);
 
             state.serverCredentials = null;
 
             if (!KeyExchangeAlgorithm.IsAnonymous(securityParameters.KeyExchangeAlgorithm))
             {
-                state.serverCredentials = TlsUtilities.EstablishServerCredentials(state.server);
+                state.serverCredentials = TlsUtilities.EstablishServerCredentials(server);
             }
 
             // Server certificate
@@ -179,7 +205,7 @@ namespace Org.BouncyCastle.Tls
 
                     serverCertificate = state.serverCredentials.Certificate;
 
-                    SendCertificateMessage(state.serverContext, handshake, serverCertificate, endPointHash);
+                    SendCertificateMessage(serverContext, handshake, serverCertificate, endPointHash);
                 }
                 securityParameters.m_tlsServerEndPoint = endPointHash.ToArray();
 
@@ -192,7 +218,7 @@ namespace Org.BouncyCastle.Tls
 
             if (securityParameters.StatusRequestVersion > 0)
             {
-                CertificateStatus certificateStatus = state.server.GetCertificateStatus();
+                CertificateStatus certificateStatus = server.GetCertificateStatus();
                 if (certificateStatus != null)
                 {
                     byte[] certificateStatusBody = GenerateCertificateStatus(state, certificateStatus);
@@ -208,7 +234,7 @@ namespace Org.BouncyCastle.Tls
 
             if (state.serverCredentials != null)
             {
-                state.certificateRequest = state.server.GetCertificateRequest();
+                state.certificateRequest = server.GetCertificateRequest();
 
                 if (null == state.certificateRequest)
                 {
@@ -222,7 +248,7 @@ namespace Org.BouncyCastle.Tls
                 }
                 else
                 {
-                    if (TlsUtilities.IsTlsV12(state.serverContext)
+                    if (TlsUtilities.IsTlsV12(serverContext)
                         != (state.certificateRequest.SupportedSignatureAlgorithms != null))
                     {
                         throw new TlsFatalAlert(AlertDescription.internal_error);
@@ -236,14 +262,14 @@ namespace Org.BouncyCastle.Tls
                     {
                         TlsUtilities.TrackHashAlgorithms(handshake.HandshakeHash, securityParameters.ServerSigAlgs);
 
-                        if (state.serverContext.Crypto.HasAnyStreamVerifiers(securityParameters.ServerSigAlgs))
+                        if (serverContext.Crypto.HasAnyStreamVerifiers(securityParameters.ServerSigAlgs))
                         {
                             handshake.HandshakeHash.ForceBuffering();
                         }
                     }
                     else
                     {
-                        if (state.serverContext.Crypto.HasAnyStreamVerifiersLegacy(state.certificateRequest.CertificateTypes))
+                        if (serverContext.Crypto.HasAnyStreamVerifiersLegacy(state.certificateRequest.CertificateTypes))
                         {
                             handshake.HandshakeHash.ForceBuffering();
                         }
@@ -270,7 +296,7 @@ namespace Org.BouncyCastle.Tls
             }
             else
             {
-                state.server.ProcessClientSupplementalData(null);
+                server.ProcessClientSupplementalData(null);
             }
 
             if (state.certificateRequest == null)
@@ -286,7 +312,7 @@ namespace Org.BouncyCastle.Tls
                 }
                 else
                 {
-                    if (TlsUtilities.IsTlsV12(state.serverContext))
+                    if (TlsUtilities.IsTlsV12(serverContext))
                     {
                         /*
                          * RFC 5246 If no suitable certificate is available, the client MUST send a
@@ -312,8 +338,8 @@ namespace Org.BouncyCastle.Tls
 
             securityParameters.m_sessionHash = TlsUtilities.GetCurrentPrfHash(handshake.HandshakeHash);
 
-            TlsProtocol.EstablishMasterSecret(state.serverContext, state.keyExchange);
-            recordLayer.InitPendingEpoch(TlsUtilities.InitCipher(state.serverContext));
+            TlsProtocol.EstablishMasterSecret(serverContext, state.keyExchange);
+            recordLayer.InitPendingEpoch(TlsUtilities.InitCipher(serverContext));
 
             /*
              * RFC 5246 7.4.8 This message is only sent following a client certificate that has signing
@@ -335,8 +361,10 @@ namespace Org.BouncyCastle.Tls
                 }
             }
 
+            clientMessage = null;
+
             // NOTE: Calculated exclusive of the actual Finished message from the client
-            securityParameters.m_peerVerifyData = TlsUtilities.CalculateVerifyData(state.serverContext,
+            securityParameters.m_peerVerifyData = TlsUtilities.CalculateVerifyData(serverContext,
                 handshake.HandshakeHash, false);
             ProcessFinished(handshake.ReceiveMessageBody(HandshakeType.finished), securityParameters.PeerVerifyData);
 
@@ -347,13 +375,13 @@ namespace Org.BouncyCastle.Tls
                 * is going to ignore any session ID it received once it sees the new_session_ticket message.
                 */
 
-                NewSessionTicket newSessionTicket = state.server.GetNewSessionTicket();
+                NewSessionTicket newSessionTicket = server.GetNewSessionTicket();
                 byte[] newSessionTicketBody = GenerateNewSessionTicket(state, newSessionTicket);
                 handshake.SendMessage(HandshakeType.new_session_ticket, newSessionTicketBody);
             }
 
             // NOTE: Calculated exclusive of the Finished message itself
-            securityParameters.m_localVerifyData = TlsUtilities.CalculateVerifyData(state.serverContext,
+            securityParameters.m_localVerifyData = TlsUtilities.CalculateVerifyData(serverContext,
                 handshake.HandshakeHash, true);
             handshake.SendMessage(HandshakeType.finished, securityParameters.LocalVerifyData);
 
@@ -365,7 +393,7 @@ namespace Org.BouncyCastle.Tls
                 .SetCipherSuite(securityParameters.CipherSuite)
                 .SetExtendedMasterSecret(securityParameters.IsExtendedMasterSecret)
                 .SetLocalCertificate(securityParameters.LocalCertificate)
-                .SetMasterSecret(state.serverContext.Crypto.AdoptSecret(state.sessionMasterSecret))
+                .SetMasterSecret(serverContext.Crypto.AdoptSecret(state.sessionMasterSecret))
                 .SetNegotiatedVersion(securityParameters.NegotiatedVersion)
                 .SetPeerCertificate(securityParameters.PeerCertificate)
                 .SetPskIdentity(securityParameters.PskIdentity)
@@ -374,15 +402,15 @@ 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;
 
-            state.serverContext.HandshakeComplete(state.server, state.tlsSession);
+            serverContext.HandshakeComplete(server, state.tlsSession);
 
             recordLayer.InitHeartbeat(state.heartbeat, HeartbeatMode.peer_allowed_to_send == state.heartbeatPolicy);
 
-            return new DtlsTransport(recordLayer, state.server.IgnoreCorruptDtlsRecords);
+            return new DtlsTransport(recordLayer, server.IgnoreCorruptDtlsRecords);
         }
 
         /// <exception cref="IOException"/>
@@ -416,12 +444,18 @@ namespace Org.BouncyCastle.Tls
         /// <exception cref="IOException"/>
         internal virtual byte[] GenerateServerHello(ServerHandshakeState state, DtlsRecordLayer recordLayer)
         {
-            TlsServerContextImpl context = state.serverContext;
-            SecurityParameters securityParameters = context.SecurityParameters;
+            TlsServer server = state.server;
+            TlsServerContextImpl serverContext = state.serverContext;
+            SecurityParameters securityParameters = serverContext.SecurityParameters;
+
+            // TODO[dtls13] Negotiate cipher suite first?
+
+            ProtocolVersion serverVersion;
 
-            ProtocolVersion server_version = state.server.GetServerVersion();
+            // NOT renegotiating
             {
-                if (!ProtocolVersion.Contains(context.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
@@ -430,28 +464,113 @@ namespace Org.BouncyCastle.Tls
                 //    : server_version;
 
                 //recordLayer.SetWriteVersion(legacy_record_version);
-                securityParameters.m_negotiatedVersion = server_version;
+                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);
 
-                TlsUtilities.NegotiatedVersionDtlsServer(context);
+            {
+                bool useGmtUnixTime = server.ShouldUseGmtUnixTime();
+
+                securityParameters.m_serverRandom = TlsProtocol.CreateRandomBlock(useGmtUnixTime, serverContext);
+
+                if (!serverVersion.Equals(ProtocolVersion.GetLatestDtls(server.GetProtocolVersions())))
+                {
+                    TlsUtilities.WriteDowngradeMarker(serverVersion, securityParameters.ServerRandom);
+                }
+            }
+
+            var clientHelloExtensions = state.clientHello.Extensions;
+
+            TlsSession sessionToResume = server.GetSessionToResume(state.clientHello.SessionID);
+
+            bool resumedSession = EstablishSession(state, sessionToResume);
+
+            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 useGmtUnixTime = ProtocolVersion.DTLSv12.IsEqualOrLaterVersionOf(server_version)
-                    && state.server.ShouldUseGmtUnixTime();
+                bool negotiateEms = false;
+
+                if (TlsUtilities.IsExtendedMasterSecretOptional(serverVersion) &&
+                    server.ShouldUseExtendedMasterSecret())
+                {
+                    if (TlsExtensionsUtilities.HasExtendedMasterSecretExtension(clientHelloExtensions))
+                    {
+                        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_serverRandom = TlsProtocol.CreateRandomBlock(useGmtUnixTime, context);
+                securityParameters.m_extendedMasterSecret = negotiateEms;
+            }
+
+            if (!resumedSession)
+            {
+                CancelSession(state);
 
-                if (!server_version.Equals(ProtocolVersion.GetLatestDtls(state.server.GetProtocolVersions())))
+                byte[] newSessionID = server.GetNewSessionID();
+                if (null == newSessionID)
                 {
-                    TlsUtilities.WriteDowngradeMarker(server_version, securityParameters.ServerRandom);
+                    newSessionID = TlsUtilities.EmptyBytes;
                 }
+
+                state.tlsSession = TlsUtilities.ImportSession(newSessionID, null);
             }
 
+            securityParameters.m_resumedSession = resumedSession;
+            securityParameters.m_sessionID = state.tlsSession.SessionID;
+
+            server.NotifySession(state.tlsSession);
+
+            TlsUtilities.NegotiatedVersionDtlsServer(serverContext);
+
             {
-                int cipherSuite = ValidateSelectedCipherSuite(state.server.GetSelectedCipherSuite(),
+                int cipherSuite = ValidateSelectedCipherSuite(server.GetSelectedCipherSuite(),
                     AlertDescription.internal_error);
 
-                if (!TlsUtilities.IsValidCipherSuiteSelection(state.offeredCipherSuites, cipherSuite) ||
+                if (!TlsUtilities.IsValidCipherSuiteSelection(state.clientHello.CipherSuites, cipherSuite) ||
                     !TlsUtilities.IsValidVersionForCipherSuite(cipherSuite, securityParameters.NegotiatedVersion))
                 {
                     throw new TlsFatalAlert(AlertDescription.internal_error);
@@ -460,76 +579,54 @@ namespace Org.BouncyCastle.Tls
                 TlsUtilities.NegotiatedCipherSuite(securityParameters, cipherSuite);
             }
 
-            state.serverExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(
-                state.server.GetServerExtensions());
-
-            state.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
-                    && state.server.ShouldUseExtendedMasterSecret();
-
-                if (securityParameters.IsExtendedMasterSecret)
-                {
-                    TlsExtensionsUtilities.AddExtendedMasterSecretExtension(state.serverExtensions);
-                }
-                else if (state.server.RequiresExtendedMasterSecret())
-                {
-                    throw new TlsFatalAlert(AlertDescription.handshake_failure);
-                }
-                else if (state.resumedSession && !state.server.AllowLegacyResumption())
-                {
-                    throw new TlsFatalAlert(AlertDescription.internal_error);
-                }
+                state.serverExtensions.Remove(ExtensionType.extended_master_secret);
             }
 
             // Heartbeats
@@ -539,13 +636,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;
@@ -560,7 +650,7 @@ namespace Org.BouncyCastle.Tls
                 var serverConnectionID = TlsExtensionsUtilities.GetConnectionIDExtension(state.serverExtensions);
                 if (serverConnectionID != null)
                 {
-                    var clientConnectionID = TlsExtensionsUtilities.GetConnectionIDExtension(state.clientExtensions)
+                    var clientConnectionID = TlsExtensionsUtilities.GetConnectionIDExtension(clientHelloExtensions)
                         ?? throw new TlsFatalAlert(AlertDescription.internal_error);
 
                     securityParameters.m_connectionIDLocal = clientConnectionID;
@@ -568,26 +658,19 @@ 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(
                     state.serverExtensions);
 
-                securityParameters.m_maxFragmentLength = EvaluateMaxFragmentLengthExtension(state.resumedSession,
-                    state.clientExtensions, state.serverExtensions, AlertDescription.internal_error);
+                securityParameters.m_maxFragmentLength = TlsUtilities.ProcessMaxFragmentLengthExtension(
+                    resumedSession ? null : clientHelloExtensions, state.serverExtensions,
+                    AlertDescription.internal_error);
 
-                securityParameters.m_truncatedHmac = TlsExtensionsUtilities.HasTruncatedHmacExtension(state.serverExtensions);
+                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 (!state.resumedSession)
+                if (!resumedSession)
                 {
                     // TODO[tls13] See RFC 8446 4.4.2.1
                     if (TlsUtilities.HasExpectedEmptyExtensionData(state.serverExtensions,
@@ -600,26 +683,30 @@ namespace Org.BouncyCastle.Tls
                     {
                         securityParameters.m_statusRequestVersion = 1;
                     }
-                }
 
-                state.expectSessionTicket = !state.resumedSession
-                    && TlsUtilities.HasExpectedEmptyExtensionData(state.serverExtensions, ExtensionType.session_ticket,
-                        AlertDescription.internal_error);
-            }
+                    securityParameters.m_clientCertificateType = TlsUtilities.ProcessClientCertificateTypeExtension(
+                        clientHelloExtensions, state.serverExtensions, AlertDescription.internal_error);
+                    securityParameters.m_serverCertificateType = TlsUtilities.ProcessServerCertificateTypeExtension(
+                        clientHelloExtensions, state.serverExtensions, AlertDescription.internal_error);
 
-            ApplyMaxFragmentLengthExtension(recordLayer, securityParameters.MaxFragmentLength);
+                    state.expectSessionTicket = TlsUtilities.HasExpectedEmptyExtensionData(state.serverExtensions,
+                        ExtensionType.session_ticket, AlertDescription.internal_error);
+                }
+            }
 
+            ServerHello serverHello = new ServerHello(serverVersion, securityParameters.ServerRandom,
+                securityParameters.SessionID, securityParameters.CipherSuite, state.serverExtensions);
 
+            state.clientHello = null;
 
-            ServerHello serverHello = new ServerHello(legacy_version, securityParameters.ServerRandom,
-                state.tlsSession.SessionID, securityParameters.CipherSuite, state.serverExtensions);
+            ApplyMaxFragmentLengthExtension(recordLayer, securityParameters.MaxFragmentLength);
 
             MemoryStream buf = new MemoryStream();
-            serverHello.Encode(state.serverContext, buf);
+            serverHello.Encode(serverContext, buf);
             return buf.ToArray();
         }
 
-        protected virtual void InvalidateSession(ServerHandshakeState state)
+        protected virtual void CancelSession(ServerHandshakeState state)
         {
             if (state.sessionMasterSecret != null)
             {
@@ -633,11 +720,53 @@ namespace Org.BouncyCastle.Tls
                 state.sessionParameters = null;
             }
 
+            state.tlsSession = null;
+        }
+
+        protected virtual bool EstablishSession(ServerHandshakeState 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.serverContext.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(ServerHandshakeState state)
+        {
             if (state.tlsSession != null)
             {
                 state.tlsSession.Invalidate();
-                state.tlsSession = null;
             }
+
+            CancelSession(state);
         }
 
         /// <exception cref="IOException"/>
@@ -657,8 +786,7 @@ namespace Org.BouncyCastle.Tls
 
             Certificate.ParseOptions options = new Certificate.ParseOptions()
             {
-                CertificateType = TlsExtensionsUtilities.GetClientCertificateTypeExtensionServer(
-                    state.clientExtensions, CertificateType.X509),
+                CertificateType = state.serverContext.SecurityParameters.ClientCertificateType,
                 MaxChainLength = state.server.GetMaxCertificateChainLength(),
             };
 
@@ -678,12 +806,13 @@ namespace Org.BouncyCastle.Tls
 
             MemoryStream buf = new MemoryStream(body, false);
 
-            TlsServerContextImpl context = state.serverContext;
-            DigitallySigned certificateVerify = DigitallySigned.Parse(context, buf);
+            TlsServerContextImpl serverContext = state.serverContext;
+            DigitallySigned certificateVerify = DigitallySigned.Parse(serverContext, buf);
 
             TlsProtocol.AssertEmpty(buf);
 
-            TlsUtilities.VerifyCertificateVerifyClient(context, state.certificateRequest, certificateVerify, handshakeHash);
+            TlsUtilities.VerifyCertificateVerifyClient(serverContext, state.certificateRequest, certificateVerify,
+                handshakeHash);
         }
 
         /// <exception cref="IOException"/>
@@ -697,67 +826,69 @@ namespace Org.BouncyCastle.Tls
         /// <exception cref="IOException"/>
         protected virtual void ProcessClientHello(ServerHandshakeState state, ClientHello clientHello)
         {
+            state.clientHello = clientHello;
+
             // TODO Read RFCs for guidance on the expected record layer version number
             ProtocolVersion legacy_version = clientHello.Version;
-            state.offeredCipherSuites = clientHello.CipherSuites;
-
-            /*
-             * 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.
-             */
-            state.clientExtensions = clientHello.Extensions;
+            int[] offeredCipherSuites = clientHello.CipherSuites;
+            var clientHelloExtensions = clientHello.Extensions;
 
 
 
-            TlsServerContextImpl context = state.serverContext;
-            SecurityParameters securityParameters = context.SecurityParameters;
+            TlsServer server = state.server;
+            TlsServerContextImpl serverContext = state.serverContext;
+            SecurityParameters securityParameters = serverContext.SecurityParameters;
 
             if (!legacy_version.IsDtls)
                 throw new TlsFatalAlert(AlertDescription.illegal_parameter);
 
-            context.SetRsaPreMasterSecretVersion(legacy_version);
+            serverContext.SetRsaPreMasterSecretVersion(legacy_version);
 
-            context.SetClientSupportedVersions(
-                TlsExtensionsUtilities.GetSupportedVersionsExtensionClient(state.clientExtensions));
+            serverContext.SetClientSupportedVersions(
+                TlsExtensionsUtilities.GetSupportedVersionsExtensionClient(clientHelloExtensions));
 
             ProtocolVersion client_version = legacy_version;
-            if (null == context.ClientSupportedVersions)
+            if (null == serverContext.ClientSupportedVersions)
             {
                 if (client_version.IsLaterVersionOf(ProtocolVersion.DTLSv12))
                 {
                     client_version = ProtocolVersion.DTLSv12;
                 }
 
-                context.SetClientSupportedVersions(client_version.DownTo(ProtocolVersion.DTLSv10));
+                serverContext.SetClientSupportedVersions(client_version.DownTo(ProtocolVersion.DTLSv10));
             }
             else
             {
-                client_version = ProtocolVersion.GetLatestDtls(context.ClientSupportedVersions);
+                client_version = ProtocolVersion.GetLatestDtls(serverContext.ClientSupportedVersions);
             }
 
             if (!ProtocolVersion.SERVER_EARLIEST_SUPPORTED_DTLS.IsEqualOrEarlierVersionOf(client_version))
                 throw new TlsFatalAlert(AlertDescription.protocol_version);
 
-            context.SetClientVersion(client_version);
+            serverContext.SetClientVersion(client_version);
 
-            state.server.NotifyClientVersion(context.ClientVersion);
+            server.NotifyClientVersion(serverContext.ClientVersion);
 
             securityParameters.m_clientRandom = clientHello.Random;
 
-            state.server.NotifyFallback(Arrays.Contains(state.offeredCipherSuites, CipherSuite.TLS_FALLBACK_SCSV));
+            server.NotifyFallback(Arrays.Contains(offeredCipherSuites, CipherSuite.TLS_FALLBACK_SCSV));
 
-            state.server.NotifyOfferedCipherSuites(state.offeredCipherSuites);
+            server.NotifyOfferedCipherSuites(offeredCipherSuites);
 
             /*
              * TODO[resumption] Check RFC 7627 5.4. for required behaviour 
              */
 
-            /*
-             * RFC 5746 3.6. Server Behavior: Initial Handshake
-             */
+            byte[] clientRenegExtData = TlsUtilities.GetExtensionData(clientHelloExtensions,
+                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.
@@ -768,18 +899,12 @@ namespace Org.BouncyCastle.Tls
                  * TLS_EMPTY_RENEGOTIATION_INFO_SCSV SCSV. If it does, set the secure_renegotiation flag
                  * to TRUE.
                  */
-                if (Arrays.Contains(state.offeredCipherSuites, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV))
+                if (Arrays.Contains(offeredCipherSuites, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV))
                 {
                     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
@@ -788,7 +913,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);
@@ -796,18 +921,15 @@ namespace Org.BouncyCastle.Tls
                 }
             }
 
-            state.server.NotifySecureRenegotiation(securityParameters.IsSecureRenegotiation);
-
-            state.offeredExtendedMasterSecret = TlsExtensionsUtilities.HasExtendedMasterSecretExtension(
-                state.clientExtensions);
+            server.NotifySecureRenegotiation(securityParameters.IsSecureRenegotiation);
 
-            if (state.clientExtensions != null)
+            if (clientHelloExtensions != null)
             {
                 // NOTE: Validates the padding extension data, if present
-                TlsExtensionsUtilities.GetPaddingExtension(state.clientExtensions);
+                TlsExtensionsUtilities.GetPaddingExtension(clientHelloExtensions);
 
                 securityParameters.m_clientServerNames = TlsExtensionsUtilities.GetServerNameExtensionClient(
-                    state.clientExtensions);
+                    clientHelloExtensions);
 
                 /*
                  * RFC 5246 7.4.1.4.1. Note: this extension is not meaningful for TLS versions prior
@@ -815,28 +937,28 @@ namespace Org.BouncyCastle.Tls
                  */
                 if (TlsUtilities.IsSignatureAlgorithmsExtensionAllowed(client_version))
                 {
-                    TlsUtilities.EstablishClientSigAlgs(securityParameters, state.clientExtensions);
+                    TlsUtilities.EstablishClientSigAlgs(securityParameters, clientHelloExtensions);
                 }
 
                 securityParameters.m_clientSupportedGroups = TlsExtensionsUtilities.GetSupportedGroupsExtension(
-                    state.clientExtensions);
+                    clientHelloExtensions);
 
                 // Heartbeats
                 {
                     HeartbeatExtension heartbeatExtension = TlsExtensionsUtilities.GetHeartbeatExtension(
-                        state.clientExtensions);
+                        clientHelloExtensions);
                     if (null != heartbeatExtension)
                     {
                         if (HeartbeatMode.peer_allowed_to_send == heartbeatExtension.Mode)
                         {
-                            state.heartbeat = state.server.GetHeartbeat();
+                            state.heartbeat = server.GetHeartbeat();
                         }
 
-                        state.heartbeatPolicy = state.server.GetHeartbeatPolicy();
+                        state.heartbeatPolicy = server.GetHeartbeatPolicy();
                     }
                 }
 
-                state.server.ProcessClientExtensions(state.clientExtensions);
+                server.ProcessClientExtensions(clientHelloExtensions);
             }
         }
 
@@ -875,11 +997,8 @@ namespace Org.BouncyCastle.Tls
             internal SessionParameters sessionParameters = null;
             internal TlsSecret sessionMasterSecret = null;
             internal SessionParameters.Builder sessionParametersBuilder = null;
-            internal int[] offeredCipherSuites = null;
-            internal IDictionary<int, byte[]> clientExtensions = null;
+            internal ClientHello clientHello = null;
             internal IDictionary<int, byte[]> serverExtensions = null;
-            internal bool offeredExtendedMasterSecret = false;
-            internal bool resumedSession = false;
             internal bool expectSessionTicket = false;
             internal TlsKeyExchange keyExchange = null;
             internal TlsCredentials serverCredentials = null;