diff options
Diffstat (limited to 'crypto/src/tls/DtlsClientProtocol.cs')
-rw-r--r-- | crypto/src/tls/DtlsClientProtocol.cs | 511 |
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; |