A generic class for ciphersuites in TLS 1.0.
Example #1
0
 internal RecordStream(
     TlsProtocolHandler handler,
     Stream inStr,
     Stream outStr)
 {
     this.handler    = handler;
     this.inStr      = inStr;
     this.outStr     = outStr;
     hash1           = new CombinedHash();
     hash2           = new CombinedHash();
     this.readSuite  = new TlsNullCipherSuite();
     this.writeSuite = this.readSuite;
 }
Example #2
0
		internal RecordStream(
			TlsProtocolHandler	handler,
			Stream				inStr,
			Stream				outStr)
		{
			this.handler = handler;
			this.inStr = inStr;
			this.outStr = outStr;
			hash1 = new CombinedHash();
			hash2 = new CombinedHash();
			this.readSuite = new TlsNullCipherSuite();
			this.writeSuite = this.readSuite;
		}
Example #3
0
        private void processHandshake()
        {
            bool read;

            do
            {
                read = false;

                /*
                 * We need the first 4 bytes, they contain type and length of
                 * the message.
                 */
                if (handshakeQueue.Available >= 4)
                {
                    byte[] beginning = new byte[4];
                    handshakeQueue.Read(beginning, 0, 4, 0);
                    MemoryStream bis  = new MemoryStream(beginning, false);
                    short        type = TlsUtilities.ReadUint8(bis);
                    int          len  = TlsUtilities.ReadUint24(bis);

                    /*
                     * Check if we have enough bytes in the buffer to read
                     * the full message.
                     */
                    if (handshakeQueue.Available >= (len + 4))
                    {
                        /*
                         * Read the message.
                         */
                        byte[] buf = new byte[len];
                        handshakeQueue.Read(buf, 0, len, 4);
                        handshakeQueue.RemoveData(len + 4);

                        /*
                         * If it is not a finished message, update our hashes
                         * we prepare for the finish message.
                         */
                        if (type != HP_FINISHED)
                        {
                            rs.hash1.BlockUpdate(beginning, 0, 4);
                            rs.hash2.BlockUpdate(beginning, 0, 4);
                            rs.hash1.BlockUpdate(buf, 0, len);
                            rs.hash2.BlockUpdate(buf, 0, len);
                        }

                        /*
                         * Now, parse the message.
                         */
                        MemoryStream inStr = new MemoryStream(buf, false);

                        /*
                         * Check the type.
                         */
                        switch (type)
                        {
                        case HP_CERTIFICATE:
                        {
                            switch (connection_state)
                            {
                            case CS_SERVER_HELLO_RECEIVED:
                            {
                                /*
                                 * Parse the certificates.
                                 */
                                Certificate cert = Certificate.Parse(inStr);
                                AssertEmpty(inStr);

                                X509CertificateStructure x509Cert = cert.certs[0];
                                SubjectPublicKeyInfo     keyInfo  = x509Cert.SubjectPublicKeyInfo;

                                try
                                {
                                    this.serverPublicKey = PublicKeyFactory.CreateKey(keyInfo);
                                }
                                catch (Exception)
                                {
                                    this.FailWithError(AL_fatal, AP_unsupported_certificate);
                                }

                                // Sanity check the PublicKeyFactory
                                if (this.serverPublicKey.IsPrivate)
                                {
                                    this.FailWithError(AL_fatal, AP_internal_error);
                                }

                                /*
                                 * Perform various checks per RFC2246 7.4.2
                                 * TODO "Unless otherwise specified, the signing algorithm for the certificate
                                 * must be the same as the algorithm for the certificate key."
                                 */
                                switch (this.chosenCipherSuite.KeyExchangeAlgorithm)
                                {
                                case TlsCipherSuite.KE_RSA:
                                    if (!(this.serverPublicKey is RsaKeyParameters))
                                    {
                                        this.FailWithError(AL_fatal, AP_certificate_unknown);
                                    }
                                    validateKeyUsage(x509Cert, KeyUsage.KeyEncipherment);
                                    break;

                                case TlsCipherSuite.KE_DHE_RSA:
                                    if (!(this.serverPublicKey is RsaKeyParameters))
                                    {
                                        this.FailWithError(AL_fatal, AP_certificate_unknown);
                                    }
                                    validateKeyUsage(x509Cert, KeyUsage.DigitalSignature);
                                    break;

                                case TlsCipherSuite.KE_DHE_DSS:
                                    if (!(this.serverPublicKey is DsaPublicKeyParameters))
                                    {
                                        this.FailWithError(AL_fatal, AP_certificate_unknown);
                                    }
                                    break;

                                default:
                                    this.FailWithError(AL_fatal, AP_unsupported_certificate);
                                    break;
                                }

                                /*
                                 * Verify them.
                                 */
                                if (!this.verifyer.IsValid(cert.GetCerts()))
                                {
                                    this.FailWithError(AL_fatal, AP_user_canceled);
                                }

                                break;
                            }

                            default:
                                this.FailWithError(AL_fatal, AP_unexpected_message);
                                break;
                            }

                            connection_state = CS_SERVER_CERTIFICATE_RECEIVED;
                            read             = true;
                            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[] receivedChecksum = new byte[12];
                                TlsUtilities.ReadFully(receivedChecksum, inStr);
                                AssertEmpty(inStr);

                                /*
                                 * Calculate our own checksum.
                                 */
                                byte[] checksum   = new byte[12];
                                byte[] md5andsha1 = new byte[16 + 20];
                                rs.hash2.DoFinal(md5andsha1, 0);
                                TlsUtilities.PRF(this.ms, TlsUtilities.ToByteArray("server finished"), md5andsha1, checksum);

                                /*
                                 * Compare both checksums.
                                 */
                                for (int i = 0; i < receivedChecksum.Length; i++)
                                {
                                    if (receivedChecksum[i] != checksum[i])
                                    {
                                        /*
                                         * 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;
                                read = 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
                                 */
                                this.serverRandom = new byte[32];
                                TlsUtilities.ReadFully(this.serverRandom, inStr);

                                /*
                                 * Currently, we don't support session ids
                                 */
                                short  sessionIdLength = TlsUtilities.ReadUint8(inStr);
                                byte[] sessionId       = new byte[sessionIdLength];
                                TlsUtilities.ReadFully(sessionId, inStr);

                                /*
                                 * Find out which ciphersuite the server has
                                 * chosen. If we don't support this ciphersuite,
                                 * the TlsCipherSuiteManager will throw an
                                 * exception.
                                 */
                                this.chosenCipherSuite = TlsCipherSuiteManager.GetCipherSuite(
                                    TlsUtilities.ReadUint16(inStr), this);

                                /*
                                 * 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);
                                }
                                AssertEmpty(inStr);

                                connection_state = CS_SERVER_HELLO_RECEIVED;
                                read             = true;
                                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
                                     * that we are doing RSA key exchange.
                                     */
                                    if (this.chosenCipherSuite.KeyExchangeAlgorithm != TlsCipherSuite.KE_RSA)
                                    {
                                        this.FailWithError(AL_fatal, AP_unexpected_message);
                                    }
                                }

                                AssertEmpty(inStr);
                                bool isCertReq = (connection_state == CS_CERTIFICATE_REQUEST_RECEIVED);
                                connection_state = CS_SERVER_HELLO_DONE_RECEIVED;

                                if (isCertReq)
                                {
                                    sendClientCertificate();
                                }

                                /*
                                 * Send the client key exchange message, depending
                                 * on the key exchange we are using in our
                                 * ciphersuite.
                                 */
                                switch (this.chosenCipherSuite.KeyExchangeAlgorithm)
                                {
                                case TlsCipherSuite.KE_RSA:
                                {
                                    /*
                                     * We are doing RSA key exchange. We will
                                     * choose a pre master secret and send it
                                     * rsa encrypted to the server.
                                     *
                                     * Prepare pre master secret.
                                     */
                                    pms    = new byte[48];
                                    pms[0] = 3;
                                    pms[1] = 1;
                                    random.NextBytes(pms, 2, 46);

                                    /*
                                     * Encode the pms and send it to the server.
                                     *
                                     * Prepare an Pkcs1Encoding with good random
                                     * padding.
                                     */
                                    RsaBlindedEngine rsa      = new RsaBlindedEngine();
                                    Pkcs1Encoding    encoding = new Pkcs1Encoding(rsa);
                                    encoding.Init(true, new ParametersWithRandom(this.serverPublicKey, this.random));
                                    byte[] encrypted = null;
                                    try
                                    {
                                        encrypted = encoding.ProcessBlock(pms, 0, pms.Length);
                                    }
                                    catch (InvalidCipherTextException)
                                    {
                                        /*
                                         * This should never happen, only during decryption.
                                         */
                                        this.FailWithError(AL_fatal, AP_internal_error);
                                    }

                                    /*
                                     * Send the encrypted pms.
                                     */
                                    sendClientKeyExchange(encrypted);
                                    break;
                                }

                                case TlsCipherSuite.KE_DHE_DSS:
                                case TlsCipherSuite.KE_DHE_RSA:
                                {
                                    /*
                                     * Send the Client Key Exchange message for
                                     * DHE key exchange.
                                     */
                                    byte[] YcByte = BigIntegers.AsUnsignedByteArray(this.Yc);

                                    sendClientKeyExchange(YcByte);

                                    break;
                                }

                                default:
                                    /*
                                     * Problem during handshake, we don't know
                                     * how to handle this key exchange method.
                                     */
                                    this.FailWithError(AL_fatal, AP_unexpected_message);
                                    break;
                                }

                                connection_state = CS_CLIENT_KEY_EXCHANGE_SEND;

                                /*
                                 * Now, we send change cipher state
                                 */
                                byte[] cmessage = new byte[1];
                                cmessage[0] = 1;
                                rs.WriteMessage((short)RL_CHANGE_CIPHER_SPEC, cmessage, 0, cmessage.Length);

                                connection_state = CS_CLIENT_CHANGE_CIPHER_SPEC_SEND;

                                /*
                                 * Calculate the ms
                                 */
                                this.ms = new byte[48];
                                byte[] randBytes = new byte[clientRandom.Length + serverRandom.Length];
                                Array.Copy(clientRandom, 0, randBytes, 0, clientRandom.Length);
                                Array.Copy(serverRandom, 0, randBytes, clientRandom.Length, serverRandom.Length);
                                TlsUtilities.PRF(pms, TlsUtilities.ToByteArray("master secret"), randBytes, this.ms);

                                /*
                                 * Initialize our cipher suite
                                 */
                                rs.writeSuite = this.chosenCipherSuite;
                                rs.writeSuite.Init(this.ms, clientRandom, serverRandom);

                                /*
                                 * Send our finished message.
                                 */
                                byte[] checksum   = new byte[12];
                                byte[] md5andsha1 = new byte[16 + 20];
                                rs.hash1.DoFinal(md5andsha1, 0);
                                TlsUtilities.PRF(this.ms, TlsUtilities.ToByteArray("client finished"), md5andsha1, checksum);

                                MemoryStream bos2 = new MemoryStream();
                                TlsUtilities.WriteUint8(HP_FINISHED, bos2);
                                TlsUtilities.WriteUint24(12, bos2);
                                bos2.Write(checksum, 0, checksum.Length);
                                byte[] message2 = bos2.ToArray();

                                rs.WriteMessage((short)RL_HANDSHAKE, message2, 0, message2.Length);

                                this.connection_state = CS_CLIENT_FINISHED_SEND;
                                read = true;
                                break;

                            default:
                                this.FailWithError(AL_fatal, AP_handshake_failure);
                                break;
                            }
                            break;

                        case HP_SERVER_KEY_EXCHANGE:
                        {
                            switch (connection_state)
                            {
                            case CS_SERVER_CERTIFICATE_RECEIVED:
                            {
                                /*
                                 * Check that we are doing DHE key exchange
                                 */
                                switch (this.chosenCipherSuite.KeyExchangeAlgorithm)
                                {
                                case TlsCipherSuite.KE_DHE_RSA:
                                {
                                    processDHEKeyExchange(inStr, new TlsRsaSigner());
                                    break;
                                }

                                case TlsCipherSuite.KE_DHE_DSS:
                                {
                                    processDHEKeyExchange(inStr, new TlsDssSigner());
                                    break;
                                }

                                default:
                                    this.FailWithError(AL_fatal, AP_unexpected_message);
                                    break;
                                }
                                break;
                            }

                            default:
                                this.FailWithError(AL_fatal, AP_unexpected_message);
                                break;
                            }

                            this.connection_state = CS_SERVER_KEY_EXCHANGE_RECEIVED;
                            read = true;
                            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
                                     * that we are doing RSA key exchange.
                                     */
                                    if (this.chosenCipherSuite.KeyExchangeAlgorithm != TlsCipherSuite.KE_RSA)
                                    {
                                        this.FailWithError(AL_fatal, AP_unexpected_message);
                                    }
                                }

                                int    typesLength = TlsUtilities.ReadUint8(inStr);
                                byte[] types       = new byte[typesLength];
                                TlsUtilities.ReadFully(types, inStr);

                                int    authsLength = TlsUtilities.ReadUint16(inStr);
                                byte[] auths       = new byte[authsLength];
                                TlsUtilities.ReadFully(auths, inStr);

                                AssertEmpty(inStr);
                                break;
                            }

                            default:
                                this.FailWithError(AL_fatal, AP_unexpected_message);
                                break;
                            }

                            this.connection_state = CS_CERTIFICATE_REQUEST_RECEIVED;
                            read = true;
                            break;

                        case HP_HELLO_REQUEST:
                        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;
                        }
                    }
                }
            }while (read);
        }
        private void processHandshake()
        {
            bool read;
            do
            {
                read = false;

                /*
                * We need the first 4 bytes, they contain type and length of
                * the message.
                */
                if (handshakeQueue.Available >= 4)
                {
                    byte[] beginning = new byte[4];
                    handshakeQueue.Read(beginning, 0, 4, 0);
                    MemoryStream bis = new MemoryStream(beginning, false);
                    short type = TlsUtilities.ReadUint8(bis);
                    int len = TlsUtilities.ReadUint24(bis);

                    /*
                    * Check if we have enough bytes in the buffer to read
                    * the full message.
                    */
                    if (handshakeQueue.Available >= (len + 4))
                    {
                        /*
                        * Read the message.
                        */
                        byte[] buf = new byte[len];
                        handshakeQueue.Read(buf, 0, len, 4);
                        handshakeQueue.RemoveData(len + 4);

                        /*
                        * If it is not a finished message, update our hashes
                        * we prepare for the finish message.
                        */
                        if (type != HP_FINISHED)
                        {
                            rs.hash1.BlockUpdate(beginning, 0, 4);
                            rs.hash2.BlockUpdate(beginning, 0, 4);
                            rs.hash1.BlockUpdate(buf, 0, len);
                            rs.hash2.BlockUpdate(buf, 0, len);
                        }

                        /*
                        * Now, parse the message.
                        */
                        MemoryStream inStr = new MemoryStream(buf, false);

                        /*
                        * Check the type.
                        */
                        switch (type)
                        {
                            case HP_CERTIFICATE:
                                switch (connection_state)
                                {
                                    case CS_SERVER_HELLO_RECEIVED:
                                        /*
                                        * Parse the certificates.
                                        */
                                        Certificate cert = Certificate.Parse(inStr);
                                        AssertEmpty(inStr);

                                        /*
                                        * Verify them.
                                        */
                                        if (!this.verifyer.IsValid(cert.GetCerts()))
                                        {
                                            this.FailWithError(AL_fatal, AP_user_canceled);
                                        }

                                        /*
                                        * We only support RSA certificates. Lets hope
                                        * this is one.
                                        */
                                        RsaPublicKeyStructure rsaKey = null;
                                        try
                                        {
                                            rsaKey = RsaPublicKeyStructure.GetInstance(
                                                cert.certs[0].TbsCertificate.SubjectPublicKeyInfo.GetPublicKey());
                                        }
                                        catch (Exception)
                                        {
                                            /*
                                            * Sorry, we have to fail ;-(
                                            */
                                            this.FailWithError(AL_fatal, AP_unsupported_certificate);
                                        }

                                        /*
                                        * Parse the servers public RSA key.
                                        */
                                        this.serverRsaKey = new RsaKeyParameters(
                                            false,
                                            rsaKey.Modulus,
                                            rsaKey.PublicExponent);

                                        connection_state = CS_SERVER_CERTIFICATE_RECEIVED;
                                        read = true;
                                        break;
                                    default:
                                        this.FailWithError(AL_fatal, AP_unexpected_message);
                                        break;
                                }
                                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[] receivedChecksum = new byte[12];
                                        TlsUtilities.ReadFully(receivedChecksum, inStr);
                                        AssertEmpty(inStr);

                                        /*
                                        * Calculate our owne checksum.
                                        */
                                        byte[] checksum = new byte[12];
                                        byte[] md5andsha1 = new byte[16 + 20];
                                        rs.hash2.DoFinal(md5andsha1, 0);
                                        TlsUtilities.PRF(this.ms, TlsUtilities.ToByteArray("server finished"), md5andsha1, checksum);

                                        /*
                                        * Compare both checksums.
                                        */
                                        for (int i = 0; i < receivedChecksum.Length; i++)
                                        {
                                            if (receivedChecksum[i] != checksum[i])
                                            {
                                                /*
                                                * 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;
                                        read = 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
                                        */
                                        this.serverRandom = new byte[32];
                                        TlsUtilities.ReadFully(this.serverRandom, inStr);

                                        /*
                                        * Currenty, we don't support session ids
                                        */
                                        short sessionIdLength = TlsUtilities.ReadUint8(inStr);
                                        byte[] sessionId = new byte[sessionIdLength];
                                        TlsUtilities.ReadFully(sessionId, inStr);

                                        /*
                                        * Find out which ciphersuite the server has
                                        * choosen. If we don't support this ciphersuite,
                                        * the TlsCipherSuiteManager will throw an
                                        * exception.
                                        */
                                        this.choosenCipherSuite = TlsCipherSuiteManager.GetCipherSuite(
                                            TlsUtilities.ReadUint16(inStr), this);

                                        /*
                                        * 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);
                                        }
                                        AssertEmpty(inStr);

                                        connection_state = CS_SERVER_HELLO_RECEIVED;
                                        read = true;
                                        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:

                                        // NB: Original code used case label fall-through
                                        if (connection_state == CS_SERVER_CERTIFICATE_RECEIVED)
                                        {
                                            /*
                                            * There was no server key exchange message, check
                                            * that we are doing RSA key exchange.
                                            */
                                            if (this.choosenCipherSuite.KeyExchangeAlgorithm != TlsCipherSuite.KE_RSA)
                                            {
                                                this.FailWithError(AL_fatal, AP_unexpected_message);
                                            }
                                        }

                                        AssertEmpty(inStr);
                                        connection_state = CS_SERVER_HELLO_DONE_RECEIVED;

                                        /*
                                        * Send the client key exchange message, depending
                                        * on the key exchange we are using in our
                                        * ciphersuite.
                                        */
                                        short ke = this.choosenCipherSuite.KeyExchangeAlgorithm;

                                        switch (ke)
                                        {
                                            case TlsCipherSuite.KE_RSA:
                                                /*
                                                * We are doing RSA key exchange. We will
                                                * choose a pre master secret and send it
                                                * rsa encrypted to the server.
                                                *
                                                * Prepare pre master secret.
                                                */
                                                pms = new byte[48];
                                                pms[0] = 3;
                                                pms[1] = 1;
                                                random.NextBytes(pms, 2, 46);

                                                /*
                                                * Encode the pms and send it to the server.
                                                *
                                                * Prepare an Pkcs1Encoding with good random
                                                * padding.
                                                */
                                                RsaBlindedEngine rsa = new RsaBlindedEngine();
                                                Pkcs1Encoding encoding = new Pkcs1Encoding(rsa);
                                                encoding.Init(true, new ParametersWithRandom(this.serverRsaKey, this.random));
                                                byte[] encrypted = null;
                                                try
                                                {
                                                    encrypted = encoding.ProcessBlock(pms, 0, pms.Length);
                                                }
                                                catch (InvalidCipherTextException)
                                                {
                                                    /*
                                                    * This should never happen, only during decryption.
                                                    */
                                                    this.FailWithError(AL_fatal, AP_internal_error);
                                                }

                                                /*
                                                * Send the encrypted pms.
                                                */
                                                MemoryStream bos = new MemoryStream();
                                                TlsUtilities.WriteUint8(HP_CLIENT_KEY_EXCHANGE, bos);
                                                TlsUtilities.WriteUint24(encrypted.Length + 2, bos);
                                                TlsUtilities.WriteUint16(encrypted.Length, bos);
                                                bos.Write(encrypted, 0, encrypted.Length);
                                                byte[] message = bos.ToArray();

                                                rs.WriteMessage((short)RL_HANDSHAKE, message, 0, message.Length);
                                                break;
                                            case TlsCipherSuite.KE_DHE_RSA:
                                                /*
                                                * Send the Client Key Exchange message for
                                                * DHE key exchange.
                                                */
                                                byte[] YcByte = this.Yc.ToByteArray();
                                                MemoryStream DHbos = new MemoryStream();
                                                TlsUtilities.WriteUint8(HP_CLIENT_KEY_EXCHANGE, DHbos);
                                                TlsUtilities.WriteUint24(YcByte.Length + 2, DHbos);
                                                TlsUtilities.WriteUint16(YcByte.Length, DHbos);
                                                DHbos.Write(YcByte, 0, YcByte.Length);
                                                byte[] DHmessage = DHbos.ToArray();

                                                rs.WriteMessage((short)RL_HANDSHAKE, DHmessage, 0, DHmessage.Length);

                                                break;
                                            default:
                                                /*
                                                * Proble during handshake, we don't know
                                                * how to thandle this key exchange method.
                                                */
                                                this.FailWithError(AL_fatal, AP_unexpected_message);
                                                break;

                                        }

                                        connection_state = CS_CLIENT_KEY_EXCHANGE_SEND;

                                        /*
                                        * Now, we send change cipher state
                                        */
                                        byte[] cmessage = new byte[1];
                                        cmessage[0] = 1;
                                        rs.WriteMessage((short)RL_CHANGE_CIPHER_SPEC, cmessage, 0, cmessage.Length);

                                        connection_state = CS_CLIENT_CHANGE_CIPHER_SPEC_SEND;

                                        /*
                                        * Calculate the ms
                                        */
                                        this.ms = new byte[48];
                                        byte[] randBytes = new byte[clientRandom.Length + serverRandom.Length];
                                        Array.Copy(clientRandom, 0, randBytes, 0, clientRandom.Length);
                                        Array.Copy(serverRandom, 0, randBytes, clientRandom.Length, serverRandom.Length);
                                        TlsUtilities.PRF(pms, TlsUtilities.ToByteArray("master secret"), randBytes, this.ms);

                                        /*
                                        * Initialize our cipher suite
                                        */
                                        rs.writeSuite = this.choosenCipherSuite;
                                        rs.writeSuite.Init(this.ms, clientRandom, serverRandom);

                                        /*
                                        * Send our finished message.
                                        */
                                        byte[] checksum = new byte[12];
                                        byte[] md5andsha1 = new byte[16 + 20];
                                        rs.hash1.DoFinal(md5andsha1, 0);
                                        TlsUtilities.PRF(this.ms, TlsUtilities.ToByteArray("client finished"), md5andsha1, checksum);

                                        MemoryStream bos2 = new MemoryStream();
                                        TlsUtilities.WriteUint8(HP_FINISHED, bos2);
                                        TlsUtilities.WriteUint24(12, bos2);
                                        bos2.Write(checksum, 0, checksum.Length);
                                        byte[] message2 = bos2.ToArray();

                                        rs.WriteMessage((short)RL_HANDSHAKE, message2, 0, message2.Length);

                                        this.connection_state = CS_CLIENT_FINISHED_SEND;
                                        read = true;
                                        break;
                                    default:
                                        this.FailWithError(AL_fatal, AP_handshake_failure);
                                        break;
                                }
                                break;
                            case HP_SERVER_KEY_EXCHANGE:
                                switch (connection_state)
                                {
                                    case CS_SERVER_CERTIFICATE_RECEIVED:
                                        /*
                                        * Check that we are doing DHE key exchange
                                        */
                                        if (this.choosenCipherSuite.KeyExchangeAlgorithm != TlsCipherSuite.KE_DHE_RSA)
                                        {
                                            this.FailWithError(AL_fatal, AP_unexpected_message);
                                        }

                                        /*
                                        * Parse the Structure
                                        */
                                        int pLength = TlsUtilities.ReadUint16(inStr);
                                        byte[] pByte = new byte[pLength];
                                        TlsUtilities.ReadFully(pByte, inStr);

                                        int gLength = TlsUtilities.ReadUint16(inStr);
                                        byte[] gByte = new byte[gLength];
                                        TlsUtilities.ReadFully(gByte, inStr);

                                        int YsLength = TlsUtilities.ReadUint16(inStr);
                                        byte[] YsByte = new byte[YsLength];
                                        TlsUtilities.ReadFully(YsByte, inStr);

                                        int sigLength = TlsUtilities.ReadUint16(inStr);
                                        byte[] sigByte = new byte[sigLength];
                                        TlsUtilities.ReadFully(sigByte, inStr);

                                        this.AssertEmpty(inStr);

                                        /*
                                        * Verify the Signature.
                                        *
                                        * First, calculate the hash.
                                        */
                                        CombinedHash sigDigest = new CombinedHash();
                                        MemoryStream signedData = new MemoryStream();
                                        TlsUtilities.WriteUint16(pLength, signedData);
                                        signedData.Write(pByte, 0, pByte.Length);
                                        TlsUtilities.WriteUint16(gLength, signedData);
                                        signedData.Write(gByte, 0, gByte.Length);
                                        TlsUtilities.WriteUint16(YsLength, signedData);
                                        signedData.Write(YsByte, 0, YsByte.Length);
                                        byte[] signed = signedData.ToArray();

                                        sigDigest.BlockUpdate(this.clientRandom, 0, this.clientRandom.Length);
                                        sigDigest.BlockUpdate(this.serverRandom, 0, this.serverRandom.Length);
                                        sigDigest.BlockUpdate(signed, 0, signed.Length);
                                        byte[] hash = new byte[sigDigest.GetDigestSize()];
                                        sigDigest.DoFinal(hash, 0);

                                        /*
                                        * Now, do the RSA operation
                                        */
                                        RsaBlindedEngine rsa = new RsaBlindedEngine();
                                        Pkcs1Encoding encoding = new Pkcs1Encoding(rsa);
                                        encoding.Init(false, this.serverRsaKey);

                                        /*
                                        * The data which was signed
                                        */
                                        byte[] sigHash = null;

                                        try
                                        {
                                            sigHash = encoding.ProcessBlock(sigByte, 0, sigByte.Length);
                                        }
                                        catch (InvalidCipherTextException)
                                        {
                                            this.FailWithError(AL_fatal, AP_bad_certificate);
                                        }

                                        /*
                                        * Check if the data which was signed is equal to
                                        * the hash we calculated.
                                        */
                                        if (sigHash.Length != hash.Length)
                                        {
                                            this.FailWithError(AL_fatal, AP_bad_certificate);
                                        }

                                        for (int i = 0; i < sigHash.Length; i++)
                                        {
                                            if (sigHash[i] != hash[i])
                                            {
                                                this.FailWithError(AL_fatal, AP_bad_certificate);
                                            }
                                        }

                                        /*
                                        * OK, Signature was correct.
                                        *
                                        * Do the DH calculation.
                                        */
                                        BigInteger p = new BigInteger(1, pByte);
                                        BigInteger g = new BigInteger(1, gByte);
                                        BigInteger Ys = new BigInteger(1, YsByte);
                                        BigInteger x = new BigInteger(p.BitLength - 1, this.random);
                                        Yc = g.ModPow(x, p);
                                        this.pms = (Ys.ModPow(x, p)).ToByteArray();

                                        /*
                                        * Remove leading zero byte, if present.
                                        */
                                        if (this.pms[0] == 0)
                                        {
                                            byte[] tmp = new byte[this.pms.Length - 1];
                                            Array.Copy(this.pms, 1, tmp, 0, tmp.Length);
                                            this.pms = tmp;
                                        }

                                        this.connection_state = CS_SERVER_KEY_EXCHANGE_RECEIVED;
                                        read = true;
                                        break;
                                    default:
                                        this.FailWithError(AL_fatal, AP_unexpected_message);
                                        break;
                                }
                                break;
                            case HP_HELLO_REQUEST:
                            case HP_CLIENT_KEY_EXCHANGE:
                            case HP_CERTIFICATE_REQUEST:
                            case HP_CERTIFICATE_VERIFY:
                            case HP_CLIENT_HELLO:
                            default:
                                // We do not support this!
                                this.FailWithError(AL_fatal, AP_unexpected_message);
                                break;

                        }

                    }
                }
            }
            while (read);
        }
Example #5
0
		private void processHandshake()
		{
			bool read;
			do
			{
				read = false;

				/*
				* We need the first 4 bytes, they contain type and length of
				* the message.
				*/
				if (handshakeQueue.Available >= 4)
				{
					byte[] beginning = new byte[4];
					handshakeQueue.Read(beginning, 0, 4, 0);
					MemoryStream bis = new MemoryStream(beginning, false);
					short type = TlsUtilities.ReadUint8(bis);
					int len = TlsUtilities.ReadUint24(bis);

					/*
					* Check if we have enough bytes in the buffer to read
					* the full message.
					*/
					if (handshakeQueue.Available >= (len + 4))
					{
						/*
						* Read the message.
						*/
						byte[] buf = new byte[len];
						handshakeQueue.Read(buf, 0, len, 4);
						handshakeQueue.RemoveData(len + 4);

						/*
						* If it is not a finished message, update our hashes
						* we prepare for the finish message.
						*/
						if (type != HP_FINISHED)
						{
							rs.hash1.BlockUpdate(beginning, 0, 4);
							rs.hash2.BlockUpdate(beginning, 0, 4);
							rs.hash1.BlockUpdate(buf, 0, len);
							rs.hash2.BlockUpdate(buf, 0, len);
						}

						/*
						* Now, parse the message.
						*/
						MemoryStream inStr = new MemoryStream(buf, false);

						/*
						* Check the type.
						*/
						switch (type)
						{
							case HP_CERTIFICATE:
							{
								switch (connection_state)
								{
									case CS_SERVER_HELLO_RECEIVED:
									{
										/*
										* Parse the certificates.
										*/
										Certificate cert = Certificate.Parse(inStr);
										AssertEmpty(inStr);

										X509CertificateStructure x509Cert = cert.certs[0];
										SubjectPublicKeyInfo keyInfo = x509Cert.SubjectPublicKeyInfo;

										try
										{
											this.serverPublicKey = PublicKeyFactory.CreateKey(keyInfo);
										}
										catch (Exception)
										{
											this.FailWithError(AL_fatal, AP_unsupported_certificate);
										}

										// Sanity check the PublicKeyFactory
										if (this.serverPublicKey.IsPrivate)
										{
											this.FailWithError(AL_fatal, AP_internal_error);
										}

										/*
										* Perform various checks per RFC2246 7.4.2
										* TODO "Unless otherwise specified, the signing algorithm for the certificate
										* must be the same as the algorithm for the certificate key."
										*/
										switch (this.chosenCipherSuite.KeyExchangeAlgorithm)
										{
											case TlsCipherSuite.KE_RSA:
												if (!(this.serverPublicKey is RsaKeyParameters))
												{
													this.FailWithError(AL_fatal, AP_certificate_unknown);
												}
												validateKeyUsage(x509Cert, KeyUsage.KeyEncipherment);
												break;
											case TlsCipherSuite.KE_DHE_RSA:
											case TlsCipherSuite.KE_SRP_RSA:
												if (!(this.serverPublicKey is RsaKeyParameters))
												{
													this.FailWithError(AL_fatal, AP_certificate_unknown);
												}
												validateKeyUsage(x509Cert, KeyUsage.DigitalSignature);
												break;
											case TlsCipherSuite.KE_DHE_DSS:
											case TlsCipherSuite.KE_SRP_DSS:
												if (!(this.serverPublicKey is DsaPublicKeyParameters))
												{
													this.FailWithError(AL_fatal, AP_certificate_unknown);
												}
												break;
											default:
												this.FailWithError(AL_fatal, AP_unsupported_certificate);
												break;
										}

										/*
										* Verify them.
										*/
										if (!this.verifyer.IsValid(cert.GetCerts()))
										{
											this.FailWithError(AL_fatal, AP_user_canceled);
										}

										break;
									}
									default:
										this.FailWithError(AL_fatal, AP_unexpected_message);
										break;
								}

								connection_state = CS_SERVER_CERTIFICATE_RECEIVED;
								read = true;
								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[] receivedChecksum = new byte[12];
										TlsUtilities.ReadFully(receivedChecksum, inStr);
										AssertEmpty(inStr);

										/*
										* Calculate our own checksum.
										*/
										byte[] checksum = new byte[12];
										byte[] md5andsha1 = new byte[16 + 20];
										rs.hash2.DoFinal(md5andsha1, 0);
										TlsUtilities.PRF(this.ms, TlsUtilities.ToByteArray("server finished"), md5andsha1, checksum);

										/*
										* Compare both checksums.
										*/
										for (int i = 0; i < receivedChecksum.Length; i++)
										{
											if (receivedChecksum[i] != checksum[i])
											{
												/*
												* 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;
										read = 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
										*/
										this.serverRandom = new byte[32];
										TlsUtilities.ReadFully(this.serverRandom, inStr);

										/*
										* Currently, we don't support session ids
										*/
										byte[] sessionId = TlsUtilities.ReadOpaque8(inStr);

										/*
										* Find out which ciphersuite the server has
										* chosen. If we don't support this ciphersuite,
										* the TlsCipherSuiteManager will throw an
										* exception.
										*/
										this.chosenCipherSuite = TlsCipherSuiteManager.GetCipherSuite(
											TlsUtilities.ReadUint16(inStr), this);

										/*
										* 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 && inStr.Position < inStr.Length)
	                                    {
	                                        // Process extensions from extended server hello
	                                        byte[] extBytes = TlsUtilities.ReadOpaque16(inStr);
	
	                                        // Int32 -> byte[]
	                                        Hashtable serverExtensions = new Hashtable();

	                                        MemoryStream ext = new MemoryStream(extBytes, false);
	                                        while (ext.Position < ext.Length)
	                                        {
	                                            int extType = TlsUtilities.ReadUint16(ext);
	                                            byte[] extValue = TlsUtilities.ReadOpaque16(ext);

	                                            serverExtensions[extType] = extValue;
	                                        }

	                                        // TODO Validate/process serverExtensions (via client?)
	                                        // TODO[SRP]
	                                    }

										/*
										* Process any extensions
										*/
										// TODO[SRP]
//										if (inStr.Position < inStr.Length)
//										{
//											int extensionsLength = TlsUtilities.ReadUint16(inStr);
//											byte[] extensions = new byte[extensionsLength];
//											TlsUtilities.ReadFully(extensions, inStr);
//
//											// TODO Validate/process
//										}

										AssertEmpty(inStr);

										connection_state = CS_SERVER_HELLO_RECEIVED;
										read = true;
										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
											* that we are doing RSA key exchange.
											*/
											if (this.chosenCipherSuite.KeyExchangeAlgorithm != TlsCipherSuite.KE_RSA)
											{
												this.FailWithError(AL_fatal, AP_unexpected_message);
											}
										}

										AssertEmpty(inStr);
										bool isCertReq = (connection_state == CS_CERTIFICATE_REQUEST_RECEIVED);
										connection_state = CS_SERVER_HELLO_DONE_RECEIVED;

										if (isCertReq)
										{
											sendClientCertificate();
										}

										/*
										* Send the client key exchange message, depending
										* on the key exchange we are using in our
										* ciphersuite.
										*/
										switch (this.chosenCipherSuite.KeyExchangeAlgorithm)
										{
											case TlsCipherSuite.KE_RSA:
											{
												/*
												* We are doing RSA key exchange. We will
												* choose a pre master secret and send it
												* rsa encrypted to the server.
												*
												* Prepare pre master secret.
												*/
												pms = new byte[48];
												pms[0] = 3;
												pms[1] = 1;
												random.NextBytes(pms, 2, 46);

												/*
												* Encode the pms and send it to the server.
												*
												* Prepare an Pkcs1Encoding with good random
												* padding.
												*/
												RsaBlindedEngine rsa = new RsaBlindedEngine();
												Pkcs1Encoding encoding = new Pkcs1Encoding(rsa);
												encoding.Init(true, new ParametersWithRandom(this.serverPublicKey, this.random));
												byte[] encrypted = null;
												try
												{
													encrypted = encoding.ProcessBlock(pms, 0, pms.Length);
												}
												catch (InvalidCipherTextException)
												{
													/*
													* This should never happen, only during decryption.
													*/
													this.FailWithError(AL_fatal, AP_internal_error);
												}

												/*
												* Send the encrypted pms.
												*/
												sendClientKeyExchange(encrypted);
												break;
											}
											case TlsCipherSuite.KE_DHE_DSS:
											case TlsCipherSuite.KE_DHE_RSA:
											{
												/*
												* Send the Client Key Exchange message for
												* DHE key exchange.
												*/
												byte[] YcByte = BigIntegers.AsUnsignedByteArray(this.Yc);

												sendClientKeyExchange(YcByte);

												break;
											}
											case TlsCipherSuite.KE_SRP:
											case TlsCipherSuite.KE_SRP_RSA:
											case TlsCipherSuite.KE_SRP_DSS:
											{
												/*
												* Send the Client Key Exchange message for
												* SRP key exchange.
												*/
												byte[] bytes = BigIntegers.AsUnsignedByteArray(this.SRP_A);

												sendClientKeyExchange(bytes);

												break;
											}
											default:
												/*
												* Problem during handshake, we don't know
												* how to handle this key exchange method.
												*/
												this.FailWithError(AL_fatal, AP_unexpected_message);
												break;

										}

										connection_state = CS_CLIENT_KEY_EXCHANGE_SEND;

										/*
										* Now, we send change cipher state
										*/
										byte[] cmessage = new byte[1];
										cmessage[0] = 1;
										rs.WriteMessage((short)RL_CHANGE_CIPHER_SPEC, cmessage, 0, cmessage.Length);

										connection_state = CS_CLIENT_CHANGE_CIPHER_SPEC_SEND;

										/*
										* Calculate the ms
										*/
										this.ms = new byte[48];
										byte[] randBytes = new byte[clientRandom.Length + serverRandom.Length];
										Array.Copy(clientRandom, 0, randBytes, 0, clientRandom.Length);
										Array.Copy(serverRandom, 0, randBytes, clientRandom.Length, serverRandom.Length);
										TlsUtilities.PRF(pms, TlsUtilities.ToByteArray("master secret"), randBytes, this.ms);

										/*
										* Initialize our cipher suite
										*/
										rs.writeSuite = this.chosenCipherSuite;
										rs.writeSuite.Init(this.ms, clientRandom, serverRandom);

										/*
										* Send our finished message.
										*/
										byte[] checksum = new byte[12];
										byte[] md5andsha1 = new byte[16 + 20];
										rs.hash1.DoFinal(md5andsha1, 0);
										TlsUtilities.PRF(this.ms, TlsUtilities.ToByteArray("client finished"), md5andsha1, checksum);

										MemoryStream bos2 = new MemoryStream();
										TlsUtilities.WriteUint8(HP_FINISHED, bos2);
										TlsUtilities.WriteUint24(12, bos2);
										bos2.Write(checksum, 0, checksum.Length);
										byte[] message2 = bos2.ToArray();

										rs.WriteMessage((short)RL_HANDSHAKE, message2, 0, message2.Length);

										this.connection_state = CS_CLIENT_FINISHED_SEND;
										read = true;
										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
											* that we are doing SRP key exchange.
											*/
											if (this.chosenCipherSuite.KeyExchangeAlgorithm != TlsCipherSuite.KE_SRP)
											{
												this.FailWithError(AL_fatal, AP_unexpected_message);
											}
										}

										/*
										* Check that we are doing DHE key exchange
										*/
										switch (this.chosenCipherSuite.KeyExchangeAlgorithm)
										{
											case TlsCipherSuite.KE_DHE_RSA:
											{
												processDHEKeyExchange(inStr, new TlsRsaSigner());
												break;
											}
											case TlsCipherSuite.KE_DHE_DSS:
											{
												processDHEKeyExchange(inStr, new TlsDssSigner());
												break;
											}
											case TlsCipherSuite.KE_SRP:
											{
												processSRPKeyExchange(inStr, null);
												break;
											}
											case TlsCipherSuite.KE_SRP_RSA:
											{
												processSRPKeyExchange(inStr, new TlsRsaSigner());
												break;
											}
											case TlsCipherSuite.KE_SRP_DSS:
											{
												processSRPKeyExchange(inStr, new TlsDssSigner());
												break;
											}
											default:
												this.FailWithError(AL_fatal, AP_unexpected_message);
												break;
										}
										break;
									}
									default:
										this.FailWithError(AL_fatal, AP_unexpected_message);
										break;
								}

								this.connection_state = CS_SERVER_KEY_EXCHANGE_RECEIVED;
								read = true;
								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
											* that we are doing RSA key exchange.
											*/
											if (this.chosenCipherSuite.KeyExchangeAlgorithm != TlsCipherSuite.KE_RSA)
											{
												this.FailWithError(AL_fatal, AP_unexpected_message);
											}
										}

										byte[] types = TlsUtilities.ReadOpaque8(inStr);
										byte[] auths = TlsUtilities.ReadOpaque8(inStr);

										// TODO Validate/process

										AssertEmpty(inStr);
										break;
									}
									default:
										this.FailWithError(AL_fatal, AP_unexpected_message);
										break;
								}

								this.connection_state = CS_CERTIFICATE_REQUEST_RECEIVED;
								read = true;
								break;
							case HP_HELLO_REQUEST:
							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;
						}
					}
				}
			}
			while (read);
		}
Example #6
0
        private void processHandshake()
        {
            bool read;

            do
            {
                read = false;

                /*
                 * We need the first 4 bytes, they contain type and length of
                 * the message.
                 */
                if (handshakeQueue.Available >= 4)
                {
                    byte[] beginning = new byte[4];
                    handshakeQueue.Read(beginning, 0, 4, 0);
                    MemoryStream bis  = new MemoryStream(beginning, false);
                    short        type = TlsUtilities.ReadUint8(bis);
                    int          len  = TlsUtilities.ReadUint24(bis);

                    /*
                     * Check if we have enough bytes in the buffer to read
                     * the full message.
                     */
                    if (handshakeQueue.Available >= (len + 4))
                    {
                        /*
                         * Read the message.
                         */
                        byte[] buf = new byte[len];
                        handshakeQueue.Read(buf, 0, len, 4);
                        handshakeQueue.RemoveData(len + 4);

                        /*
                         * If it is not a finished message, update our hashes
                         * we prepare for the finish message.
                         */
                        if (type != HP_FINISHED)
                        {
                            rs.hash1.BlockUpdate(beginning, 0, 4);
                            rs.hash2.BlockUpdate(beginning, 0, 4);
                            rs.hash1.BlockUpdate(buf, 0, len);
                            rs.hash2.BlockUpdate(buf, 0, len);
                        }

                        /*
                         * Now, parse the message.
                         */
                        MemoryStream inStr = new MemoryStream(buf, false);

                        /*
                         * Check the type.
                         */
                        switch (type)
                        {
                        case HP_CERTIFICATE:
                            switch (connection_state)
                            {
                            case CS_SERVER_HELLO_RECEIVED:
                                /*
                                 * Parse the certificates.
                                 */
                                Certificate cert = Certificate.Parse(inStr);
                                AssertEmpty(inStr);

                                /*
                                 * Verify them.
                                 */
                                if (!this.verifyer.IsValid(cert.GetCerts()))
                                {
                                    this.FailWithError(AL_fatal, AP_user_canceled);
                                }

                                /*
                                 * We only support RSA certificates. Lets hope
                                 * this is one.
                                 */
                                RsaPublicKeyStructure rsaKey = null;
                                try
                                {
                                    rsaKey = RsaPublicKeyStructure.GetInstance(
                                        cert.certs[0].TbsCertificate.SubjectPublicKeyInfo.GetPublicKey());
                                }
                                catch (Exception)
                                {
                                    /*
                                     * Sorry, we have to fail ;-(
                                     */
                                    this.FailWithError(AL_fatal, AP_unsupported_certificate);
                                }

                                /*
                                 * Parse the servers public RSA key.
                                 */
                                this.serverRsaKey = new RsaKeyParameters(
                                    false,
                                    rsaKey.Modulus,
                                    rsaKey.PublicExponent);

                                connection_state = CS_SERVER_CERTIFICATE_RECEIVED;
                                read             = true;
                                break;

                            default:
                                this.FailWithError(AL_fatal, AP_unexpected_message);
                                break;
                            }
                            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[] receivedChecksum = new byte[12];
                                TlsUtilities.ReadFully(receivedChecksum, inStr);
                                AssertEmpty(inStr);

                                /*
                                 * Calculate our owne checksum.
                                 */
                                byte[] checksum   = new byte[12];
                                byte[] md5andsha1 = new byte[16 + 20];
                                rs.hash2.DoFinal(md5andsha1, 0);
                                TlsUtilities.PRF(this.ms, TlsUtilities.ToByteArray("server finished"), md5andsha1, checksum);

                                /*
                                 * Compare both checksums.
                                 */
                                for (int i = 0; i < receivedChecksum.Length; i++)
                                {
                                    if (receivedChecksum[i] != checksum[i])
                                    {
                                        /*
                                         * 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;
                                read = 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
                                 */
                                this.serverRandom = new byte[32];
                                TlsUtilities.ReadFully(this.serverRandom, inStr);

                                /*
                                 * Currenty, we don't support session ids
                                 */
                                short  sessionIdLength = TlsUtilities.ReadUint8(inStr);
                                byte[] sessionId       = new byte[sessionIdLength];
                                TlsUtilities.ReadFully(sessionId, inStr);

                                /*
                                 * Find out which ciphersuite the server has
                                 * choosen. If we don't support this ciphersuite,
                                 * the TlsCipherSuiteManager will throw an
                                 * exception.
                                 */
                                this.choosenCipherSuite = TlsCipherSuiteManager.GetCipherSuite(
                                    TlsUtilities.ReadUint16(inStr), this);

                                /*
                                 * 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);
                                }
                                AssertEmpty(inStr);

                                connection_state = CS_SERVER_HELLO_RECEIVED;
                                read             = true;
                                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:

                                // NB: Original code used case label fall-through
                                if (connection_state == CS_SERVER_CERTIFICATE_RECEIVED)
                                {
                                    /*
                                     * There was no server key exchange message, check
                                     * that we are doing RSA key exchange.
                                     */
                                    if (this.choosenCipherSuite.KeyExchangeAlgorithm != TlsCipherSuite.KE_RSA)
                                    {
                                        this.FailWithError(AL_fatal, AP_unexpected_message);
                                    }
                                }

                                AssertEmpty(inStr);
                                connection_state = CS_SERVER_HELLO_DONE_RECEIVED;

                                /*
                                 * Send the client key exchange message, depending
                                 * on the key exchange we are using in our
                                 * ciphersuite.
                                 */
                                short ke = this.choosenCipherSuite.KeyExchangeAlgorithm;

                                switch (ke)
                                {
                                case TlsCipherSuite.KE_RSA:
                                    /*
                                     * We are doing RSA key exchange. We will
                                     * choose a pre master secret and send it
                                     * rsa encrypted to the server.
                                     *
                                     * Prepare pre master secret.
                                     */
                                    pms    = new byte[48];
                                    pms[0] = 3;
                                    pms[1] = 1;
                                    random.NextBytes(pms, 2, 46);

                                    /*
                                     * Encode the pms and send it to the server.
                                     *
                                     * Prepare an Pkcs1Encoding with good random
                                     * padding.
                                     */
                                    RsaBlindedEngine rsa      = new RsaBlindedEngine();
                                    Pkcs1Encoding    encoding = new Pkcs1Encoding(rsa);
                                    encoding.Init(true, new ParametersWithRandom(this.serverRsaKey, this.random));
                                    byte[] encrypted = null;
                                    try
                                    {
                                        encrypted = encoding.ProcessBlock(pms, 0, pms.Length);
                                    }
                                    catch (InvalidCipherTextException)
                                    {
                                        /*
                                         * This should never happen, only during decryption.
                                         */
                                        this.FailWithError(AL_fatal, AP_internal_error);
                                    }

                                    /*
                                     * Send the encrypted pms.
                                     */
                                    MemoryStream bos = new MemoryStream();
                                    TlsUtilities.WriteUint8(HP_CLIENT_KEY_EXCHANGE, bos);
                                    TlsUtilities.WriteUint24(encrypted.Length + 2, bos);
                                    TlsUtilities.WriteUint16(encrypted.Length, bos);
                                    bos.Write(encrypted, 0, encrypted.Length);
                                    byte[] message = bos.ToArray();

                                    rs.WriteMessage((short)RL_HANDSHAKE, message, 0, message.Length);
                                    break;

                                case TlsCipherSuite.KE_DHE_RSA:
                                    /*
                                     * Send the Client Key Exchange message for
                                     * DHE key exchange.
                                     */
                                    byte[]       YcByte = this.Yc.ToByteArray();
                                    MemoryStream DHbos  = new MemoryStream();
                                    TlsUtilities.WriteUint8(HP_CLIENT_KEY_EXCHANGE, DHbos);
                                    TlsUtilities.WriteUint24(YcByte.Length + 2, DHbos);
                                    TlsUtilities.WriteUint16(YcByte.Length, DHbos);
                                    DHbos.Write(YcByte, 0, YcByte.Length);
                                    byte[] DHmessage = DHbos.ToArray();

                                    rs.WriteMessage((short)RL_HANDSHAKE, DHmessage, 0, DHmessage.Length);

                                    break;

                                default:
                                    /*
                                     * Proble during handshake, we don't know
                                     * how to thandle this key exchange method.
                                     */
                                    this.FailWithError(AL_fatal, AP_unexpected_message);
                                    break;
                                }

                                connection_state = CS_CLIENT_KEY_EXCHANGE_SEND;

                                /*
                                 * Now, we send change cipher state
                                 */
                                byte[] cmessage = new byte[1];
                                cmessage[0] = 1;
                                rs.WriteMessage((short)RL_CHANGE_CIPHER_SPEC, cmessage, 0, cmessage.Length);

                                connection_state = CS_CLIENT_CHANGE_CIPHER_SPEC_SEND;

                                /*
                                 * Calculate the ms
                                 */
                                this.ms = new byte[48];
                                byte[] randBytes = new byte[clientRandom.Length + serverRandom.Length];
                                Array.Copy(clientRandom, 0, randBytes, 0, clientRandom.Length);
                                Array.Copy(serverRandom, 0, randBytes, clientRandom.Length, serverRandom.Length);
                                TlsUtilities.PRF(pms, TlsUtilities.ToByteArray("master secret"), randBytes, this.ms);

                                /*
                                 * Initialize our cipher suite
                                 */
                                rs.writeSuite = this.choosenCipherSuite;
                                rs.writeSuite.Init(this.ms, clientRandom, serverRandom);

                                /*
                                 * Send our finished message.
                                 */
                                byte[] checksum   = new byte[12];
                                byte[] md5andsha1 = new byte[16 + 20];
                                rs.hash1.DoFinal(md5andsha1, 0);
                                TlsUtilities.PRF(this.ms, TlsUtilities.ToByteArray("client finished"), md5andsha1, checksum);

                                MemoryStream bos2 = new MemoryStream();
                                TlsUtilities.WriteUint8(HP_FINISHED, bos2);
                                TlsUtilities.WriteUint24(12, bos2);
                                bos2.Write(checksum, 0, checksum.Length);
                                byte[] message2 = bos2.ToArray();

                                rs.WriteMessage((short)RL_HANDSHAKE, message2, 0, message2.Length);

                                this.connection_state = CS_CLIENT_FINISHED_SEND;
                                read = true;
                                break;

                            default:
                                this.FailWithError(AL_fatal, AP_handshake_failure);
                                break;
                            }
                            break;

                        case HP_SERVER_KEY_EXCHANGE:
                            switch (connection_state)
                            {
                            case CS_SERVER_CERTIFICATE_RECEIVED:
                                /*
                                 * Check that we are doing DHE key exchange
                                 */
                                if (this.choosenCipherSuite.KeyExchangeAlgorithm != TlsCipherSuite.KE_DHE_RSA)
                                {
                                    this.FailWithError(AL_fatal, AP_unexpected_message);
                                }

                                /*
                                 * Parse the Structure
                                 */
                                int    pLength = TlsUtilities.ReadUint16(inStr);
                                byte[] pByte   = new byte[pLength];
                                TlsUtilities.ReadFully(pByte, inStr);

                                int    gLength = TlsUtilities.ReadUint16(inStr);
                                byte[] gByte   = new byte[gLength];
                                TlsUtilities.ReadFully(gByte, inStr);

                                int    YsLength = TlsUtilities.ReadUint16(inStr);
                                byte[] YsByte   = new byte[YsLength];
                                TlsUtilities.ReadFully(YsByte, inStr);

                                int    sigLength = TlsUtilities.ReadUint16(inStr);
                                byte[] sigByte   = new byte[sigLength];
                                TlsUtilities.ReadFully(sigByte, inStr);

                                this.AssertEmpty(inStr);

                                /*
                                 * Verify the Signature.
                                 *
                                 * First, calculate the hash.
                                 */
                                CombinedHash sigDigest  = new CombinedHash();
                                MemoryStream signedData = new MemoryStream();
                                TlsUtilities.WriteUint16(pLength, signedData);
                                signedData.Write(pByte, 0, pByte.Length);
                                TlsUtilities.WriteUint16(gLength, signedData);
                                signedData.Write(gByte, 0, gByte.Length);
                                TlsUtilities.WriteUint16(YsLength, signedData);
                                signedData.Write(YsByte, 0, YsByte.Length);
                                byte[] signed = signedData.ToArray();

                                sigDigest.BlockUpdate(this.clientRandom, 0, this.clientRandom.Length);
                                sigDigest.BlockUpdate(this.serverRandom, 0, this.serverRandom.Length);
                                sigDigest.BlockUpdate(signed, 0, signed.Length);
                                byte[] hash = new byte[sigDigest.GetDigestSize()];
                                sigDigest.DoFinal(hash, 0);

                                /*
                                 * Now, do the RSA operation
                                 */
                                RsaBlindedEngine rsa      = new RsaBlindedEngine();
                                Pkcs1Encoding    encoding = new Pkcs1Encoding(rsa);
                                encoding.Init(false, this.serverRsaKey);

                                /*
                                 * The data which was signed
                                 */
                                byte[] sigHash = null;

                                try
                                {
                                    sigHash = encoding.ProcessBlock(sigByte, 0, sigByte.Length);
                                }
                                catch (InvalidCipherTextException)
                                {
                                    this.FailWithError(AL_fatal, AP_bad_certificate);
                                }

                                /*
                                 * Check if the data which was signed is equal to
                                 * the hash we calculated.
                                 */
                                if (sigHash.Length != hash.Length)
                                {
                                    this.FailWithError(AL_fatal, AP_bad_certificate);
                                }

                                for (int i = 0; i < sigHash.Length; i++)
                                {
                                    if (sigHash[i] != hash[i])
                                    {
                                        this.FailWithError(AL_fatal, AP_bad_certificate);
                                    }
                                }

                                /*
                                 * OK, Signature was correct.
                                 *
                                 * Do the DH calculation.
                                 */
                                BigInteger p  = new BigInteger(1, pByte);
                                BigInteger g  = new BigInteger(1, gByte);
                                BigInteger Ys = new BigInteger(1, YsByte);
                                BigInteger x  = new BigInteger(p.BitLength - 1, this.random);
                                Yc       = g.ModPow(x, p);
                                this.pms = (Ys.ModPow(x, p)).ToByteArray();

                                /*
                                 * Remove leading zero byte, if present.
                                 */
                                if (this.pms[0] == 0)
                                {
                                    byte[] tmp = new byte[this.pms.Length - 1];
                                    Array.Copy(this.pms, 1, tmp, 0, tmp.Length);
                                    this.pms = tmp;
                                }

                                this.connection_state = CS_SERVER_KEY_EXCHANGE_RECEIVED;
                                read = true;
                                break;

                            default:
                                this.FailWithError(AL_fatal, AP_unexpected_message);
                                break;
                            }
                            break;

                        case HP_HELLO_REQUEST:
                        case HP_CLIENT_KEY_EXCHANGE:
                        case HP_CERTIFICATE_REQUEST:
                        case HP_CERTIFICATE_VERIFY:
                        case HP_CLIENT_HELLO:
                        default:
                            // We do not support this!
                            this.FailWithError(AL_fatal, AP_unexpected_message);
                            break;
                        }
                    }
                }
            }while (read);
        }