private void ProcessHandshakeMessage(short type, byte[] buf) { MemoryStream inStr = new MemoryStream(buf, false); /* * Check the type. */ switch (type) { case HP_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); break; } default: this.FailWithError(AL_fatal, AP_unexpected_message); break; } connection_state = CS_SERVER_CERTIFICATE_RECEIVED; break; } case HP_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(AL_fatal, AP_handshake_failure); } connection_state = CS_DONE; /* * We are now ready to receive application data. */ this.appDataReady = true; break; default: this.FailWithError(AL_fatal, AP_unexpected_message); break; } break; case HP_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); /* * Currently, we don't support session ids */ byte[] sessionID = TlsUtilities.ReadOpaque8(inStr); if (sessionID.Length > 32) { this.FailWithError(TlsProtocolHandler.AL_fatal, TlsProtocolHandler.AP_illegal_parameter); } this.tlsClient.NotifySessionID(sessionID); /* * Find out which ciphersuite the server has chosen and check that * it was one of the offered ones. */ int selectedCipherSuite = TlsUtilities.ReadUint16(inStr); if (!WasCipherSuiteOffered(selectedCipherSuite)) { this.FailWithError(TlsProtocolHandler.AL_fatal, TlsProtocolHandler.AP_illegal_parameter); } this.tlsClient.NotifySelectedCipherSuite(selectedCipherSuite); /* * We support only the null compression which means no * compression. */ short compressionMethod = TlsUtilities.ReadUint8(inStr); if (compressionMethod != 0) { this.FailWithError(TlsProtocolHandler.AL_fatal, TlsProtocolHandler.AP_illegal_parameter); } /* * RFC4366 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. */ if (extendedClientHello) { // Integer -> byte[] Hashtable serverExtensions = new Hashtable(); 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) { int extType = TlsUtilities.ReadUint16(ext); byte[] extValue = TlsUtilities.ReadOpaque16(ext); serverExtensions.Add(extType, extValue); } } // TODO[RFC 5746] If renegotiation_info was sent in client hello, check here tlsClient.ProcessServerExtensions(serverExtensions); } AssertEmpty(inStr); this.keyExchange = tlsClient.CreateKeyExchange(); connection_state = CS_SERVER_HELLO_RECEIVED; break; default: this.FailWithError(AL_fatal, AP_unexpected_message); break; } break; case HP_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); bool isClientCertificateRequested = (connection_state == CS_CERTIFICATE_REQUEST_RECEIVED); connection_state = CS_SERVER_HELLO_DONE_RECEIVED; if (isClientCertificateRequested) { SendClientCertificate(tlsClient.GetCertificate()); } /* * Send the client key exchange message, depending on the key * exchange we are using in our ciphersuite. */ SendClientKeyExchange(this.keyExchange.GenerateClientKeyExchange()); connection_state = CS_CLIENT_KEY_EXCHANGE_SEND; if (isClientCertificateRequested) { byte[] clientCertificateSignature = tlsClient.GenerateCertificateSignature(rs.GetCurrentHash()); if (clientCertificateSignature != null) { SendCertificateVerify(clientCertificateSignature); connection_state = CS_CERTIFICATE_VERIFY_SEND; } } /* * Now, we send change cipher state */ byte[] cmessage = new byte[1]; cmessage[0] = 1; rs.WriteMessage(RL_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.CreateCipher(securityParameters)); /* * Send our finished message. */ byte[] clientVerifyData = TlsUtilities.PRF(securityParameters.masterSecret, "client finished", rs.GetCurrentHash(), 12); MemoryStream bos = new MemoryStream(); TlsUtilities.WriteUint8(HP_FINISHED, bos); TlsUtilities.WriteOpaque24(clientVerifyData, bos); byte[] message = bos.ToArray(); rs.WriteMessage(RL_HANDSHAKE, message, 0, message.Length); this.connection_state = CS_CLIENT_FINISHED_SEND; break; default: this.FailWithError(AL_fatal, AP_handshake_failure); break; } break; case HP_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.keyExchange.ProcessServerKeyExchange(inStr, securityParameters); AssertEmpty(inStr); break; } default: this.FailWithError(AL_fatal, AP_unexpected_message); break; } this.connection_state = CS_SERVER_KEY_EXCHANGE_RECEIVED; break; } case HP_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(); } byte[] types = TlsUtilities.ReadOpaque8(inStr); byte[] authorities = TlsUtilities.ReadOpaque16(inStr); AssertEmpty(inStr); ArrayList authorityDNs = new ArrayList(); MemoryStream bis = new MemoryStream(authorities, false); while (bis.Position < bis.Length) { byte[] dnBytes = TlsUtilities.ReadOpaque16(bis); authorityDNs.Add(X509Name.GetInstance(Asn1Object.FromByteArray(dnBytes))); } this.tlsClient.ProcessServerCertificateRequest(types, authorityDNs); break; } default: this.FailWithError(AL_fatal, AP_unexpected_message); break; } this.connection_state = CS_CERTIFICATE_REQUEST_RECEIVED; break; case HP_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(AL_warning, AP_no_renegotiation); } break; case HP_CLIENT_KEY_EXCHANGE: case HP_CERTIFICATE_VERIFY: case HP_CLIENT_HELLO: default: // We do not support this! this.FailWithError(AL_fatal, AP_unexpected_message); break; } }