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.cs286
1 files changed, 163 insertions, 123 deletions
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;