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; }