public virtual void NotifySelectedCipherSuite(CipherSuite selectedCipherSuite) { this.selectedCipherSuite = selectedCipherSuite; }
protected virtual void ProcessServerHello(ClientHandshakeState state, byte[] body) { SecurityParameters securityParameters = state.clientContext.SecurityParameters; MemoryStream buf = new MemoryStream(body, false); { ProtocolVersion server_version = TlsUtilities.ReadVersion(buf); ReportServerVersion(state, server_version); } securityParameters.serverRandom = TlsUtilities.ReadFully(32, buf); state.selectedSessionID = TlsUtilities.ReadOpaque8(buf); if (state.selectedSessionID.Length > 32) { throw new TlsFatalAlert(AlertDescription.illegal_parameter); } state.client.NotifySessionID(state.selectedSessionID); state.resumedSession = state.selectedSessionID.Length > 0 && state.tlsSession != null && Arrays.AreEqual(state.selectedSessionID, state.tlsSession.SessionID); int selectedCipherSuite = TlsUtilities.ReadUint16(buf); if (!Arrays.Contains(state.offeredCipherSuites, selectedCipherSuite) || selectedCipherSuite == CipherSuite.TLS_NULL_WITH_NULL_NULL || CipherSuite.IsScsv(selectedCipherSuite) || !TlsUtilities.IsValidCipherSuiteForVersion(selectedCipherSuite, state.clientContext.ServerVersion)) { throw new TlsFatalAlert(AlertDescription.illegal_parameter); } ValidateSelectedCipherSuite(selectedCipherSuite, AlertDescription.illegal_parameter); state.client.NotifySelectedCipherSuite(selectedCipherSuite); byte selectedCompressionMethod = TlsUtilities.ReadUint8(buf); if (CompressionMethod.cls_null != selectedCompressionMethod) { throw new TlsFatalAlert(AlertDescription.illegal_parameter); } state.client.NotifySelectedCompressionMethod(selectedCompressionMethod); /* * 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. */ // Integer -> byte[] state.serverExtensions = TlsProtocol.ReadExtensions(buf); /* * RFC 7627 4. Clients and servers SHOULD NOT accept handshakes that do not use the extended * master secret [..]. (and see 5.2, 5.3) */ securityParameters.extendedMasterSecret = TlsExtensionsUtilities.HasExtendedMasterSecretExtension(state.serverExtensions); if (!securityParameters.IsExtendedMasterSecret && (state.resumedSession || state.client.RequiresExtendedMasterSecret())) { throw new TlsFatalAlert(AlertDescription.handshake_failure); } /* * 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) { foreach (int extType in state.serverExtensions.Keys) { /* * RFC 5746 3.6. 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 (extType == ExtensionType.renegotiation_info) { continue; } /* * RFC 5246 7.4.1.4 An extension type MUST NOT appear in the ServerHello unless the * same extension type appeared in the corresponding ClientHello. If a client * receives an extension type in ServerHello that it did not request in the * associated ClientHello, it MUST abort the handshake with an unsupported_extension * fatal alert. */ if (null == TlsUtilities.GetExtensionData(state.clientExtensions, extType)) { throw new TlsFatalAlert(AlertDescription.unsupported_extension); } /* * 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.resumedSession) { // 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 // TODO[compat-polarssl] PolarSSL test server sends server extensions e.g. ec_point_formats //throw new TlsFatalAlert(AlertDescription.illegal_parameter); } } } /* * RFC 5746 3.4. Client Behavior: Initial Handshake */ { /* * 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 the extension is present, set the secure_renegotiation flag to TRUE. The * client MUST then verify that the length of the "renegotiated_connection" * field is zero, and if it is not, MUST abort the handshake (by sending a fatal * handshake_failure alert). */ state.secure_renegotiation = true; if (!Arrays.ConstantTimeAreEqual(renegExtData, TlsProtocol.CreateRenegotiationInfo(TlsUtilities.EmptyBytes))) { throw new TlsFatalAlert(AlertDescription.handshake_failure); } } } // TODO[compat-gnutls] GnuTLS test server fails to send renegotiation_info extension when resuming state.client.NotifySecureRenegotiation(state.secure_renegotiation); IDictionary sessionClientExtensions = state.clientExtensions, sessionServerExtensions = state.serverExtensions; if (state.resumedSession) { if (selectedCipherSuite != state.sessionParameters.CipherSuite || selectedCompressionMethod != state.sessionParameters.CompressionAlgorithm) { throw new TlsFatalAlert(AlertDescription.illegal_parameter); } sessionClientExtensions = null; sessionServerExtensions = state.sessionParameters.ReadServerExtensions(); } securityParameters.cipherSuite = selectedCipherSuite; securityParameters.compressionAlgorithm = selectedCompressionMethod; if (sessionServerExtensions != null && sessionServerExtensions.Count > 0) { { /* * RFC 7366 3. If a server receives an encrypt-then-MAC request extension from a client * and then selects a stream or Authenticated Encryption with Associated Data (AEAD) * ciphersuite, it MUST NOT send an encrypt-then-MAC response extension back to the * client. */ bool serverSentEncryptThenMAC = TlsExtensionsUtilities.HasEncryptThenMacExtension(sessionServerExtensions); if (serverSentEncryptThenMAC && !TlsUtilities.IsBlockCipherSuite(securityParameters.CipherSuite)) { throw new TlsFatalAlert(AlertDescription.illegal_parameter); } securityParameters.encryptThenMac = serverSentEncryptThenMAC; } securityParameters.maxFragmentLength = EvaluateMaxFragmentLengthExtension(state.resumedSession, sessionClientExtensions, sessionServerExtensions, AlertDescription.illegal_parameter); securityParameters.truncatedHMac = TlsExtensionsUtilities.HasTruncatedHMacExtension(sessionServerExtensions); /* * TODO It's surprising that there's no provision to allow a 'fresh' CertificateStatus to be * sent in a session resumption handshake. */ state.allowCertificateStatus = !state.resumedSession && TlsUtilities.HasExpectedEmptyExtensionData(sessionServerExtensions, ExtensionType.status_request, AlertDescription.illegal_parameter); state.expectSessionTicket = !state.resumedSession && TlsUtilities.HasExpectedEmptyExtensionData(sessionServerExtensions, ExtensionType.session_ticket, AlertDescription.illegal_parameter); short s = TlsExtensionsUtilities.GetServerCertificateTypeExtensionServer(sessionServerExtensions); if (s != -1) { state.serverCertificateType = s; } s = TlsExtensionsUtilities.GetClientCertificateTypeExtensionServer(sessionServerExtensions); if (s != -1) { state.clientCertificateType = s; } } if (sessionClientExtensions != null) { state.client.ProcessServerExtensions(sessionServerExtensions); } securityParameters.prfAlgorithm = TlsProtocol.GetPrfAlgorithm(state.clientContext, securityParameters.CipherSuite); /* * RFC 5246 7.4.9. Any cipher suite which does not explicitly specify verify_data_length has * a verify_data_length equal to 12. This includes all existing cipher suites. */ securityParameters.verifyDataLength = 12; }
private static bool ArrayContains(CipherSuite[] a, CipherSuite n) { for (int i = 0; i < a.Length; ++i) { if (a[i] == n) return true; } return false; }
public virtual void NotifySelectedCipherSuite(CipherSuite selectedCipherSuite) { this.selectedCipherSuite = selectedCipherSuite; }
protected virtual byte[] GenerateServerHello(ServerHandshakeState state) { SecurityParameters securityParameters = state.serverContext.SecurityParameters; MemoryStream buf = new MemoryStream(); { ProtocolVersion server_version = state.server.GetServerVersion(); if (!server_version.IsEqualOrEarlierVersionOf(state.serverContext.ClientVersion)) { throw new TlsFatalAlert(AlertDescription.internal_error); } // TODO Read RFCs for guidance on the expected record layer version number // recordStream.setReadVersion(server_version); // recordStream.setWriteVersion(server_version); // recordStream.setRestrictReadVersion(true); state.serverContext.SetServerVersion(server_version); TlsUtilities.WriteVersion(state.serverContext.ServerVersion, buf); } buf.Write(securityParameters.ServerRandom, 0, securityParameters.ServerRandom.Length); /* * The server may return an empty session_id to indicate that the session will not be cached * and therefore cannot be resumed. */ TlsUtilities.WriteOpaque8(TlsUtilities.EmptyBytes, buf); int selectedCipherSuite = state.server.GetSelectedCipherSuite(); if (!Arrays.Contains(state.offeredCipherSuites, selectedCipherSuite) || selectedCipherSuite == CipherSuite.TLS_NULL_WITH_NULL_NULL || CipherSuite.IsScsv(selectedCipherSuite) || !TlsUtilities.IsValidCipherSuiteForVersion(selectedCipherSuite, state.serverContext.ServerVersion)) { throw new TlsFatalAlert(AlertDescription.internal_error); } ValidateSelectedCipherSuite(selectedCipherSuite, AlertDescription.internal_error); securityParameters.cipherSuite = selectedCipherSuite; byte selectedCompressionMethod = state.server.GetSelectedCompressionMethod(); if (!Arrays.Contains(state.offeredCompressionMethods, selectedCompressionMethod)) { throw new TlsFatalAlert(AlertDescription.internal_error); } securityParameters.compressionAlgorithm = selectedCompressionMethod; TlsUtilities.WriteUint16(selectedCipherSuite, buf); TlsUtilities.WriteUint8(selectedCompressionMethod, buf); state.serverExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(state.server.GetServerExtensions()); /* * RFC 5746 3.6. Server Behavior: Initial Handshake */ if (state.secure_renegotiation) { byte[] renegExtData = TlsUtilities.GetExtensionData(state.serverExtensions, ExtensionType.renegotiation_info); bool noRenegExt = (null == renegExtData); 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); } } if (securityParameters.IsExtendedMasterSecret) { TlsExtensionsUtilities.AddExtendedMasterSecretExtension(state.serverExtensions); } /* * 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.encryptThenMac = TlsExtensionsUtilities.HasEncryptThenMacExtension(state.serverExtensions); securityParameters.maxFragmentLength = EvaluateMaxFragmentLengthExtension(state.resumedSession, state.clientExtensions, state.serverExtensions, AlertDescription.internal_error); securityParameters.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. */ state.allowCertificateStatus = !state.resumedSession && TlsUtilities.HasExpectedEmptyExtensionData(state.serverExtensions, ExtensionType.status_request, AlertDescription.internal_error); state.expectSessionTicket = !state.resumedSession && TlsUtilities.HasExpectedEmptyExtensionData(state.serverExtensions, ExtensionType.session_ticket, AlertDescription.internal_error); TlsProtocol.WriteExtensions(buf, state.serverExtensions); } securityParameters.prfAlgorithm = TlsProtocol.GetPrfAlgorithm(state.serverContext, securityParameters.CipherSuite); /* * RFC 5246 7.4.9. Any cipher suite which does not explicitly specify verify_data_length * has a verify_data_length equal to 12. This includes all existing cipher suites. */ securityParameters.verifyDataLength = 12; return(buf.ToArray()); }
protected virtual void ReceiveServerHelloMessage(MemoryStream buf) { { ProtocolVersion server_version = TlsUtilities.ReadVersion(buf); if (server_version.IsDtls) { throw new TlsFatalAlert(AlertDescription.illegal_parameter); } // Check that this matches what the server is Sending in the record layer if (!server_version.Equals(this.mRecordStream.ReadVersion)) { throw new TlsFatalAlert(AlertDescription.illegal_parameter); } ProtocolVersion client_version = Context.ClientVersion; if (!server_version.IsEqualOrEarlierVersionOf(client_version)) { throw new TlsFatalAlert(AlertDescription.illegal_parameter); } this.mRecordStream.SetWriteVersion(server_version); ContextAdmin.SetServerVersion(server_version); this.mTlsClient.NotifyServerVersion(server_version); } /* * Read the server random */ this.mSecurityParameters.serverRandom = TlsUtilities.ReadFully(32, buf); this.mSelectedSessionID = TlsUtilities.ReadOpaque8(buf); if (this.mSelectedSessionID.Length > 32) { throw new TlsFatalAlert(AlertDescription.illegal_parameter); } this.mTlsClient.NotifySessionID(this.mSelectedSessionID); this.mResumedSession = this.mSelectedSessionID.Length > 0 && this.mTlsSession != null && Arrays.AreEqual(this.mSelectedSessionID, this.mTlsSession.SessionID); /* * Find out which CipherSuite the server has chosen and check that it was one of the offered * ones, and is a valid selection for the negotiated version. */ int selectedCipherSuite = TlsUtilities.ReadUint16(buf); if (!Arrays.Contains(this.mOfferedCipherSuites, selectedCipherSuite) || selectedCipherSuite == CipherSuite.TLS_NULL_WITH_NULL_NULL || CipherSuite.IsScsv(selectedCipherSuite) || !TlsUtilities.IsValidCipherSuiteForVersion(selectedCipherSuite, Context.ServerVersion)) { throw new TlsFatalAlert(AlertDescription.illegal_parameter); } this.mTlsClient.NotifySelectedCipherSuite(selectedCipherSuite); /* * Find out which CompressionMethod the server has chosen and check that it was one of the * offered ones. */ byte selectedCompressionMethod = TlsUtilities.ReadUint8(buf); if (!Arrays.Contains(this.mOfferedCompressionMethods, selectedCompressionMethod)) { throw new TlsFatalAlert(AlertDescription.illegal_parameter); } this.mTlsClient.NotifySelectedCompressionMethod(selectedCompressionMethod); /* * 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. */ this.mServerExtensions = ReadExtensions(buf); /* * 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 (this.mServerExtensions != null) { foreach (int extType in this.mServerExtensions.Keys) { /* * RFC 5746 3.6. 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 (extType == ExtensionType.renegotiation_info) { continue; } /* * RFC 5246 7.4.1.4 An extension type MUST NOT appear in the ServerHello unless the * same extension type appeared in the corresponding ClientHello. If a client * receives an extension type in ServerHello that it did not request in the * associated ClientHello, it MUST abort the handshake with an unsupported_extension * fatal alert. */ if (null == TlsUtilities.GetExtensionData(this.mClientExtensions, extType)) { throw new TlsFatalAlert(AlertDescription.unsupported_extension); } /* * 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 (this.mResumedSession) { // 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 // TODO[compat-polarssl] PolarSSL test server Sends server extensions e.g. ec_point_formats // throw new TlsFatalAlert(AlertDescription.illegal_parameter); } } } /* * RFC 5746 3.4. Client Behavior: Initial Handshake */ { /* * When a ServerHello is received, the client MUST check if it includes the * "renegotiation_info" extension: */ byte[] renegExtData = TlsUtilities.GetExtensionData(this.mServerExtensions, ExtensionType.renegotiation_info); if (renegExtData != null) { /* * If the extension is present, set the secure_renegotiation flag to TRUE. The * client MUST then verify that the length of the "renegotiated_connection" * field is zero, and if it is not, MUST abort the handshake (by Sending a fatal * handshake_failure alert). */ this.mSecureRenegotiation = true; if (!Arrays.ConstantTimeAreEqual(renegExtData, CreateRenegotiationInfo(TlsUtilities.EmptyBytes))) { throw new TlsFatalAlert(AlertDescription.handshake_failure); } } } // TODO[compat-gnutls] GnuTLS test server fails to Send renegotiation_info extension when resuming this.mTlsClient.NotifySecureRenegotiation(this.mSecureRenegotiation); IDictionary sessionClientExtensions = mClientExtensions, sessionServerExtensions = mServerExtensions; if (this.mResumedSession) { if (selectedCipherSuite != this.mSessionParameters.CipherSuite || selectedCompressionMethod != this.mSessionParameters.CompressionAlgorithm) { throw new TlsFatalAlert(AlertDescription.illegal_parameter); } sessionClientExtensions = null; sessionServerExtensions = this.mSessionParameters.ReadServerExtensions(); } this.mSecurityParameters.cipherSuite = selectedCipherSuite; this.mSecurityParameters.compressionAlgorithm = selectedCompressionMethod; if (sessionServerExtensions != null) { { /* * RFC 7366 3. If a server receives an encrypt-then-MAC request extension from a client * and then selects a stream or Authenticated Encryption with Associated Data (AEAD) * ciphersuite, it MUST NOT send an encrypt-then-MAC response extension back to the * client. */ bool serverSentEncryptThenMAC = TlsExtensionsUtilities.HasEncryptThenMacExtension(sessionServerExtensions); if (serverSentEncryptThenMAC && !TlsUtilities.IsBlockCipherSuite(selectedCipherSuite)) { throw new TlsFatalAlert(AlertDescription.illegal_parameter); } this.mSecurityParameters.encryptThenMac = serverSentEncryptThenMAC; } this.mSecurityParameters.extendedMasterSecret = TlsExtensionsUtilities.HasExtendedMasterSecretExtension(sessionServerExtensions); this.mSecurityParameters.maxFragmentLength = ProcessMaxFragmentLengthExtension(sessionClientExtensions, sessionServerExtensions, AlertDescription.illegal_parameter); this.mSecurityParameters.truncatedHMac = TlsExtensionsUtilities.HasTruncatedHMacExtension(sessionServerExtensions); /* * TODO It's surprising that there's no provision to allow a 'fresh' CertificateStatus to be sent in * a session resumption handshake. */ this.mAllowCertificateStatus = !this.mResumedSession && TlsUtilities.HasExpectedEmptyExtensionData(sessionServerExtensions, ExtensionType.status_request, AlertDescription.illegal_parameter); this.mExpectSessionTicket = !this.mResumedSession && TlsUtilities.HasExpectedEmptyExtensionData(sessionServerExtensions, ExtensionType.session_ticket, AlertDescription.illegal_parameter); } /* * TODO[session-hash] * * draft-ietf-tls-session-hash-04 4. Clients and servers SHOULD NOT accept handshakes * that do not use the extended master secret [..]. (and see 5.2, 5.3) */ if (sessionClientExtensions != null) { this.mTlsClient.ProcessServerExtensions(sessionServerExtensions); } this.mSecurityParameters.prfAlgorithm = GetPrfAlgorithm(Context, this.mSecurityParameters.CipherSuite); /* * RFC 5264 7.4.9. Any cipher suite which does not explicitly specify * verify_data_length has a verify_data_length equal to 12. This includes all * existing cipher suites. */ this.mSecurityParameters.verifyDataLength = 12; }
protected virtual void ReceiveServerHelloMessage(MemoryStream buf) { ProtocolVersion protocolVersion = TlsUtilities.ReadVersion((Stream)(object)buf); if (protocolVersion.IsDtls) { throw new TlsFatalAlert(47); } if (!protocolVersion.Equals(mRecordStream.ReadVersion)) { throw new TlsFatalAlert(47); } ProtocolVersion clientVersion = Context.ClientVersion; if (!protocolVersion.IsEqualOrEarlierVersionOf(clientVersion)) { throw new TlsFatalAlert(47); } mRecordStream.SetWriteVersion(protocolVersion); ContextAdmin.SetServerVersion(protocolVersion); mTlsClient.NotifyServerVersion(protocolVersion); mSecurityParameters.serverRandom = TlsUtilities.ReadFully(32, (Stream)(object)buf); mSelectedSessionID = TlsUtilities.ReadOpaque8((Stream)(object)buf); if (mSelectedSessionID.Length > 32) { throw new TlsFatalAlert(47); } mTlsClient.NotifySessionID(mSelectedSessionID); mResumedSession = mSelectedSessionID.Length > 0 && mTlsSession != null && Arrays.AreEqual(mSelectedSessionID, mTlsSession.SessionID); int num = TlsUtilities.ReadUint16((Stream)(object)buf); if (!Arrays.Contains(mOfferedCipherSuites, num) || num == 0 || CipherSuite.IsScsv(num) || !TlsUtilities.IsValidCipherSuiteForVersion(num, Context.ServerVersion)) { throw new TlsFatalAlert(47); } mTlsClient.NotifySelectedCipherSuite(num); byte b = TlsUtilities.ReadUint8((Stream)(object)buf); if (!Arrays.Contains(mOfferedCompressionMethods, b)) { throw new TlsFatalAlert(47); } mTlsClient.NotifySelectedCompressionMethod(b); mServerExtensions = TlsProtocol.ReadExtensions(buf); if (mServerExtensions != null) { { global::System.Collections.IEnumerator enumerator = ((global::System.Collections.IEnumerable)mServerExtensions.get_Keys()).GetEnumerator(); try { while (enumerator.MoveNext()) { int num2 = (int)enumerator.get_Current(); if (num2 != 65281) { if (TlsUtilities.GetExtensionData(mClientExtensions, num2) == null) { throw new TlsFatalAlert(110); } _ = mResumedSession; } } } finally { global::System.IDisposable disposable = enumerator as global::System.IDisposable; if (disposable != null) { disposable.Dispose(); } } } } byte[] extensionData = TlsUtilities.GetExtensionData(mServerExtensions, 65281); if (extensionData != null) { mSecureRenegotiation = true; if (!Arrays.ConstantTimeAreEqual(extensionData, TlsProtocol.CreateRenegotiationInfo(TlsUtilities.EmptyBytes))) { throw new TlsFatalAlert(40); } } mTlsClient.NotifySecureRenegotiation(mSecureRenegotiation); IDictionary val = mClientExtensions; IDictionary val2 = mServerExtensions; if (mResumedSession) { if (num != mSessionParameters.CipherSuite || b != mSessionParameters.CompressionAlgorithm) { throw new TlsFatalAlert(47); } val = null; val2 = mSessionParameters.ReadServerExtensions(); } mSecurityParameters.cipherSuite = num; mSecurityParameters.compressionAlgorithm = b; if (val2 != null) { bool flag = TlsExtensionsUtilities.HasEncryptThenMacExtension(val2); if (flag && !TlsUtilities.IsBlockCipherSuite(num)) { throw new TlsFatalAlert(47); } mSecurityParameters.encryptThenMac = flag; mSecurityParameters.extendedMasterSecret = TlsExtensionsUtilities.HasExtendedMasterSecretExtension(val2); mSecurityParameters.maxFragmentLength = ProcessMaxFragmentLengthExtension(val, val2, 47); mSecurityParameters.truncatedHMac = TlsExtensionsUtilities.HasTruncatedHMacExtension(val2); mAllowCertificateStatus = !mResumedSession && TlsUtilities.HasExpectedEmptyExtensionData(val2, 5, 47); mExpectSessionTicket = !mResumedSession && TlsUtilities.HasExpectedEmptyExtensionData(val2, 35, 47); } if (val != null) { mTlsClient.ProcessServerExtensions(val2); } mSecurityParameters.prfAlgorithm = TlsProtocol.GetPrfAlgorithm(Context, mSecurityParameters.CipherSuite); mSecurityParameters.verifyDataLength = 12; }
private void ProcessHandshakeMessage(HandshakeType type, byte[] buf) { MemoryStream inStr = new MemoryStream(buf, false); /* * Check the type. */ switch (type) { case HandshakeType.certificate: { switch (connection_state) { case CS_SERVER_HELLO_RECEIVED: { // Parse the Certificate message and send to cipher suite Certificate serverCertificate = Certificate.Parse(inStr); AssertEmpty(inStr); this.keyExchange.ProcessServerCertificate(serverCertificate); this.authentication = tlsClient.GetAuthentication(); this.authentication.NotifyServerCertificate(serverCertificate); break; } default: this.FailWithError(AlertLevel.fatal, AlertDescription.unexpected_message); break; } connection_state = CS_SERVER_CERTIFICATE_RECEIVED; break; } case HandshakeType.finished: switch (connection_state) { case CS_SERVER_CHANGE_CIPHER_SPEC_RECEIVED: /* * Read the checksum from the finished message, it has always 12 bytes. */ byte[] serverVerifyData = new byte[12]; TlsUtilities.ReadFully(serverVerifyData, inStr); AssertEmpty(inStr); /* * Calculate our own checksum. */ byte[] expectedServerVerifyData = TlsUtilities.PRF( securityParameters.masterSecret, "server finished", rs.GetCurrentHash(), 12); /* * Compare both checksums. */ if (!Arrays.ConstantTimeAreEqual(expectedServerVerifyData, serverVerifyData)) { /* * Wrong checksum in the finished message. */ this.FailWithError(AlertLevel.fatal, AlertDescription.handshake_failure); } connection_state = CS_DONE; /* * We are now ready to receive application data. */ this.appDataReady = true; break; default: this.FailWithError(AlertLevel.fatal, AlertDescription.unexpected_message); break; } break; case HandshakeType.server_hello: switch (connection_state) { case CS_CLIENT_HELLO_SEND: /* * Read the server hello message */ TlsUtilities.CheckVersion(inStr, this); /* * Read the server random */ securityParameters.serverRandom = new byte[32]; TlsUtilities.ReadFully(securityParameters.serverRandom, inStr); byte[] sessionID = TlsUtilities.ReadOpaque8(inStr); if (sessionID.Length > 32) { this.FailWithError(AlertLevel.fatal, AlertDescription.illegal_parameter); } this.tlsClient.NotifySessionID(sessionID); /* * Find out which CipherSuite the server has chosen and check that * it was one of the offered ones. */ CipherSuite selectedCipherSuite = (CipherSuite)TlsUtilities.ReadUint16(inStr); if (!ArrayContains(offeredCipherSuites, selectedCipherSuite) || selectedCipherSuite == CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV) { this.FailWithError(AlertLevel.fatal, AlertDescription.illegal_parameter); } this.tlsClient.NotifySelectedCipherSuite(selectedCipherSuite); /* * Find out which CompressionMethod the server has chosen and check that * it was one of the offered ones. */ CompressionMethod selectedCompressionMethod = (CompressionMethod)TlsUtilities.ReadUint8(inStr); if (!ArrayContains(offeredCompressionMethods, selectedCompressionMethod)) { this.FailWithError(AlertLevel.fatal, AlertDescription.illegal_parameter); } this.tlsClient.NotifySelectedCompressionMethod(selectedCompressionMethod); /* * 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. */ // ExtensionType -> byte[] IDictionary serverExtensions = Platform.CreateHashtable(); if (inStr.Position < inStr.Length) { // Process extensions from extended server hello byte[] extBytes = TlsUtilities.ReadOpaque16(inStr); MemoryStream ext = new MemoryStream(extBytes, false); while (ext.Position < ext.Length) { ExtensionType extType = (ExtensionType)TlsUtilities.ReadUint16(ext); byte[] extValue = TlsUtilities.ReadOpaque16(ext); // Note: RFC 5746 makes a special case for EXT_RenegotiationInfo if (extType != ExtensionType.renegotiation_info && !clientExtensions.Contains(extType)) { /* * RFC 3546 2.3 * Note that for all extension types (including those defined in * future), the extension type MUST NOT appear in the extended server * hello unless the same extension type appeared in the corresponding * client hello. Thus clients MUST abort the handshake if they receive * an extension type in the extended server hello that they did not * request in the associated (extended) client hello. */ this.FailWithError(AlertLevel.fatal, AlertDescription.unsupported_extension); } if (serverExtensions.Contains(extType)) { /* * RFC 3546 2.3 * Also note that when multiple extensions of different types are * present in the extended client hello or the extended server hello, * the extensions may appear in any order. There MUST NOT be more than * one extension of the same type. */ this.FailWithError(AlertLevel.fatal, AlertDescription.illegal_parameter); } serverExtensions.Add(extType, extValue); } } AssertEmpty(inStr); /* * RFC 5746 3.4. When a ServerHello is received, the client MUST check if it * includes the "renegotiation_info" extension: */ { bool secure_negotiation = serverExtensions.Contains(ExtensionType.renegotiation_info); /* * If the extension is present, set the secure_renegotiation flag * to TRUE. The client MUST then verify that the length of the * "renegotiated_connection" field is zero, and if it is not, MUST * abort the handshake (by sending a fatal handshake_failure * alert). */ if (secure_negotiation) { byte[] renegExtValue = (byte[])serverExtensions[ExtensionType.renegotiation_info]; if (!Arrays.ConstantTimeAreEqual(renegExtValue, CreateRenegotiationInfo(emptybuf))) { this.FailWithError(AlertLevel.fatal, AlertDescription.handshake_failure); } } tlsClient.NotifySecureRenegotiation(secure_negotiation); } if (clientExtensions != null) { tlsClient.ProcessServerExtensions(serverExtensions); } this.keyExchange = tlsClient.GetKeyExchange(); connection_state = CS_SERVER_HELLO_RECEIVED; break; default: this.FailWithError(AlertLevel.fatal, AlertDescription.unexpected_message); break; } break; case HandshakeType.server_hello_done: switch (connection_state) { case CS_SERVER_CERTIFICATE_RECEIVED: case CS_SERVER_KEY_EXCHANGE_RECEIVED: case CS_CERTIFICATE_REQUEST_RECEIVED: // NB: Original code used case label fall-through if (connection_state == CS_SERVER_CERTIFICATE_RECEIVED) { // There was no server key exchange message; check it's OK this.keyExchange.SkipServerKeyExchange(); } AssertEmpty(inStr); connection_state = CS_SERVER_HELLO_DONE_RECEIVED; TlsCredentials clientCreds = null; if (certificateRequest == null) { this.keyExchange.SkipClientCredentials(); } else { clientCreds = this.authentication.GetClientCredentials(certificateRequest); Certificate clientCert; if (clientCreds == null) { this.keyExchange.SkipClientCredentials(); clientCert = Certificate.EmptyChain; } else { this.keyExchange.ProcessClientCredentials(clientCreds); clientCert = clientCreds.Certificate; } SendClientCertificate(clientCert); } /* * Send the client key exchange message, depending on the key * exchange we are using in our CipherSuite. */ SendClientKeyExchange(); connection_state = CS_CLIENT_KEY_EXCHANGE_SEND; if (clientCreds != null && clientCreds is TlsSignerCredentials) { TlsSignerCredentials signerCreds = (TlsSignerCredentials)clientCreds; byte[] md5andsha1 = rs.GetCurrentHash(); byte[] clientCertificateSignature = signerCreds.GenerateCertificateSignature( md5andsha1); SendCertificateVerify(clientCertificateSignature); connection_state = CS_CERTIFICATE_VERIFY_SEND; } /* * Now, we send change cipher state */ byte[] cmessage = new byte[1]; cmessage[0] = 1; rs.WriteMessage(ContentType.change_cipher_spec, cmessage, 0, cmessage.Length); connection_state = CS_CLIENT_CHANGE_CIPHER_SPEC_SEND; /* * Calculate the master_secret */ byte[] pms = this.keyExchange.GeneratePremasterSecret(); securityParameters.masterSecret = TlsUtilities.PRF(pms, "master secret", TlsUtilities.Concat(securityParameters.clientRandom, securityParameters.serverRandom), 48); // TODO Is there a way to ensure the data is really overwritten? /* * RFC 2246 8.1. The pre_master_secret should be deleted from * memory once the master_secret has been computed. */ Array.Clear(pms, 0, pms.Length); /* * Initialize our cipher suite */ rs.ClientCipherSpecDecided(tlsClient.GetCompression(), tlsClient.GetCipher()); /* * Send our finished message. */ byte[] clientVerifyData = TlsUtilities.PRF(securityParameters.masterSecret, "client finished", rs.GetCurrentHash(), 12); MemoryStream bos = new MemoryStream(); TlsUtilities.WriteUint8((byte)HandshakeType.finished, bos); TlsUtilities.WriteOpaque24(clientVerifyData, bos); byte[] message = bos.ToArray(); rs.WriteMessage(ContentType.handshake, message, 0, message.Length); this.connection_state = CS_CLIENT_FINISHED_SEND; break; default: this.FailWithError(AlertLevel.fatal, AlertDescription.handshake_failure); break; } break; case HandshakeType.server_key_exchange: { switch (connection_state) { case CS_SERVER_HELLO_RECEIVED: case CS_SERVER_CERTIFICATE_RECEIVED: { // NB: Original code used case label fall-through if (connection_state == CS_SERVER_HELLO_RECEIVED) { // There was no server certificate message; check it's OK this.keyExchange.SkipServerCertificate(); this.authentication = null; } this.keyExchange.ProcessServerKeyExchange(inStr); AssertEmpty(inStr); break; } default: this.FailWithError(AlertLevel.fatal, AlertDescription.unexpected_message); break; } this.connection_state = CS_SERVER_KEY_EXCHANGE_RECEIVED; break; } case HandshakeType.certificate_request: switch (connection_state) { case CS_SERVER_CERTIFICATE_RECEIVED: case CS_SERVER_KEY_EXCHANGE_RECEIVED: { // NB: Original code used case label fall-through if (connection_state == CS_SERVER_CERTIFICATE_RECEIVED) { // There was no server key exchange message; check it's OK this.keyExchange.SkipServerKeyExchange(); } if (this.authentication == null) { /* * RFC 2246 7.4.4. It is a fatal handshake_failure alert * for an anonymous server to request client identification. */ this.FailWithError(AlertLevel.fatal, AlertDescription.handshake_failure); } int numTypes = TlsUtilities.ReadUint8(inStr); ClientCertificateType[] certificateTypes = new ClientCertificateType[numTypes]; for (int i = 0; i < numTypes; ++i) { certificateTypes[i] = (ClientCertificateType)TlsUtilities.ReadUint8(inStr); } byte[] authorities = TlsUtilities.ReadOpaque16(inStr); AssertEmpty(inStr); IList authorityDNs = Platform.CreateArrayList(); MemoryStream bis = new MemoryStream(authorities, false); while (bis.Position < bis.Length) { byte[] dnBytes = TlsUtilities.ReadOpaque16(bis); // TODO Switch to X500Name when available authorityDNs.Add(X509Name.GetInstance(Asn1Object.FromByteArray(dnBytes))); } this.certificateRequest = new CertificateRequest(certificateTypes, authorityDNs); this.keyExchange.ValidateCertificateRequest(this.certificateRequest); break; } default: this.FailWithError(AlertLevel.fatal, AlertDescription.unexpected_message); break; } this.connection_state = CS_CERTIFICATE_REQUEST_RECEIVED; break; case HandshakeType.hello_request: /* * RFC 2246 7.4.1.1 Hello request * This message will be ignored by the client if the client is currently * negotiating a session. This message may be ignored by the client if it * does not wish to renegotiate a session, or the client may, if it wishes, * respond with a no_renegotiation alert. */ if (connection_state == CS_DONE) { // Renegotiation not supported yet SendAlert(AlertLevel.warning, AlertDescription.no_renegotiation); } break; case HandshakeType.client_key_exchange: case HandshakeType.certificate_verify: case HandshakeType.client_hello: default: // We do not support this! this.FailWithError(AlertLevel.fatal, AlertDescription.unexpected_message); break; } }
protected virtual void ProcessServerHello(ClientHandshakeState state, byte[] body) { SecurityParameters securityParameters = state.clientContext.SecurityParameters; MemoryStream buf = new MemoryStream(body, false); ProtocolVersion server_version = TlsUtilities.ReadVersion(buf); ReportServerVersion(state, server_version); securityParameters.serverRandom = TlsUtilities.ReadFully(32, buf); state.selectedSessionID = TlsUtilities.ReadOpaque8(buf); if (state.selectedSessionID.Length > 32) { throw new TlsFatalAlert(AlertDescription.illegal_parameter); } state.client.NotifySessionID(state.selectedSessionID); state.selectedCipherSuite = TlsUtilities.ReadUint16(buf); if (!Arrays.Contains(state.offeredCipherSuites, state.selectedCipherSuite) || state.selectedCipherSuite == CipherSuite.TLS_NULL_WITH_NULL_NULL || CipherSuite.IsScsv(state.selectedCipherSuite) || !TlsUtilities.IsValidCipherSuiteForVersion(state.selectedCipherSuite, server_version)) { throw new TlsFatalAlert(AlertDescription.illegal_parameter); } ValidateSelectedCipherSuite(state.selectedCipherSuite, AlertDescription.illegal_parameter); state.client.NotifySelectedCipherSuite(state.selectedCipherSuite); state.selectedCompressionMethod = TlsUtilities.ReadUint8(buf); if (!Arrays.Contains(state.offeredCompressionMethods, (byte)state.selectedCompressionMethod)) { throw new TlsFatalAlert(AlertDescription.illegal_parameter); } state.client.NotifySelectedCompressionMethod((byte)state.selectedCompressionMethod); /* * 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. */ // Integer -> byte[] IDictionary serverExtensions = TlsProtocol.ReadExtensions(buf); /* * draft-ietf-tls-session-hash-01 5.2. If a server receives the "extended_master_secret" * extension, it MUST include the "extended_master_secret" extension in its ServerHello * message. */ bool serverSentExtendedMasterSecret = TlsExtensionsUtilities.HasExtendedMasterSecretExtension(serverExtensions); if (serverSentExtendedMasterSecret != securityParameters.extendedMasterSecret) { throw new TlsFatalAlert(AlertDescription.handshake_failure); } /* * 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 (serverExtensions != null) { foreach (int extType in serverExtensions.Keys) { /* * RFC 5746 3.6. 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 (extType == ExtensionType.renegotiation_info) { continue; } /* * RFC 5246 7.4.1.4 An extension type MUST NOT appear in the ServerHello unless the * same extension type appeared in the corresponding ClientHello. If a client * receives an extension type in ServerHello that it did not request in the * associated ClientHello, it MUST abort the handshake with an unsupported_extension * fatal alert. */ if (null == TlsUtilities.GetExtensionData(state.clientExtensions, extType)) { throw new TlsFatalAlert(AlertDescription.unsupported_extension); } /* * draft-ietf-tls-session-hash-01 5.2. Implementation note: if the server decides to * proceed with resumption, the extension does not have any effect. Requiring the * extension to be included anyway makes the extension negotiation logic easier, * because it does not depend on whether resumption is accepted or not. */ if (extType == ExtensionType.extended_master_secret) { continue; } /* * 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[.] */ // TODO[sessions] // if (this.mResumedSession) // { // // 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 // // TODO[compat-polarssl] PolarSSL test server sends server extensions e.g. ec_point_formats //// throw new TlsFatalAlert(AlertDescription.illegal_parameter); // } } /* * RFC 5746 3.4. Client Behavior: Initial Handshake */ { /* * When a ServerHello is received, the client MUST check if it includes the * "renegotiation_info" extension: */ byte[] renegExtData = (byte[])serverExtensions[ExtensionType.renegotiation_info]; if (renegExtData != null) { /* * If the extension is present, set the secure_renegotiation flag to TRUE. The * client MUST then verify that the length of the "renegotiated_connection" * field is zero, and if it is not, MUST abort the handshake (by sending a fatal * handshake_failure alert). */ state.secure_renegotiation = true; if (!Arrays.ConstantTimeAreEqual(renegExtData, TlsProtocol.CreateRenegotiationInfo(TlsUtilities.EmptyBytes))) { throw new TlsFatalAlert(AlertDescription.handshake_failure); } } } /* * RFC 7366 3. If a server receives an encrypt-then-MAC request extension from a client * and then selects a stream or Authenticated Encryption with Associated Data (AEAD) * ciphersuite, it MUST NOT send an encrypt-then-MAC response extension back to the * client. */ bool serverSentEncryptThenMAC = TlsExtensionsUtilities.HasEncryptThenMacExtension(serverExtensions); if (serverSentEncryptThenMAC && !TlsUtilities.IsBlockCipherSuite(state.selectedCipherSuite)) { throw new TlsFatalAlert(AlertDescription.illegal_parameter); } securityParameters.encryptThenMac = serverSentEncryptThenMAC; state.maxFragmentLength = EvaluateMaxFragmentLengthExtension(state.clientExtensions, serverExtensions, AlertDescription.illegal_parameter); securityParameters.truncatedHMac = TlsExtensionsUtilities.HasTruncatedHMacExtension(serverExtensions); state.allowCertificateStatus = TlsUtilities.HasExpectedEmptyExtensionData(serverExtensions, ExtensionType.status_request, AlertDescription.illegal_parameter); state.expectSessionTicket = TlsUtilities.HasExpectedEmptyExtensionData(serverExtensions, ExtensionType.session_ticket, AlertDescription.illegal_parameter); } state.client.NotifySecureRenegotiation(state.secure_renegotiation); if (state.clientExtensions != null) { state.client.ProcessServerExtensions(serverExtensions); } }
protected virtual void ReceiveServerHelloMessage(MemoryStream buf) { ProtocolVersion protocolVersion = TlsUtilities.ReadVersion(buf); if (protocolVersion.IsDtls) { throw new TlsFatalAlert(47); } if (!protocolVersion.Equals(mRecordStream.ReadVersion)) { throw new TlsFatalAlert(47); } ProtocolVersion clientVersion = Context.ClientVersion; if (!protocolVersion.IsEqualOrEarlierVersionOf(clientVersion)) { throw new TlsFatalAlert(47); } mRecordStream.SetWriteVersion(protocolVersion); ContextAdmin.SetServerVersion(protocolVersion); mTlsClient.NotifyServerVersion(protocolVersion); mSecurityParameters.serverRandom = TlsUtilities.ReadFully(32, buf); mSelectedSessionID = TlsUtilities.ReadOpaque8(buf); if (mSelectedSessionID.Length > 32) { throw new TlsFatalAlert(47); } mTlsClient.NotifySessionID(mSelectedSessionID); mResumedSession = (mSelectedSessionID.Length > 0 && mTlsSession != null && Arrays.AreEqual(mSelectedSessionID, mTlsSession.SessionID)); int num = TlsUtilities.ReadUint16(buf); if (!Arrays.Contains(mOfferedCipherSuites, num) || num == 0 || CipherSuite.IsScsv(num) || !TlsUtilities.IsValidCipherSuiteForVersion(num, Context.ServerVersion)) { throw new TlsFatalAlert(47); } mTlsClient.NotifySelectedCipherSuite(num); byte b = TlsUtilities.ReadUint8(buf); if (!Arrays.Contains(mOfferedCompressionMethods, b)) { throw new TlsFatalAlert(47); } mTlsClient.NotifySelectedCompressionMethod(b); mServerExtensions = TlsProtocol.ReadExtensions(buf); if (mServerExtensions != null) { foreach (int key in mServerExtensions.Keys) { if (key != 65281) { if (TlsUtilities.GetExtensionData(mClientExtensions, key) == null) { throw new TlsFatalAlert(110); } if (!mResumedSession) { } } } } byte[] extensionData = TlsUtilities.GetExtensionData(mServerExtensions, 65281); if (extensionData != null) { mSecureRenegotiation = true; if (!Arrays.ConstantTimeAreEqual(extensionData, TlsProtocol.CreateRenegotiationInfo(TlsUtilities.EmptyBytes))) { throw new TlsFatalAlert(40); } } mTlsClient.NotifySecureRenegotiation(mSecureRenegotiation); IDictionary dictionary = mClientExtensions; IDictionary dictionary2 = mServerExtensions; if (mResumedSession) { if (num != mSessionParameters.CipherSuite || b != mSessionParameters.CompressionAlgorithm) { throw new TlsFatalAlert(47); } dictionary = null; dictionary2 = mSessionParameters.ReadServerExtensions(); } mSecurityParameters.cipherSuite = num; mSecurityParameters.compressionAlgorithm = b; if (dictionary2 != null) { bool flag = TlsExtensionsUtilities.HasEncryptThenMacExtension(dictionary2); if (flag && !TlsUtilities.IsBlockCipherSuite(num)) { throw new TlsFatalAlert(47); } mSecurityParameters.encryptThenMac = flag; mSecurityParameters.extendedMasterSecret = TlsExtensionsUtilities.HasExtendedMasterSecretExtension(dictionary2); mSecurityParameters.maxFragmentLength = ProcessMaxFragmentLengthExtension(dictionary, dictionary2, 47); mSecurityParameters.truncatedHMac = TlsExtensionsUtilities.HasTruncatedHMacExtension(dictionary2); mAllowCertificateStatus = (!mResumedSession && TlsUtilities.HasExpectedEmptyExtensionData(dictionary2, 5, 47)); mExpectSessionTicket = (!mResumedSession && TlsUtilities.HasExpectedEmptyExtensionData(dictionary2, 35, 47)); } if (dictionary != null) { mTlsClient.ProcessServerExtensions(dictionary2); } mSecurityParameters.prfAlgorithm = TlsProtocol.GetPrfAlgorithm(Context, mSecurityParameters.CipherSuite); mSecurityParameters.verifyDataLength = 12; }
protected virtual void SendServerHelloMessage() { HandshakeMessage message = new HandshakeMessage(HandshakeType.server_hello); { ProtocolVersion server_version = mTlsServer.GetServerVersion(); if (!server_version.IsEqualOrEarlierVersionOf(Context.ClientVersion)) { throw new TlsFatalAlert(AlertDescription.internal_error); } mRecordStream.ReadVersion = server_version; mRecordStream.SetWriteVersion(server_version); mRecordStream.SetRestrictReadVersion(true); ContextAdmin.SetServerVersion(server_version); TlsUtilities.WriteVersion(server_version, message); } message.Write(this.mSecurityParameters.serverRandom); /* * The server may return an empty session_id to indicate that the session will not be cached * and therefore cannot be resumed. */ TlsUtilities.WriteOpaque8(TlsUtilities.EmptyBytes, message); int selectedCipherSuite = mTlsServer.GetSelectedCipherSuite(); if (!Arrays.Contains(mOfferedCipherSuites, selectedCipherSuite) || selectedCipherSuite == CipherSuite.TLS_NULL_WITH_NULL_NULL || CipherSuite.IsScsv(selectedCipherSuite) || !TlsUtilities.IsValidCipherSuiteForVersion(selectedCipherSuite, Context.ServerVersion)) { throw new TlsFatalAlert(AlertDescription.internal_error); } mSecurityParameters.cipherSuite = selectedCipherSuite; byte selectedCompressionMethod = mTlsServer.GetSelectedCompressionMethod(); if (!Arrays.Contains(mOfferedCompressionMethods, selectedCompressionMethod)) { throw new TlsFatalAlert(AlertDescription.internal_error); } mSecurityParameters.compressionAlgorithm = selectedCompressionMethod; TlsUtilities.WriteUint16(selectedCipherSuite, message); TlsUtilities.WriteUint8(selectedCompressionMethod, message); this.mServerExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(mTlsServer.GetServerExtensions()); /* * RFC 5746 3.6. Server Behavior: Initial Handshake */ if (this.mSecureRenegotiation) { byte[] renegExtData = TlsUtilities.GetExtensionData(this.mServerExtensions, ExtensionType.renegotiation_info); bool noRenegExt = (null == renegExtData); 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. */ this.mServerExtensions[ExtensionType.renegotiation_info] = CreateRenegotiationInfo(TlsUtilities.EmptyBytes); } } if (TlsUtilities.IsSsl(mTlsServerContext)) { mSecurityParameters.extendedMasterSecret = false; } else if (mSecurityParameters.IsExtendedMasterSecret) { TlsExtensionsUtilities.AddExtendedMasterSecretExtension(mServerExtensions); } /* * 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 (this.mServerExtensions.Count > 0) { this.mSecurityParameters.encryptThenMac = TlsExtensionsUtilities.HasEncryptThenMacExtension(mServerExtensions); this.mSecurityParameters.maxFragmentLength = ProcessMaxFragmentLengthExtension(mClientExtensions, mServerExtensions, AlertDescription.internal_error); this.mSecurityParameters.truncatedHMac = TlsExtensionsUtilities.HasTruncatedHMacExtension(mServerExtensions); /* * TODO It's surprising that there's no provision to allow a 'fresh' CertificateStatus to be sent in * a session resumption handshake. */ this.mAllowCertificateStatus = !mResumedSession && TlsUtilities.HasExpectedEmptyExtensionData(mServerExtensions, ExtensionType.status_request, AlertDescription.internal_error); this.mExpectSessionTicket = !mResumedSession && TlsUtilities.HasExpectedEmptyExtensionData(mServerExtensions, ExtensionType.session_ticket, AlertDescription.internal_error); WriteExtensions(message, this.mServerExtensions); } mSecurityParameters.prfAlgorithm = GetPrfAlgorithm(Context, mSecurityParameters.CipherSuite); /* * RFC 5246 7.4.9. Any cipher suite which does not explicitly specify verify_data_length has * a verify_data_length equal to 12. This includes all existing cipher suites. */ mSecurityParameters.verifyDataLength = 12; ApplyMaxFragmentLengthExtension(); message.WriteToRecordStream(this); }
protected virtual void ReceiveServerHelloMessage(MemoryStream buf) { ProtocolVersion writeVersion = TlsUtilities.ReadVersion(buf); if (writeVersion.IsDtls) { throw new TlsFatalAlert(0x2f); } if (!writeVersion.Equals(base.mRecordStream.ReadVersion)) { throw new TlsFatalAlert(0x2f); } ProtocolVersion clientVersion = this.Context.ClientVersion; if (!writeVersion.IsEqualOrEarlierVersionOf(clientVersion)) { throw new TlsFatalAlert(0x2f); } base.mRecordStream.SetWriteVersion(writeVersion); this.ContextAdmin.SetServerVersion(writeVersion); this.mTlsClient.NotifyServerVersion(writeVersion); base.mSecurityParameters.serverRandom = TlsUtilities.ReadFully(0x20, buf); this.mSelectedSessionID = TlsUtilities.ReadOpaque8(buf); if (this.mSelectedSessionID.Length > 0x20) { throw new TlsFatalAlert(0x2f); } this.mTlsClient.NotifySessionID(this.mSelectedSessionID); base.mResumedSession = ((this.mSelectedSessionID.Length > 0) && (base.mTlsSession != null)) && Arrays.AreEqual(this.mSelectedSessionID, base.mTlsSession.SessionID); int n = TlsUtilities.ReadUint16(buf); if ((!Arrays.Contains(base.mOfferedCipherSuites, n) || (n == 0)) || (CipherSuite.IsScsv(n) || !TlsUtilities.IsValidCipherSuiteForVersion(n, this.Context.ServerVersion))) { throw new TlsFatalAlert(0x2f); } this.mTlsClient.NotifySelectedCipherSuite(n); byte num2 = TlsUtilities.ReadUint8(buf); if (!Arrays.Contains(base.mOfferedCompressionMethods, num2)) { throw new TlsFatalAlert(0x2f); } this.mTlsClient.NotifySelectedCompressionMethod(num2); base.mServerExtensions = TlsProtocol.ReadExtensions(buf); if (base.mServerExtensions != null) { IEnumerator enumerator = base.mServerExtensions.Keys.GetEnumerator(); try { while (enumerator.MoveNext()) { int current = (int)enumerator.Current; if (current != 0xff01) { if (TlsUtilities.GetExtensionData(base.mClientExtensions, current) == null) { throw new TlsFatalAlert(110); } if (base.mResumedSession) { } } } } finally { if (enumerator is IDisposable disposable) { IDisposable disposable; disposable.Dispose(); } } } byte[] extensionData = TlsUtilities.GetExtensionData(base.mServerExtensions, 0xff01); if (extensionData != null) { base.mSecureRenegotiation = true; if (!Arrays.ConstantTimeAreEqual(extensionData, TlsProtocol.CreateRenegotiationInfo(TlsUtilities.EmptyBytes))) { throw new TlsFatalAlert(40); } } this.mTlsClient.NotifySecureRenegotiation(base.mSecureRenegotiation); IDictionary mClientExtensions = base.mClientExtensions; IDictionary mServerExtensions = base.mServerExtensions; if (base.mResumedSession) { if ((n != base.mSessionParameters.CipherSuite) || (num2 != base.mSessionParameters.CompressionAlgorithm)) { throw new TlsFatalAlert(0x2f); } mClientExtensions = null; mServerExtensions = base.mSessionParameters.ReadServerExtensions(); } base.mSecurityParameters.cipherSuite = n; base.mSecurityParameters.compressionAlgorithm = num2; if (mServerExtensions != null) { bool flag = TlsExtensionsUtilities.HasEncryptThenMacExtension(mServerExtensions); if (flag && !TlsUtilities.IsBlockCipherSuite(n)) { throw new TlsFatalAlert(0x2f); } base.mSecurityParameters.encryptThenMac = flag; base.mSecurityParameters.extendedMasterSecret = TlsExtensionsUtilities.HasExtendedMasterSecretExtension(mServerExtensions); base.mSecurityParameters.maxFragmentLength = this.ProcessMaxFragmentLengthExtension(mClientExtensions, mServerExtensions, 0x2f); base.mSecurityParameters.truncatedHMac = TlsExtensionsUtilities.HasTruncatedHMacExtension(mServerExtensions); base.mAllowCertificateStatus = !base.mResumedSession && TlsUtilities.HasExpectedEmptyExtensionData(mServerExtensions, 5, 0x2f); base.mExpectSessionTicket = !base.mResumedSession && TlsUtilities.HasExpectedEmptyExtensionData(mServerExtensions, 0x23, 0x2f); } if (mClientExtensions != null) { this.mTlsClient.ProcessServerExtensions(mServerExtensions); } base.mSecurityParameters.prfAlgorithm = TlsProtocol.GetPrfAlgorithm(this.Context, base.mSecurityParameters.CipherSuite); base.mSecurityParameters.verifyDataLength = 12; }