diff options
Diffstat (limited to 'crypto/src/tls/DtlsClientProtocol.cs')
-rw-r--r-- | crypto/src/tls/DtlsClientProtocol.cs | 243 |
1 files changed, 147 insertions, 96 deletions
diff --git a/crypto/src/tls/DtlsClientProtocol.cs b/crypto/src/tls/DtlsClientProtocol.cs index eec920c4a..da39320ea 100644 --- a/crypto/src/tls/DtlsClientProtocol.cs +++ b/crypto/src/tls/DtlsClientProtocol.cs @@ -35,27 +35,6 @@ namespace Org.BouncyCastle.Tls SecurityParameters securityParameters = clientContext.SecurityParameters; securityParameters.m_extendedPadding = client.ShouldUseExtendedPadding(); - TlsSession sessionToResume = client.GetSessionToResume(); - if (sessionToResume != null && sessionToResume.IsResumable) - { - SessionParameters sessionParameters = sessionToResume.ExportSessionParameters(); - - if (sessionParameters != null - && (sessionParameters.IsExtendedMasterSecret - || (!client.RequiresExtendedMasterSecret() && client.AllowLegacyResumption()))) - { - TlsCrypto crypto = clientContext.Crypto; - TlsSecret sessionMasterSecret = TlsUtilities.GetSessionMasterSecret(crypto, - sessionParameters.MasterSecret); - if (sessionMasterSecret != null) - { - state.tlsSession = sessionToResume; - state.sessionParameters = sessionParameters; - state.sessionMasterSecret = sessionMasterSecret; - } - } - } - DtlsRecordLayer recordLayer = new DtlsRecordLayer(clientContext, client, transport); client.NotifyCloseHandle(recordLayer); @@ -129,6 +108,8 @@ namespace Org.BouncyCastle.Tls recordLayer.SetWriteVersion(recordLayerVersion); ProcessServerHello(state, serverMessage.Body); + + ApplyMaxFragmentLengthExtension(recordLayer, securityParameters.MaxFragmentLength); } else { @@ -137,8 +118,6 @@ namespace Org.BouncyCastle.Tls handshake.HandshakeHash.NotifyPrfDetermined(); - ApplyMaxFragmentLengthExtension(recordLayer, securityParameters.MaxFragmentLength); - if (securityParameters.IsResumedSession) { securityParameters.m_masterSecret = state.sessionMasterSecret; @@ -412,7 +391,7 @@ namespace Org.BouncyCastle.Tls ProtocolVersion[] supportedVersions = client.GetProtocolVersions(); - //ProtocolVersion earliestVersion = ProtocolVersion.GetEarliestDtls(supportedVersions); + ProtocolVersion earliestVersion = ProtocolVersion.GetEarliestDtls(supportedVersions); ProtocolVersion latestVersion = ProtocolVersion.GetLatestDtls(supportedVersions); if (!ProtocolVersion.IsSupportedDtlsVersionClient(latestVersion)) @@ -421,7 +400,7 @@ namespace Org.BouncyCastle.Tls clientContext.SetClientVersion(latestVersion); clientContext.SetClientSupportedVersions(supportedVersions); - //bool offeringDtlsV12Minus = ProtocolVersion.DTLSv12.IsEqualOrLaterVersionOf(earliestVersion); + bool offeringDtlsV12Minus = ProtocolVersion.DTLSv12.IsEqualOrLaterVersionOf(earliestVersion); bool offeringDtlsV13Plus = ProtocolVersion.DTLSv13.IsEqualOrEarlierVersionOf(latestVersion); { @@ -430,12 +409,18 @@ namespace Org.BouncyCastle.Tls securityParameters.m_clientRandom = TlsProtocol.CreateRandomBlock(useGmtUnixTime, clientContext); } + TlsSession sessionToResume = offeringDtlsV12Minus ? client.GetSessionToResume() : null; + bool fallback = client.IsFallback(); state.offeredCipherSuites = client.GetCipherSuites(); state.clientExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(client.GetClientExtensions()); + bool shouldUseEms = client.ShouldUseExtendedMasterSecret(); + + EstablishSession(state, sessionToResume); + byte[] legacy_session_id = TlsUtilities.GetSessionID(state.tlsSession); if (legacy_session_id.Length > 0) @@ -457,7 +442,31 @@ namespace Org.BouncyCastle.Tls } } - client.NotifySessionToResume(legacy_session_id.Length < 1 ? null : state.tlsSession); + if (legacy_session_id.Length > 0 && TlsUtilities.IsExtendedMasterSecretOptional(sessionVersion)) + { + if (shouldUseEms) + { + if (!state.sessionParameters.IsExtendedMasterSecret && + !client.AllowLegacyResumption()) + { + legacy_session_id = TlsUtilities.EmptyBytes; + } + } + else + { + if (state.sessionParameters.IsExtendedMasterSecret) + { + legacy_session_id = TlsUtilities.EmptyBytes; + } + } + } + + if (legacy_session_id.Length < 1) + { + CancelSession(state); + } + + client.NotifySessionToResume(state.tlsSession); ProtocolVersion legacy_version = latestVersion; if (offeringDtlsV13Plus) @@ -493,15 +502,13 @@ namespace Org.BouncyCastle.Tls state.clientAgreements = TlsUtilities.AddKeyShareToClientHello(clientContext, client, state.clientExtensions); - if (TlsUtilities.IsExtendedMasterSecretOptional(supportedVersions) - && client.ShouldUseExtendedMasterSecret()) + if (shouldUseEms && TlsUtilities.IsExtendedMasterSecretOptional(supportedVersions)) { TlsExtensionsUtilities.AddExtendedMasterSecretExtension(state.clientExtensions); } - else if (!offeringDtlsV13Plus - && client.RequiresExtendedMasterSecret()) + else { - throw new TlsFatalAlert(AlertDescription.internal_error); + state.clientExtensions.Remove(ExtensionType.extended_master_secret); } // Cipher Suites (and SCSV) @@ -572,7 +579,7 @@ namespace Org.BouncyCastle.Tls return buf.ToArray(); } - protected virtual void InvalidateSession(ClientHandshakeState state) + protected virtual void CancelSession(ClientHandshakeState state) { if (state.sessionMasterSecret != null) { @@ -586,11 +593,53 @@ namespace Org.BouncyCastle.Tls state.sessionParameters = null; } + state.tlsSession = null; + } + + protected virtual bool EstablishSession(ClientHandshakeState state, TlsSession sessionToResume) + { + state.tlsSession = null; + state.sessionParameters = null; + state.sessionMasterSecret = null; + + if (null == sessionToResume || !sessionToResume.IsResumable) + return false; + + SessionParameters sessionParameters = sessionToResume.ExportSessionParameters(); + if (null == sessionParameters) + return false; + + ProtocolVersion sessionVersion = sessionParameters.NegotiatedVersion; + if (null == sessionVersion || !sessionVersion.IsDtls) + return false; + + bool isEms = sessionParameters.IsExtendedMasterSecret; + if (!TlsUtilities.IsExtendedMasterSecretOptional(sessionVersion)) + { + if (!isEms) + return false; + } + + TlsCrypto crypto = state.clientContext.Crypto; + TlsSecret sessionMasterSecret = TlsUtilities.GetSessionMasterSecret(crypto, sessionParameters.MasterSecret); + if (null == sessionMasterSecret) + return false; + + state.tlsSession = sessionToResume; + state.sessionParameters = sessionParameters; + state.sessionMasterSecret = sessionMasterSecret; + + return true; + } + + protected virtual void InvalidateSession(ClientHandshakeState state) + { if (state.tlsSession != null) { state.tlsSession.Invalidate(); - state.tlsSession = null; } + + CancelSession(state); } /// <exception cref="IOException"/> @@ -685,11 +734,11 @@ namespace Org.BouncyCastle.Tls MemoryStream buf = new MemoryStream(body, false); ServerHello serverHello = ServerHello.Parse(buf); - state.serverExtensions = serverHello.Extensions; + var serverHelloExtensions = serverHello.Extensions; ProtocolVersion legacy_version = serverHello.Version; ProtocolVersion supported_version = TlsExtensionsUtilities.GetSupportedVersionsExtensionServer( - state.serverExtensions); + serverHelloExtensions); ProtocolVersion server_version; if (null == supported_version) @@ -774,63 +823,15 @@ namespace Org.BouncyCastle.Tls } /* - * RFC3546 2.2 The extended server hello message format MAY be sent in place of the server - * hello message when the client has requested extended functionality via the extended - * client hello message specified in Section 2.1. ... Note that the extended server hello - * message is only sent in response to an extended client hello message. This prevents the - * possibility that the extended server hello message could "break" existing TLS 1.0 - * clients. - */ - - /* - * TODO RFC 3546 2.3 If [...] the older session is resumed, then the server MUST ignore - * extensions appearing in the client hello, and send a server hello containing no - * extensions. - */ - - /* - * RFC 7627 4. Clients and servers SHOULD NOT accept handshakes that do not use the extended - * master secret [..]. (and see 5.2, 5.3) - * - * RFC 8446 Appendix D. Because TLS 1.3 always hashes in the transcript up to the server - * Finished, implementations which support both TLS 1.3 and earlier versions SHOULD indicate - * the use of the Extended Master Secret extension in their APIs whenever TLS 1.3 is used. - */ - if (TlsUtilities.IsTlsV13(server_version)) - { - securityParameters.m_extendedMasterSecret = true; - } - else - { - bool acceptedExtendedMasterSecret = TlsExtensionsUtilities.HasExtendedMasterSecretExtension( - state.serverExtensions); - - if (acceptedExtendedMasterSecret) - { - if (!securityParameters.IsResumedSession && !client.ShouldUseExtendedMasterSecret()) - throw new TlsFatalAlert(AlertDescription.handshake_failure); - } - else - { - if (client.RequiresExtendedMasterSecret() - || (securityParameters.IsResumedSession && !client.AllowLegacyResumption())) - { - throw new TlsFatalAlert(AlertDescription.handshake_failure); - } - } - - securityParameters.m_extendedMasterSecret = acceptedExtendedMasterSecret; - } - - /* * * RFC 3546 2.2 Note that the extended server hello message is only sent in response to an * extended client hello message. However, see RFC 5746 exception below. We always include * the SCSV, so an Extended Server Hello is always allowed. */ - if (state.serverExtensions != null) + state.serverExtensions = serverHelloExtensions; + if (serverHelloExtensions != null) { - foreach (int extType in state.serverExtensions.Keys) + foreach (int extType in serverHelloExtensions.Keys) { /* * RFC 5746 3.6. Note that sending a "renegotiation_info" extension in response to a @@ -867,17 +868,30 @@ namespace Org.BouncyCastle.Tls } } - /* - * RFC 5746 3.4. Client Behavior: Initial Handshake - */ + byte[] renegExtData = TlsUtilities.GetExtensionData(serverHelloExtensions, + ExtensionType.renegotiation_info); + + // NOT renegotiating { /* + * RFC 5746 3.4. Client Behavior: Initial Handshake (both full and session-resumption) + */ + + /* * When a ServerHello is received, the client MUST check if it includes the * "renegotiation_info" extension: */ - byte[] renegExtData = TlsUtilities.GetExtensionData(state.serverExtensions, - ExtensionType.renegotiation_info); - if (renegExtData != null) + if (renegExtData == null) + { + /* + * If the extension is not present, the server does not support secure + * renegotiation; set secure_renegotiation flag to FALSE. In this case, some clients + * may want to terminate the handshake instead of continuing; see Section 4.1 for + * discussion. + */ + securityParameters.m_secureRenegotiation = false; + } + else { /* * If the extension is present, set the secure_renegotiation flag to TRUE. The @@ -898,13 +912,50 @@ namespace Org.BouncyCastle.Tls // TODO[compat-gnutls] GnuTLS test server fails to send renegotiation_info extension when resuming client.NotifySecureRenegotiation(securityParameters.IsSecureRenegotiation); + // extended_master_secret + { + bool negotiatedEms = false; + + if (TlsExtensionsUtilities.HasExtendedMasterSecretExtension(state.clientExtensions)) + { + negotiatedEms = TlsExtensionsUtilities.HasExtendedMasterSecretExtension(serverHelloExtensions); + + if (TlsUtilities.IsExtendedMasterSecretOptional(server_version)) + { + if (!negotiatedEms && + client.RequiresExtendedMasterSecret()) + { + throw new TlsFatalAlert(AlertDescription.handshake_failure, + "Extended Master Secret extension is required"); + } + } + else + { + if (negotiatedEms) + { + throw new TlsFatalAlert(AlertDescription.illegal_parameter, + "Server sent an unexpected extended_master_secret extension negotiating " + server_version); + } + } + } + + securityParameters.m_extendedMasterSecret = negotiatedEms; + } + + if (securityParameters.IsResumedSession && + securityParameters.IsExtendedMasterSecret != state.sessionParameters.IsExtendedMasterSecret) + { + throw new TlsFatalAlert(AlertDescription.handshake_failure, + "Server resumed session with mismatched extended_master_secret negotiation"); + } + /* * RFC 7301 3.1. When session resumption or session tickets [...] are used, the previous * contents of this extension are irrelevant, and only the values in the new handshake * messages are considered. */ securityParameters.m_applicationProtocol = TlsExtensionsUtilities.GetAlpnExtensionServer( - state.serverExtensions); + serverHelloExtensions); securityParameters.m_applicationProtocolSet = true; // Connection ID @@ -914,7 +965,7 @@ namespace Org.BouncyCastle.Tls * RFC 9146 3. When a DTLS session is resumed or renegotiated, the "connection_id" extension is * negotiated afresh. */ - var serverConnectionID = TlsExtensionsUtilities.GetConnectionIDExtension(state.serverExtensions); + var serverConnectionID = TlsExtensionsUtilities.GetConnectionIDExtension(serverHelloExtensions); if (serverConnectionID != null) { var clientConnectionID = TlsExtensionsUtilities.GetConnectionIDExtension(state.clientExtensions) @@ -928,7 +979,7 @@ namespace Org.BouncyCastle.Tls // Heartbeats { HeartbeatExtension heartbeatExtension = TlsExtensionsUtilities.GetHeartbeatExtension( - state.serverExtensions); + serverHelloExtensions); if (null == heartbeatExtension) { state.heartbeat = null; @@ -941,7 +992,7 @@ namespace Org.BouncyCastle.Tls } var sessionClientExtensions = state.clientExtensions; - var sessionServerExtensions = state.serverExtensions; + var sessionServerExtensions = serverHelloExtensions; if (securityParameters.IsResumedSession) { |