Example #1
0
        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;
            }
        }