summary refs log tree commit diff
path: root/crypto/src/tls/DtlsClientProtocol.cs
diff options
context:
space:
mode:
Diffstat (limited to 'crypto/src/tls/DtlsClientProtocol.cs')
-rw-r--r--crypto/src/tls/DtlsClientProtocol.cs511
1 files changed, 319 insertions, 192 deletions
diff --git a/crypto/src/tls/DtlsClientProtocol.cs b/crypto/src/tls/DtlsClientProtocol.cs
index c1bad2e6f..2b132f564 100644
--- a/crypto/src/tls/DtlsClientProtocol.cs
+++ b/crypto/src/tls/DtlsClientProtocol.cs
@@ -23,43 +23,19 @@ namespace Org.BouncyCastle.Tls
             if (transport == null)
                 throw new ArgumentNullException("transport");
 
+            TlsClientContextImpl clientContext = new TlsClientContextImpl(client.Crypto);
+
             ClientHandshakeState state = new ClientHandshakeState();
             state.client = client;
-            state.clientContext = new TlsClientContextImpl(client.Crypto);
+            state.clientContext = clientContext;
 
-            client.Init(state.clientContext);
-            state.clientContext.HandshakeBeginning(client);
+            client.Init(clientContext);
+            clientContext.HandshakeBeginning(client);
 
-            SecurityParameters securityParameters = state.clientContext.SecurityParameters;
+            SecurityParameters securityParameters = clientContext.SecurityParameters;
             securityParameters.m_extendedPadding = client.ShouldUseExtendedPadding();
 
-            TlsSession sessionToResume = state.client.GetSessionToResume();
-            if (sessionToResume != null && sessionToResume.IsResumable)
-            {
-                SessionParameters sessionParameters = sessionToResume.ExportSessionParameters();
-
-                /*
-                 * NOTE: If we ever enable session resumption without extended_master_secret, then
-                 * renegotiation MUST be disabled (see RFC 7627 5.4).
-                 */
-                if (sessionParameters != null
-                    && (sessionParameters.IsExtendedMasterSecret
-                        || (!state.client.RequiresExtendedMasterSecret() && state.client.AllowLegacyResumption())))
-                {
-                    TlsSecret masterSecret = sessionParameters.MasterSecret;
-                    lock (masterSecret)
-                    {
-                        if (masterSecret.IsAlive())
-                        {
-                            state.tlsSession = sessionToResume;
-                            state.sessionParameters = sessionParameters;
-                            state.sessionMasterSecret = state.clientContext.Crypto.AdoptSecret(masterSecret);
-                        }
-                    }
-                }
-            }
-
-            DtlsRecordLayer recordLayer = new DtlsRecordLayer(state.clientContext, state.client, transport);
+            DtlsRecordLayer recordLayer = new DtlsRecordLayer(clientContext, client, transport);
             client.NotifyCloseHandle(recordLayer);
 
             try
@@ -97,11 +73,12 @@ namespace Org.BouncyCastle.Tls
         /// <exception cref="IOException"/>
         internal virtual DtlsTransport ClientHandshake(ClientHandshakeState state, DtlsRecordLayer recordLayer)
         {
-            SecurityParameters securityParameters = state.clientContext.SecurityParameters;
+            TlsClient client = state.client;
+            TlsClientContextImpl clientContext = state.clientContext;
+            SecurityParameters securityParameters = clientContext.SecurityParameters;
 
-            DtlsReliableHandshake handshake = new DtlsReliableHandshake(state.clientContext, recordLayer,
-                state.client.GetHandshakeTimeoutMillis(), TlsUtilities.GetHandshakeResendTimeMillis(state.client),
-                null);
+            DtlsReliableHandshake handshake = new DtlsReliableHandshake(clientContext, recordLayer,
+                client.GetHandshakeTimeoutMillis(), TlsUtilities.GetHandshakeResendTimeMillis(client), null);
 
             byte[] clientHelloBody = GenerateClientHello(state);
 
@@ -131,6 +108,8 @@ namespace Org.BouncyCastle.Tls
                 recordLayer.SetWriteVersion(recordLayerVersion);
 
                 ProcessServerHello(state, serverMessage.Body);
+
+                ApplyMaxFragmentLengthExtension(recordLayer, securityParameters.MaxFragmentLength);
             }
             else
             {
@@ -139,21 +118,19 @@ namespace Org.BouncyCastle.Tls
 
             handshake.HandshakeHash.NotifyPrfDetermined();
 
-            ApplyMaxFragmentLengthExtension(recordLayer, securityParameters.MaxFragmentLength);
-
-            if (state.resumedSession)
+            if (securityParameters.IsResumedSession)
             {
                 securityParameters.m_masterSecret = state.sessionMasterSecret;
-                recordLayer.InitPendingEpoch(TlsUtilities.InitCipher(state.clientContext));
+                recordLayer.InitPendingEpoch(TlsUtilities.InitCipher(clientContext));
 
                 // NOTE: Calculated exclusive of the actual Finished message from the server
-                securityParameters.m_peerVerifyData = TlsUtilities.CalculateVerifyData(state.clientContext,
+                securityParameters.m_peerVerifyData = TlsUtilities.CalculateVerifyData(clientContext,
                     handshake.HandshakeHash, true);
                 ProcessFinished(handshake.ReceiveMessageBody(HandshakeType.finished),
                     securityParameters.PeerVerifyData);
 
                 // NOTE: Calculated exclusive of the Finished message itself
-                securityParameters.m_localVerifyData = TlsUtilities.CalculateVerifyData(state.clientContext,
+                securityParameters.m_localVerifyData = TlsUtilities.CalculateVerifyData(clientContext,
                     handshake.HandshakeHash, false);
                 handshake.SendMessage(HandshakeType.finished, securityParameters.LocalVerifyData);
 
@@ -169,12 +146,11 @@ namespace Org.BouncyCastle.Tls
                 securityParameters.m_pskIdentity = state.sessionParameters.PskIdentity;
                 securityParameters.m_srpIdentity = state.sessionParameters.SrpIdentity;
 
-                state.clientContext.HandshakeComplete(state.client, state.tlsSession);
+                clientContext.HandshakeComplete(client, state.tlsSession);
 
-                recordLayer.InitHeartbeat(state.heartbeat,
-                    HeartbeatMode.peer_allowed_to_send == state.heartbeatPolicy);
+                recordLayer.InitHeartbeat(state.heartbeat, HeartbeatMode.peer_allowed_to_send == state.heartbeatPolicy);
 
-                return new DtlsTransport(recordLayer, state.client.IgnoreCorruptDtlsRecords);
+                return new DtlsTransport(recordLayer, client.IgnoreCorruptDtlsRecords);
             }
 
             InvalidateSession(state);
@@ -189,10 +165,10 @@ namespace Org.BouncyCastle.Tls
             }
             else
             {
-                state.client.ProcessServerSupplementalData(null);
+                client.ProcessServerSupplementalData(null);
             }
 
-            state.keyExchange = TlsUtilities.InitKeyExchangeClient(state.clientContext, state.client);
+            state.keyExchange = TlsUtilities.InitKeyExchangeClient(clientContext, client);
 
             if (serverMessage.Type == HandshakeType.certificate)
             {
@@ -218,7 +194,7 @@ namespace Org.BouncyCastle.Tls
                 // Okay, CertificateStatus is optional
             }
 
-            TlsUtilities.ProcessServerCertificate(state.clientContext, state.certificateStatus, state.keyExchange,
+            TlsUtilities.ProcessServerCertificate(clientContext, state.certificateStatus, state.keyExchange,
                 state.authentication, state.clientExtensions, state.serverExtensions);
 
             if (serverMessage.Type == HandshakeType.server_key_exchange)
@@ -308,7 +284,7 @@ namespace Org.BouncyCastle.Tls
                 state.keyExchange.ProcessClientCredentials(clientAuthCredentials);                    
             }
 
-            var clientSupplementalData = state.client.GetClientSupplementalData();
+            var clientSupplementalData = client.GetClientSupplementalData();
             if (clientSupplementalData != null)
             {
                 byte[] supplementalDataBody = GenerateSupplementalData(clientSupplementalData);
@@ -317,7 +293,7 @@ namespace Org.BouncyCastle.Tls
 
             if (null != state.certificateRequest)
             {
-                SendCertificateMessage(state.clientContext, handshake, clientAuthCertificate, null);
+                SendCertificateMessage(clientContext, handshake, clientAuthCertificate, null);
             }
 
             byte[] clientKeyExchangeBody = GenerateClientKeyExchange(state);
@@ -325,12 +301,12 @@ namespace Org.BouncyCastle.Tls
 
             securityParameters.m_sessionHash = TlsUtilities.GetCurrentPrfHash(handshake.HandshakeHash);
 
-            TlsProtocol.EstablishMasterSecret(state.clientContext, state.keyExchange);
-            recordLayer.InitPendingEpoch(TlsUtilities.InitCipher(state.clientContext));
+            TlsProtocol.EstablishMasterSecret(clientContext, state.keyExchange);
+            recordLayer.InitPendingEpoch(TlsUtilities.InitCipher(clientContext));
 
             if (clientAuthSigner != null)
             {
-                DigitallySigned certificateVerify = TlsUtilities.GenerateCertificateVerifyClient(state.clientContext,
+                DigitallySigned certificateVerify = TlsUtilities.GenerateCertificateVerifyClient(clientContext,
                     clientAuthSigner, clientAuthAlgorithm, clientAuthStreamSigner, handshake.HandshakeHash);
                 byte[] certificateVerifyBody = GenerateCertificateVerify(state, certificateVerify);
                 handshake.SendMessage(HandshakeType.certificate_verify, certificateVerifyBody);
@@ -338,7 +314,7 @@ namespace Org.BouncyCastle.Tls
 
             handshake.PrepareToFinish();
 
-            securityParameters.m_localVerifyData = TlsUtilities.CalculateVerifyData(state.clientContext,
+            securityParameters.m_localVerifyData = TlsUtilities.CalculateVerifyData(clientContext,
                 handshake.HandshakeHash, false);
             handshake.SendMessage(HandshakeType.finished, securityParameters.LocalVerifyData);
 
@@ -364,7 +340,7 @@ namespace Org.BouncyCastle.Tls
             }
 
             // NOTE: Calculated exclusive of the actual Finished message from the server
-            securityParameters.m_peerVerifyData = TlsUtilities.CalculateVerifyData(state.clientContext,
+            securityParameters.m_peerVerifyData = TlsUtilities.CalculateVerifyData(clientContext,
                 handshake.HandshakeHash, true);
             ProcessFinished(handshake.ReceiveMessageBody(HandshakeType.finished), securityParameters.PeerVerifyData);
 
@@ -376,7 +352,7 @@ namespace Org.BouncyCastle.Tls
                 .SetCipherSuite(securityParameters.CipherSuite)
                 .SetExtendedMasterSecret(securityParameters.IsExtendedMasterSecret)
                 .SetLocalCertificate(securityParameters.LocalCertificate)
-                .SetMasterSecret(state.clientContext.Crypto.AdoptSecret(state.sessionMasterSecret))
+                .SetMasterSecret(clientContext.Crypto.AdoptSecret(state.sessionMasterSecret))
                 .SetNegotiatedVersion(securityParameters.NegotiatedVersion)
                 .SetPeerCertificate(securityParameters.PeerCertificate)
                 .SetPskIdentity(securityParameters.PskIdentity)
@@ -389,11 +365,11 @@ namespace Org.BouncyCastle.Tls
 
             securityParameters.m_tlsUnique = securityParameters.LocalVerifyData;
 
-            state.clientContext.HandshakeComplete(state.client, state.tlsSession);
+            clientContext.HandshakeComplete(client, state.tlsSession);
 
             recordLayer.InitHeartbeat(state.heartbeat, HeartbeatMode.peer_allowed_to_send == state.heartbeatPolicy);
 
-            return new DtlsTransport(recordLayer, state.client.IgnoreCorruptDtlsRecords);
+            return new DtlsTransport(recordLayer, client.IgnoreCorruptDtlsRecords);
         }
 
         /// <exception cref="IOException"/>
@@ -408,56 +384,107 @@ namespace Org.BouncyCastle.Tls
         /// <exception cref="IOException"/>
         protected virtual byte[] GenerateClientHello(ClientHandshakeState state)
         {
-            TlsClientContextImpl context = state.clientContext;
-            SecurityParameters securityParameters = context.SecurityParameters;
+            TlsClient client = state.client;
+            TlsClientContextImpl clientContext = state.clientContext;
+            SecurityParameters securityParameters = clientContext.SecurityParameters;
 
-            context.SetClientSupportedVersions(state.client.GetProtocolVersions());
+            ProtocolVersion[] supportedVersions = client.GetProtocolVersions();
 
-            ProtocolVersion client_version = ProtocolVersion.GetLatestDtls(context.ClientSupportedVersions);
-            if (!ProtocolVersion.IsSupportedDtlsVersionClient(client_version))
+            ProtocolVersion earliestVersion = ProtocolVersion.GetEarliestDtls(supportedVersions);
+            ProtocolVersion latestVersion = ProtocolVersion.GetLatestDtls(supportedVersions);
+
+            if (!ProtocolVersion.IsSupportedDtlsVersionClient(latestVersion))
                 throw new TlsFatalAlert(AlertDescription.internal_error);
 
-            context.SetClientVersion(client_version);
+            clientContext.SetClientVersion(latestVersion);
+            clientContext.SetClientSupportedVersions(supportedVersions);
+
+            bool offeringDtlsV12Minus = ProtocolVersion.DTLSv12.IsEqualOrLaterVersionOf(earliestVersion);
+            bool offeringDtlsV13Plus = ProtocolVersion.DTLSv13.IsEqualOrEarlierVersionOf(latestVersion);
 
             {
-                bool useGmtUnixTime = ProtocolVersion.DTLSv12.IsEqualOrLaterVersionOf(client_version)
-                    && state.client.ShouldUseGmtUnixTime();
+                bool useGmtUnixTime = !offeringDtlsV13Plus && client.ShouldUseGmtUnixTime();
 
-                securityParameters.m_clientRandom = TlsProtocol.CreateRandomBlock(useGmtUnixTime, state.clientContext);
+                securityParameters.m_clientRandom = TlsProtocol.CreateRandomBlock(useGmtUnixTime, clientContext);
             }
 
-            byte[] session_id = TlsUtilities.GetSessionID(state.tlsSession);
+            TlsSession sessionToResume = offeringDtlsV12Minus ? client.GetSessionToResume() : null;
+
+            bool fallback = client.IsFallback();
+
+            state.offeredCipherSuites = client.GetCipherSuites();
+
+            state.clientExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(client.GetClientExtensions());
 
-            bool fallback = state.client.IsFallback();
+            bool shouldUseEms = client.ShouldUseExtendedMasterSecret();
 
-            state.offeredCipherSuites = state.client.GetCipherSuites();
+            EstablishSession(state, sessionToResume);
 
-            if (session_id.Length > 0 && state.sessionParameters != null)
+            byte[] legacy_session_id = TlsUtilities.GetSessionID(state.tlsSession);
+
+            if (legacy_session_id.Length > 0)
             {
                 if (!Arrays.Contains(state.offeredCipherSuites, state.sessionParameters.CipherSuite))
                 {
-                    session_id = TlsUtilities.EmptyBytes;
+                    legacy_session_id = TlsUtilities.EmptyBytes;
+                }
+            }
+
+            ProtocolVersion sessionVersion = null;
+            if (legacy_session_id.Length > 0)
+            {
+                sessionVersion = state.sessionParameters.NegotiatedVersion;
+
+                if (!ProtocolVersion.Contains(supportedVersions, sessionVersion))
+                {
+                    legacy_session_id = TlsUtilities.EmptyBytes;
                 }
             }
 
-            state.clientExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(
-                state.client.GetClientExtensions());
+            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;
+                    }
+                }
+            }
 
-            ProtocolVersion legacy_version = client_version;
-            if (client_version.IsLaterVersionOf(ProtocolVersion.DTLSv12))
+            if (legacy_session_id.Length < 1)
+            {
+                CancelSession(state);
+            }
+
+            client.NotifySessionToResume(state.tlsSession);
+
+            ProtocolVersion legacy_version = latestVersion;
+            if (offeringDtlsV13Plus)
             {
                 legacy_version = ProtocolVersion.DTLSv12;
 
-                TlsExtensionsUtilities.AddSupportedVersionsExtensionClient(state.clientExtensions,
-                    context.ClientSupportedVersions);
+                TlsExtensionsUtilities.AddSupportedVersionsExtensionClient(state.clientExtensions, supportedVersions);
+
+                /*
+                 * RFC 9147 5. DTLS implementations do not use the TLS 1.3 "compatibility mode" [..].
+                 */
             }
 
-            context.SetRsaPreMasterSecretVersion(legacy_version);
+            clientContext.SetRsaPreMasterSecretVersion(legacy_version);
 
             securityParameters.m_clientServerNames = TlsExtensionsUtilities.GetServerNameExtensionClient(
                 state.clientExtensions);
 
-            if (TlsUtilities.IsSignatureAlgorithmsExtensionAllowed(client_version))
+            if (TlsUtilities.IsSignatureAlgorithmsExtensionAllowed(latestVersion))
             {
                 TlsUtilities.EstablishClientSigAlgs(securityParameters, state.clientExtensions);
             }
@@ -465,18 +492,22 @@ namespace Org.BouncyCastle.Tls
             securityParameters.m_clientSupportedGroups = TlsExtensionsUtilities.GetSupportedGroupsExtension(
                 state.clientExtensions);
 
-            state.clientAgreements = TlsUtilities.AddKeyShareToClientHello(state.clientContext, state.client,
+            // TODO[dtls13]
+            //state.clientBinders = TlsUtilities.AddPreSharedKeyToClientHello(clientContext, client,
+            //    state.clientExtensions, state.offeredCipherSuites);
+            state.clientBinders = null;
+
+            // TODO[tls13-psk] Perhaps don't add key_share if external PSK(s) offered and 'psk_dhe_ke' not offered
+            state.clientAgreements = TlsUtilities.AddKeyShareToClientHello(clientContext, client,
                 state.clientExtensions);
 
-            if (TlsUtilities.IsExtendedMasterSecretOptionalDtls(context.ClientSupportedVersions)
-                && state.client.ShouldUseExtendedMasterSecret())
+            if (shouldUseEms && TlsUtilities.IsExtendedMasterSecretOptional(supportedVersions))
             {
                 TlsExtensionsUtilities.AddExtendedMasterSecretExtension(state.clientExtensions);
             }
-            else if (!TlsUtilities.IsTlsV13(client_version)
-                && state.client.RequiresExtendedMasterSecret())
+            else
             {
-                throw new TlsFatalAlert(AlertDescription.internal_error);
+                state.clientExtensions.Remove(ExtensionType.extended_master_secret);
             }
 
             // Cipher Suites (and SCSV)
@@ -512,8 +543,8 @@ namespace Org.BouncyCastle.Tls
 
             // Heartbeats
             {
-                state.heartbeat = state.client.GetHeartbeat();
-                state.heartbeatPolicy = state.client.GetHeartbeatPolicy();
+                state.heartbeat = client.GetHeartbeat();
+                state.heartbeatPolicy = client.GetHeartbeatPolicy();
 
                 if (null != state.heartbeat || HeartbeatMode.peer_allowed_to_send == state.heartbeatPolicy)
                 {
@@ -524,11 +555,18 @@ namespace Org.BouncyCastle.Tls
 
 
 
-            ClientHello clientHello = new ClientHello(legacy_version, securityParameters.ClientRandom, session_id,
-                cookie: TlsUtilities.EmptyBytes, state.offeredCipherSuites, state.clientExtensions, 0);
+            int bindersSize = null == state.clientBinders ? 0 : state.clientBinders.m_bindersSize;
 
+            ClientHello clientHello = new ClientHello(legacy_version, securityParameters.ClientRandom,
+                legacy_session_id, cookie: TlsUtilities.EmptyBytes, state.offeredCipherSuites, state.clientExtensions,
+                bindersSize);
+
+            /*
+             * TODO[dtls13] See TlsClientProtocol.SendClientHelloMessage for how to prepare/encode binders and also
+             * consider the impact of binders on cookie patching after HelloVerifyRequest.
+             */
             MemoryStream buf = new MemoryStream();
-            clientHello.Encode(state.clientContext, buf);
+            clientHello.Encode(clientContext, buf);
             return buf.ToArray();
         }
 
@@ -540,7 +578,7 @@ namespace Org.BouncyCastle.Tls
             return buf.ToArray();
         }
 
-        protected virtual void InvalidateSession(ClientHandshakeState state)
+        protected virtual void CancelSession(ClientHandshakeState state)
         {
             if (state.sessionMasterSecret != null)
             {
@@ -554,11 +592,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"/>
@@ -580,10 +660,6 @@ namespace Org.BouncyCastle.Tls
             TlsProtocol.AssertEmpty(buf);
 
             state.certificateRequest = TlsUtilities.ValidateCertificateRequest(certificateRequest, state.keyExchange);
-
-            state.clientContext.SecurityParameters.m_clientCertificateType =
-                TlsExtensionsUtilities.GetClientCertificateTypeExtensionServer(state.serverExtensions,
-                    CertificateType.X509);
         }
 
         /// <exception cref="IOException"/>
@@ -644,24 +720,60 @@ namespace Org.BouncyCastle.Tls
         /// <exception cref="IOException"/>
         protected virtual void ProcessServerHello(ClientHandshakeState state, byte[] body)
         {
-            MemoryStream buf = new MemoryStream(body, false);
+            TlsClient client = state.client;
+            TlsClientContextImpl clientContext = state.clientContext;
+            SecurityParameters securityParameters = clientContext.SecurityParameters;
 
+            MemoryStream buf = new MemoryStream(body, false);
             ServerHello serverHello = ServerHello.Parse(buf);
-            ProtocolVersion server_version = serverHello.Version;
 
-            state.serverExtensions = serverHello.Extensions;
+            var serverHelloExtensions = serverHello.Extensions;
 
+            ProtocolVersion legacy_version = serverHello.Version;
+            ProtocolVersion supported_version = TlsExtensionsUtilities.GetSupportedVersionsExtensionServer(
+                serverHelloExtensions);
 
+            ProtocolVersion server_version;
+            if (null == supported_version)
+            {
+                server_version = legacy_version;
+            }
+            else
+            {
+                if (!ProtocolVersion.DTLSv12.Equals(legacy_version) ||
+                    !ProtocolVersion.DTLSv13.IsEqualOrEarlierVersionOf(supported_version))
+                {
+                    throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+                }
 
-            SecurityParameters securityParameters = state.clientContext.SecurityParameters;
+                server_version = supported_version;
+            }
+
+            // NOT renegotiating
+            {
+                ReportServerVersion(state, server_version);
+            }
+
+            // NOTE: This is integrated into ReportServerVersion call above
+            //TlsUtilities.NegotiatedVersionDtlsClient(clientContext, state.client);
 
-            // TODO[dtls13] Check supported_version extension for negotiated version
+            // TODO[dtls13]
+            //if (ProtocolVersion.DTLSv13.IsEqualOrEarlierVersionOf(server_version))
+            //{
+            //    Process13ServerHello(serverHello, false);
+            //    return;
+            //}
 
-            ReportServerVersion(state, server_version);
+            int[] offeredCipherSuites = state.offeredCipherSuites;
+
+            // TODO[dtls13]
+            //state.clientHello = null;
+            //state.retryCookie = null;
+            //state.retryGroup = -1;
 
             securityParameters.m_serverRandom = serverHello.Random;
 
-            if (!state.clientContext.ClientVersion.Equals(server_version))
+            if (!clientContext.ClientVersion.Equals(server_version))
             {
                 TlsUtilities.CheckDowngradeMarker(server_version, securityParameters.ServerRandom);
             }
@@ -669,9 +781,19 @@ namespace Org.BouncyCastle.Tls
             {
                 byte[] selectedSessionID = serverHello.SessionID;
                 securityParameters.m_sessionID = selectedSessionID;
-                state.client.NotifySessionID(selectedSessionID);
-                state.resumedSession = selectedSessionID.Length > 0 && state.tlsSession != null
+                client.NotifySessionID(selectedSessionID);
+                securityParameters.m_resumedSession = selectedSessionID.Length > 0 && state.tlsSession != null
                     && Arrays.AreEqual(selectedSessionID, state.tlsSession.SessionID);
+
+                if (securityParameters.IsResumedSession)
+                {
+                    if (serverHello.CipherSuite != state.sessionParameters.CipherSuite ||
+                        !securityParameters.NegotiatedVersion.Equals(state.sessionParameters.NegotiatedVersion))
+                    {
+                        throw new TlsFatalAlert(AlertDescription.illegal_parameter,
+                            "ServerHello parameters do not match resumed session");
+                    }
+                }
             }
 
             /*
@@ -682,63 +804,15 @@ namespace Org.BouncyCastle.Tls
                 int cipherSuite = ValidateSelectedCipherSuite(serverHello.CipherSuite,
                     AlertDescription.illegal_parameter);
 
-                if (!TlsUtilities.IsValidCipherSuiteSelection(state.offeredCipherSuites, cipherSuite) ||
+                if (!TlsUtilities.IsValidCipherSuiteSelection(offeredCipherSuites, cipherSuite) ||
                     !TlsUtilities.IsValidVersionForCipherSuite(cipherSuite, securityParameters.NegotiatedVersion))
                 {
-                    throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+                    throw new TlsFatalAlert(AlertDescription.illegal_parameter,
+                        "ServerHello selected invalid cipher suite");
                 }
 
                 TlsUtilities.NegotiatedCipherSuite(securityParameters, cipherSuite);
-                state.client.NotifySelectedCipherSuite(cipherSuite);
-            }
-
-            /*
-             * 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 (!state.resumedSession && !state.client.ShouldUseExtendedMasterSecret())
-                        throw new TlsFatalAlert(AlertDescription.handshake_failure);
-                }
-                else
-                {
-                    if (state.client.RequiresExtendedMasterSecret()
-                        || (state.resumedSession && !state.client.AllowLegacyResumption()))
-                    {
-                        throw new TlsFatalAlert(AlertDescription.handshake_failure);
-                    }
-                }
-
-                securityParameters.m_extendedMasterSecret = acceptedExtendedMasterSecret;
+                client.NotifySelectedCipherSuite(cipherSuite);
             }
 
             /*
@@ -747,9 +821,10 @@ namespace Org.BouncyCastle.Tls
              * 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
@@ -776,7 +851,7 @@ namespace Org.BouncyCastle.Tls
                      * extensions appearing in the client hello, and send a server hello containing no
                      * extensions[.]
                      */
-                    if (state.resumedSession)
+                    if (securityParameters.IsResumedSession)
                     {
                         // TODO[compat-gnutls] GnuTLS test server sends server extensions e.g. ec_point_formats
                         // TODO[compat-openssl] OpenSSL test server sends server extensions e.g. ec_point_formats
@@ -786,17 +861,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
@@ -815,7 +903,44 @@ namespace Org.BouncyCastle.Tls
             }
 
             // TODO[compat-gnutls] GnuTLS test server fails to send renegotiation_info extension when resuming
-            state.client.NotifySecureRenegotiation(securityParameters.IsSecureRenegotiation);
+            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
@@ -823,7 +948,7 @@ namespace Org.BouncyCastle.Tls
              * messages are considered.
              */
             securityParameters.m_applicationProtocol = TlsExtensionsUtilities.GetAlpnExtensionServer(
-                state.serverExtensions);
+                serverHelloExtensions);
             securityParameters.m_applicationProtocolSet = true;
 
             // Connection ID
@@ -833,7 +958,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)
@@ -847,7 +972,7 @@ namespace Org.BouncyCastle.Tls
             // Heartbeats
             {
                 HeartbeatExtension heartbeatExtension = TlsExtensionsUtilities.GetHeartbeatExtension(
-                    state.serverExtensions);
+                    serverHelloExtensions);
                 if (null == heartbeatExtension)
                 {
                     state.heartbeat = null;
@@ -859,19 +984,11 @@ namespace Org.BouncyCastle.Tls
                 }
             }
 
-
-
             var sessionClientExtensions = state.clientExtensions;
-            var sessionServerExtensions = state.serverExtensions;
+            var sessionServerExtensions = serverHelloExtensions;
 
-            if (state.resumedSession)
+            if (securityParameters.IsResumedSession)
             {
-                if (securityParameters.CipherSuite != state.sessionParameters.CipherSuite
-                    || !server_version.Equals(state.sessionParameters.NegotiatedVersion))
-                {
-                    throw new TlsFatalAlert(AlertDescription.illegal_parameter);
-                }
-
                 sessionClientExtensions = null;
                 sessionServerExtensions = state.sessionParameters.ReadServerExtensions();
             }
@@ -893,13 +1010,13 @@ namespace Org.BouncyCastle.Tls
                     securityParameters.m_encryptThenMac = serverSentEncryptThenMac;
                 }
 
-                securityParameters.m_maxFragmentLength = EvaluateMaxFragmentLengthExtension(state.resumedSession,
+                securityParameters.m_maxFragmentLength = TlsUtilities.ProcessMaxFragmentLengthExtension(
                     sessionClientExtensions, sessionServerExtensions, AlertDescription.illegal_parameter);
 
                 securityParameters.m_truncatedHmac = TlsExtensionsUtilities.HasTruncatedHmacExtension(
                     sessionServerExtensions);
 
-                if (!state.resumedSession)
+                if (!securityParameters.IsResumedSession)
                 {
                     // TODO[tls13] See RFC 8446 4.4.2.1
                     if (TlsUtilities.HasExpectedEmptyExtensionData(sessionServerExtensions,
@@ -912,16 +1029,20 @@ namespace Org.BouncyCastle.Tls
                     {
                         securityParameters.m_statusRequestVersion = 1;
                     }
-                }
 
-                state.expectSessionTicket = !state.resumedSession
-                    && TlsUtilities.HasExpectedEmptyExtensionData(sessionServerExtensions,
+                    securityParameters.m_clientCertificateType = TlsUtilities.ProcessClientCertificateTypeExtension(
+                        sessionClientExtensions, sessionServerExtensions, AlertDescription.illegal_parameter);
+                    securityParameters.m_serverCertificateType = TlsUtilities.ProcessServerCertificateTypeExtension(
+                        sessionClientExtensions, sessionServerExtensions, AlertDescription.illegal_parameter);
+
+                    state.expectSessionTicket = TlsUtilities.HasExpectedEmptyExtensionData(sessionServerExtensions,
                         ExtensionType.session_ticket, AlertDescription.illegal_parameter);
+                }
             }
 
             if (sessionClientExtensions != null)
             {
-                state.client.ProcessServerExtensions(sessionServerExtensions);
+                client.ProcessServerExtensions(sessionServerExtensions);
             }
         }
 
@@ -944,8 +1065,8 @@ namespace Org.BouncyCastle.Tls
         /// <exception cref="IOException"/>
         protected virtual void ReportServerVersion(ClientHandshakeState state, ProtocolVersion server_version)
         {
-            TlsClientContextImpl context = state.clientContext;
-            SecurityParameters securityParameters = context.SecurityParameters;
+            TlsClientContextImpl clientContext = state.clientContext;
+            SecurityParameters securityParameters = clientContext.SecurityParameters;
 
             ProtocolVersion currentServerVersion = securityParameters.NegotiatedVersion;
             if (null != currentServerVersion)
@@ -956,12 +1077,18 @@ namespace Org.BouncyCastle.Tls
                 return;
             }
 
-            if (!ProtocolVersion.Contains(context.ClientSupportedVersions, server_version))
+            if (!ProtocolVersion.Contains(clientContext.ClientSupportedVersions, server_version))
                 throw new TlsFatalAlert(AlertDescription.protocol_version);
 
+            // TODO[dtls13] Read draft/RFC for guidance on the legacy_record_version field
+            //ProtocolVersion legacy_record_version = server_version.IsLaterVersionOf(ProtocolVersion.DTLSv12)
+            //    ?   ProtocolVersion.DTLSv12
+            //    :   server_version;
+
+            //recordLayer.SetWriteVersion(legacy_record_version);
             securityParameters.m_negotiatedVersion = server_version;
 
-            TlsUtilities.NegotiatedVersionDtlsClient(state.clientContext, state.client);
+            TlsUtilities.NegotiatedVersionDtlsClient(clientContext, state.client);
         }
 
         /// <exception cref="IOException"/>
@@ -995,9 +1122,9 @@ namespace Org.BouncyCastle.Tls
             internal int[] offeredCipherSuites = null;
             internal IDictionary<int, byte[]> clientExtensions = null;
             internal IDictionary<int, byte[]> serverExtensions = null;
-            internal bool resumedSession = false;
             internal bool expectSessionTicket = false;
             internal IDictionary<int, TlsAgreement> clientAgreements = null;
+            internal OfferedPsks.BindersConfig clientBinders = null;
             internal TlsKeyExchange keyExchange = null;
             internal TlsAuthentication authentication = null;
             internal CertificateStatus certificateStatus = null;