<commit_before>package org.bouncycastle.crypto.tls;

import org.bouncycastle.asn1.DERBitString;
import org.bouncycastle.asn1.x509.KeyUsage;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.asn1.x509.X509CertificateStructure;
import org.bouncycastle.asn1.x509.X509Extension;
import org.bouncycastle.asn1.x509.X509Extensions;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.crypto.Signer;
import org.bouncycastle.crypto.encodings.PKCS1Encoding;
import org.bouncycastle.crypto.engines.RSABlindedEngine;
import org.bouncycastle.crypto.io.SignerInputStream;
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
import org.bouncycastle.crypto.params.DSAPublicKeyParameters;
import org.bouncycastle.crypto.params.ParametersWithRandom;
import org.bouncycastle.crypto.params.RSAKeyParameters;
import org.bouncycastle.crypto.prng.ThreadedSeedGenerator;
import org.bouncycastle.crypto.util.PublicKeyFactory;
import org.bouncycastle.util.BigIntegers;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigInteger;
import java.security.SecureRandom;

/**
 * An implementation of all high level protocols in TLS 1.0.
 */
public class TlsProtocolHandler
{

    private static final short RL_CHANGE_CIPHER_SPEC = 20;

    private static final short RL_ALERT = 21;

    private static final short RL_HANDSHAKE = 22;

    private static final short RL_APPLICATION_DATA = 23;

    /*
     hello_request(0), client_hello(1), server_hello(2),
     certificate(11), server_key_exchange (12),
     certificate_request(13), server_hello_done(14),
     certificate_verify(15), client_key_exchange(16),
     finished(20), (255)
     */

    private static final short HP_HELLO_REQUEST = 0;

    private static final short HP_CLIENT_HELLO = 1;

    private static final short HP_SERVER_HELLO = 2;

    private static final short HP_CERTIFICATE = 11;

    private static final short HP_SERVER_KEY_EXCHANGE = 12;

    private static final short HP_CERTIFICATE_REQUEST = 13;

    private static final short HP_SERVER_HELLO_DONE = 14;

    private static final short HP_CERTIFICATE_VERIFY = 15;

    private static final short HP_CLIENT_KEY_EXCHANGE = 16;

    private static final short HP_FINISHED = 20;

    /*
    * Our Connection states
    */

    private static final short CS_CLIENT_HELLO_SEND = 1;

    private static final short CS_SERVER_HELLO_RECEIVED = 2;

    private static final short CS_SERVER_CERTIFICATE_RECEIVED = 3;

    private static final short CS_SERVER_KEY_EXCHANGE_RECEIVED = 4;

    private static final short CS_CERTIFICATE_REQUEST_RECEIVED = 5;

    private static final short CS_SERVER_HELLO_DONE_RECEIVED = 6;

    private static final short CS_CLIENT_KEY_EXCHANGE_SEND = 7;

    private static final short CS_CLIENT_CHANGE_CIPHER_SPEC_SEND = 8;

    private static final short CS_CLIENT_FINISHED_SEND = 9;

    private static final short CS_SERVER_CHANGE_CIPHER_SPEC_RECEIVED = 10;

    private static final short CS_DONE = 11;


    protected static final short AP_close_notify = 0;
    protected static final short AP_unexpected_message = 10;
    protected static final short AP_bad_record_mac = 20;
    protected static final short AP_decryption_failed = 21;
    protected static final short AP_record_overflow = 22;
    protected static final short AP_decompression_failure = 30;
    protected static final short AP_handshake_failure = 40;
    protected static final short AP_bad_certificate = 42;
    protected static final short AP_unsupported_certificate = 43;
    protected static final short AP_certificate_revoked = 44;
    protected static final short AP_certificate_expired = 45;
    protected static final short AP_certificate_unknown = 46;
    protected static final short AP_illegal_parameter = 47;
    protected static final short AP_unknown_ca = 48;
    protected static final short AP_access_denied = 49;
    protected static final short AP_decode_error = 50;
    protected static final short AP_decrypt_error = 51;
    protected static final short AP_export_restriction = 60;
    protected static final short AP_protocol_version = 70;
    protected static final short AP_insufficient_security = 71;
    protected static final short AP_internal_error = 80;
    protected static final short AP_user_canceled = 90;
    protected static final short AP_no_renegotiation = 100;

    protected static final short AL_warning = 1;
    protected static final short AL_fatal = 2;

    private static final byte[] emptybuf = new byte[0];

    private static final String TLS_ERROR_MESSAGE = "Internal TLS error, this could be an attack";

    /*
    * Queues for data from some protocols.
    */

    private ByteQueue applicationDataQueue = new ByteQueue();

    private ByteQueue changeCipherSpecQueue = new ByteQueue();

    private ByteQueue alertQueue = new ByteQueue();

    private ByteQueue handshakeQueue = new ByteQueue();

    /*
    * The Record Stream we use
    */

    private RecordStream rs;

    private SecureRandom random;

    /*
    * The public key of the server.
    */

    private AsymmetricKeyParameter serverPublicKey = null;

    private TlsInputStream tlsInputStream = null;
    private TlsOuputStream tlsOutputStream = null;

    private boolean closed = false;
    private boolean failedWithError = false;
    private boolean appDataReady = false;

    private byte[] clientRandom;
    private byte[] serverRandom;
    private byte[] ms;

    private TlsCipherSuite chosenCipherSuite = null;

    private BigInteger Yc;
    private byte[] pms;

    private CertificateVerifyer verifyer = null;

    public TlsProtocolHandler(InputStream is, OutputStream os)
    {
        /*
         * We use our threaded seed generator to generate a good random
         * seed. If the user has a better random seed, he should use
         * the constructor with a SecureRandom.
         */
        ThreadedSeedGenerator tsg = new ThreadedSeedGenerator();
        this.random = new SecureRandom();
        /*
         * Hopefully, 20 bytes in fast mode are good enough.
         */
        this.random.setSeed(tsg.generateSeed(20, true));

        this.rs = new RecordStream(this, is, os);
    }

    public TlsProtocolHandler(InputStream is, OutputStream os, SecureRandom sr)
    {
        this.random = sr;
        this.rs = new RecordStream(this, is, os);
    }

    private short connection_state;

    protected void processData(short protocol, byte[] buf, int offset, int len)
        throws IOException
    {
        /*
         * Have a look at the protocol type, and add it to the correct queue.
         */
        switch (protocol)
        {
            case RL_CHANGE_CIPHER_SPEC:
                changeCipherSpecQueue.addData(buf, offset, len);
                processChangeCipherSpec();
                break;
            case RL_ALERT:
                alertQueue.addData(buf, offset, len);
                processAlert();
                break;
            case RL_HANDSHAKE:
                handshakeQueue.addData(buf, offset, len);
                processHandshake();
                break;
            case RL_APPLICATION_DATA:
                if (!appDataReady)
                {
                    this.failWithError(AL_fatal, AP_unexpected_message);
                }
                applicationDataQueue.addData(buf, offset, len);
                processApplicationData();
                break;
            default:
                /*
                * Uh, we don't know this protocol.
                *
                * RFC2246 defines on page 13, that we should ignore this.
                */

        }
    }

    private void processHandshake() throws IOException
    {
        boolean read;
        do
        {
            read = false;

            /*
            * We need the first 4 bytes, they contain type and length of
            * the message.
            */
            if (handshakeQueue.size() >= 4)
            {
                byte[] beginning = new byte[4];
                handshakeQueue.read(beginning, 0, 4, 0);
                ByteArrayInputStream bis = new ByteArrayInputStream(beginning);
                short type = TlsUtils.readUint8(bis);
                int len = TlsUtils.readUint24(bis);

                /*
                * Check if we have enough bytes in the buffer to read
                * the full message.
                */
                if (handshakeQueue.size() >= (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.update(beginning, 0, 4);
                        rs.hash2.update(beginning, 0, 4);
                        rs.hash1.update(buf, 0, len);
                        rs.hash2.update(buf, 0, len);
                    }

                    /*
                    * Now, parse the message.
                    */
                    ByteArrayInputStream is = new ByteArrayInputStream(buf);

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

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

                                    try
                                    {
                                        this.serverPublicKey = PublicKeyFactory.createKey(keyInfo);
                                    }
                                    catch (RuntimeException e)
                                    {
                                        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.getKeyExchangeAlgorithm())
                                    {
                                        case TlsCipherSuite.KE_RSA:
                                            if (!(this.serverPublicKey instanceof RSAKeyParameters))
                                            {
                                                this.failWithError(AL_fatal, AP_certificate_unknown);
                                            }
                                            validateKeyUsage(x509Cert, KeyUsage.keyEncipherment);
                                            break;
                                        case TlsCipherSuite.KE_DHE_RSA:
                                            if (!(this.serverPublicKey instanceof RSAKeyParameters))
                                            {
                                                this.failWithError(AL_fatal, AP_certificate_unknown);
                                            }
                                            validateKeyUsage(x509Cert, KeyUsage.digitalSignature);
                                            break;
                                        case TlsCipherSuite.KE_DHE_DSS:
                                            if (!(this.serverPublicKey instanceof DSAPublicKeyParameters))
                                            {
                                                this.failWithError(AL_fatal, AP_certificate_unknown);
                                            }
                                            break;
                                        default:
                                            this.failWithError(AL_fatal, AP_unsupported_certificate);
                                    }

                                    /*
                                     * Verify them.
                                     */
                                    if (!this.verifyer.isValid(cert.getCerts()))
                                    {
                                        this.failWithError(AL_fatal, AP_user_canceled);
                                    }

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

                            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];
                                    TlsUtils.readFully(receivedChecksum, is);
                                    assertEmpty(is);

                                    /*
                                    * Calculate our own checksum.
                                    */
                                    byte[] checksum = new byte[12];
                                    byte[] md5andsha1 = new byte[16 + 20];
                                    rs.hash2.doFinal(md5andsha1, 0);
                                    TlsUtils.PRF(this.ms, TlsUtils.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;
                        case HP_SERVER_HELLO:
                            switch (connection_state)
                            {
                                case CS_CLIENT_HELLO_SEND:
                                    /*
                                    * Read the server hello message
                                    */
                                    TlsUtils.checkVersion(is, this);

                                    /*
                                    * Read the server random
                                    */
                                    this.serverRandom = new byte[32];
                                    TlsUtils.readFully(this.serverRandom, is);

                                    /*
                                    * Currently, we don't support session ids
                                    */
                                    short sessionIdLength = TlsUtils.readUint8(is);
                                    byte[] sessionId = new byte[sessionIdLength];
                                    TlsUtils.readFully(sessionId, is);

                                    /*
                                    * 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(TlsUtils.readUint16(is), this);

                                    /*
                                    * We support only the null compression which
                                    * means no compression.
                                    */
                                    short compressionMethod = TlsUtils.readUint8(is);
                                    if (compressionMethod != 0)
                                    {
                                        this.failWithError(TlsProtocolHandler.AL_fatal, TlsProtocolHandler.AP_illegal_parameter);
                                    }
                                    assertEmpty(is);

                                    connection_state = CS_SERVER_HELLO_RECEIVED;
                                    read = true;
                                    break;
                                default:
                                    this.failWithError(AL_fatal, AP_unexpected_message);
                            }
                            break;
                        case HP_SERVER_HELLO_DONE:
                            switch (connection_state)
                            {

                                case CS_SERVER_CERTIFICATE_RECEIVED:
                                    /*
                                    * There was no server key exchange message, check
                                    * that we are doing RSA key exchange.
                                    */
                                    if (this.chosenCipherSuite.getKeyExchangeAlgorithm() != TlsCipherSuite.KE_RSA)
                                    {
                                        this.failWithError(AL_fatal, AP_unexpected_message);
                                    }

                                    /*
                                    * NB: Fall through to next case label to continue RSA key exchange
                                    */

                                case CS_SERVER_KEY_EXCHANGE_RECEIVED:
                                case CS_CERTIFICATE_REQUEST_RECEIVED:

                                    assertEmpty(is);
                                    boolean 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.getKeyExchangeAlgorithm())
                                    {
                                        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];
                                            random.nextBytes(pms);
                                            pms[0] = 3;
                                            pms[1] = 1;

                                            /*
                                            * 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 e)
                                            {
                                                /*
                                                * 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);

                                    }

                                    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[] random = new byte[clientRandom.length + serverRandom.length];
                                    System.arraycopy(clientRandom, 0, random, 0, clientRandom.length);
                                    System.arraycopy(serverRandom, 0, random, clientRandom.length, serverRandom.length);
                                    TlsUtils.PRF(pms, TlsUtils.toByteArray("master secret"), random, 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);
                                    TlsUtils.PRF(this.ms, TlsUtils.toByteArray("client finished"), md5andsha1, checksum);

                                    ByteArrayOutputStream bos = new ByteArrayOutputStream();
                                    TlsUtils.writeUint8(HP_FINISHED, bos);
                                    TlsUtils.writeUint24(12, bos);
                                    bos.write(checksum);
                                    byte[] message = bos.toByteArray();

                                    rs.writeMessage((short)RL_HANDSHAKE, message, 0, message.length);

                                    this.connection_state = CS_CLIENT_FINISHED_SEND;
                                    read = true;
                                    break;
                                default:
                                    this.failWithError(AL_fatal, AP_handshake_failure);
                            }
                            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.getKeyExchangeAlgorithm())
                                    {
                                        case TlsCipherSuite.KE_DHE_RSA:
                                        {
                                            processDHEKeyExchange(is, new TlsRSASigner());
                                            break;
                                        }
                                        case TlsCipherSuite.KE_DHE_DSS:
                                        {
                                            processDHEKeyExchange(is, new TlsDSSSigner());
                                            break;
                                        }
                                        default:
                                            this.failWithError(AL_fatal, AP_unexpected_message);
                                    }
                                    break;
                                }
                                default:
                                    this.failWithError(AL_fatal, AP_unexpected_message);
                            }

                            this.connection_state = CS_SERVER_KEY_EXCHANGE_RECEIVED;
                            read = true;
                            break;
                        }
                        case HP_CERTIFICATE_REQUEST:
                        {
                            switch (connection_state)
                            {
                                case CS_SERVER_CERTIFICATE_RECEIVED:
                                    /*
                                    * There was no server key exchange message, check
                                    * that we are doing RSA key exchange.
                                    */
                                    if (this.chosenCipherSuite.getKeyExchangeAlgorithm() != TlsCipherSuite.KE_RSA)
                                    {
                                        this.failWithError(AL_fatal, AP_unexpected_message);
                                    }

                                    /*
                                    * NB: Fall through to next case label to continue RSA key exchange
                                    */

                                case CS_SERVER_KEY_EXCHANGE_RECEIVED:
                                {
                                    short typesLength = TlsUtils.readUint8(is);
                                    byte[] types = new byte[typesLength];
                                    TlsUtils.readFully(types, is);

                                    int authsLength = TlsUtils.readUint16(is);
                                    byte[] auths = new byte[authsLength];
                                    TlsUtils.readFully(auths, is);

                                    assertEmpty(is);
                                    break;
                                }
                                default:
                                    this.failWithError(AL_fatal, AP_unexpected_message);
                            }

                            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 processApplicationData()
    {
        /*
         * There is nothing we need to do here.
         * 
         * This function could be used for callbacks when application
         * data arrives in the future.
         */
    }

    private void processAlert() throws IOException
    {
        while (alertQueue.size() >= 2)
        {
            /*
             * An alert is always 2 bytes. Read the alert.
             */
            byte[] tmp = new byte[2];
            alertQueue.read(tmp, 0, 2, 0);
            alertQueue.removeData(2);
            short level = tmp[0];
            short description = tmp[1];
            if (level == AL_fatal)
            {
                /*
                 * This is a fatal error.
                 */
                this.failedWithError = true;
                this.closed = true;
                /*
                 * Now try to close the stream, ignore errors.
                 */
                try
                {
                    rs.close();
                }
                catch (Exception e)
                {

                }
                throw new IOException(TLS_ERROR_MESSAGE);
            }
            else
            {
                /*
                 * This is just a warning.
                 */
                if (description == AP_close_notify)
                {
                    /*
                     * Close notify
                     */
                    this.failWithError(AL_warning, AP_close_notify);
                }
                /*
                 * If it is just a warning, we continue.
                 */
            }
        }
    }

    /**
     * This method is called, when a change cipher spec message is received.
     *
     * @throws IOException If the message has an invalid content or the
     *                     handshake is not in the correct state.
     */
    private void processChangeCipherSpec() throws IOException
    {
        while (changeCipherSpecQueue.size() > 0)
        {
            /*
             * A change cipher spec message is only one byte with the value 1.
             */
            byte[] b = new byte[1];
            changeCipherSpecQueue.read(b, 0, 1, 0);
            changeCipherSpecQueue.removeData(1);
            if (b[0] != 1)
            {
                /*
                 * This should never happen.
                 */
                this.failWithError(AL_fatal, AP_unexpected_message);

            }
            else
            {
                /*
                 * Check if we are in the correct connection state.
                 */
                if (this.connection_state == CS_CLIENT_FINISHED_SEND)
                {
                    rs.readSuite = rs.writeSuite;
                    this.connection_state = CS_SERVER_CHANGE_CIPHER_SPEC_RECEIVED;
                }
                else
                {
                    /*
                     * We are not in the correct connection state.
                     */
                    this.failWithError(AL_fatal, AP_handshake_failure);
                }

            }
        }
    }

    private void processDHEKeyExchange(ByteArrayInputStream is, Signer signer)
        throws IOException
    {
        InputStream sigIn = is;
        if (signer != null)
        {
            signer.init(false, this.serverPublicKey);
            signer.update(this.clientRandom, 0, this.clientRandom.length);
            signer.update(this.serverRandom, 0, this.serverRandom.length);

            sigIn = new SignerInputStream(is, signer);
        }

        /*
         * Parse the Structure
         */
         int pLength = TlsUtils.readUint16(sigIn);
         byte[] pByte = new byte[pLength];
         TlsUtils.readFully(pByte, sigIn);

         int gLength = TlsUtils.readUint16(sigIn);
         byte[] gByte = new byte[gLength];
         TlsUtils.readFully(gByte, sigIn);

         int YsLength = TlsUtils.readUint16(sigIn);
         byte[] YsByte = new byte[YsLength];
         TlsUtils.readFully(YsByte, sigIn);

         if (signer != null)
         {
             int sigLength = TlsUtils.readUint16(is);
             byte[] sigByte = new byte[sigLength];
             TlsUtils.readFully(sigByte, is);

             /*
              * Verify the Signature.
              */
             if (!signer.verifySignature(sigByte))
             {
                 this.failWithError(AL_fatal, AP_bad_certificate);
             }
         }

         this.assertEmpty(is);

         /*
         * 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);

         this.Yc = g.modPow(x, p);
         this.pms = BigIntegers.asUnsignedByteArray(Ys.modPow(x, p));
    }

    private void validateKeyUsage(X509CertificateStructure c, int keyUsageBits)
        throws IOException
    {
        X509Extensions exts = c.getTBSCertificate().getExtensions();
        if (exts != null)
        {
            X509Extension ext = exts.getExtension(X509Extensions.KeyUsage);
            if (ext != null)
            {
                DERBitString ku = KeyUsage.getInstance(ext);
                int bits = ku.getBytes()[0] & 0xff;
                if ((bits & keyUsageBits) != keyUsageBits)
                {
                    this.failWithError(AL_fatal, AP_certificate_unknown);
                }
            }
        }
    }

    private void sendClientCertificate() throws IOException
    {
        /*
         * just write back the "no client certificate" message
         * see also gnutls, auth_cert.c:643 (0B 00 00 03 00 00 00)
         */
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        TlsUtils.writeUint8(HP_CERTIFICATE, bos);
        TlsUtils.writeUint24(3, bos);
        TlsUtils.writeUint24(0, bos);<commit_msg>Use the appropriate DH classes to implement agreement<commit_after>package org.bouncycastle.crypto.tls;

import org.bouncycastle.asn1.DERBitString;
import org.bouncycastle.asn1.x509.KeyUsage;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.asn1.x509.X509CertificateStructure;
import org.bouncycastle.asn1.x509.X509Extension;
import org.bouncycastle.asn1.x509.X509Extensions;
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.crypto.Signer;
import org.bouncycastle.crypto.agreement.DHBasicAgreement;
import org.bouncycastle.crypto.encodings.PKCS1Encoding;
import org.bouncycastle.crypto.engines.RSABlindedEngine;
import org.bouncycastle.crypto.generators.DHBasicKeyPairGenerator;
import org.bouncycastle.crypto.io.SignerInputStream;
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
import org.bouncycastle.crypto.params.DHKeyGenerationParameters;
import org.bouncycastle.crypto.params.DHParameters;
import org.bouncycastle.crypto.params.DHPublicKeyParameters;
import org.bouncycastle.crypto.params.DSAPublicKeyParameters;
import org.bouncycastle.crypto.params.ParametersWithRandom;
import org.bouncycastle.crypto.params.RSAKeyParameters;
import org.bouncycastle.crypto.prng.ThreadedSeedGenerator;
import org.bouncycastle.crypto.util.PublicKeyFactory;
import org.bouncycastle.util.BigIntegers;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigInteger;
import java.security.SecureRandom;

/**
 * An implementation of all high level protocols in TLS 1.0.
 */
public class TlsProtocolHandler
{
    private static final BigInteger ONE = BigInteger.valueOf(1);
    private static final BigInteger TWO = BigInteger.valueOf(2);

    private static final short RL_CHANGE_CIPHER_SPEC = 20;

    private static final short RL_ALERT = 21;

    private static final short RL_HANDSHAKE = 22;

    private static final short RL_APPLICATION_DATA = 23;

    /*
     hello_request(0), client_hello(1), server_hello(2),
     certificate(11), server_key_exchange (12),
     certificate_request(13), server_hello_done(14),
     certificate_verify(15), client_key_exchange(16),
     finished(20), (255)
     */

    private static final short HP_HELLO_REQUEST = 0;

    private static final short HP_CLIENT_HELLO = 1;

    private static final short HP_SERVER_HELLO = 2;

    private static final short HP_CERTIFICATE = 11;

    private static final short HP_SERVER_KEY_EXCHANGE = 12;

    private static final short HP_CERTIFICATE_REQUEST = 13;

    private static final short HP_SERVER_HELLO_DONE = 14;

    private static final short HP_CERTIFICATE_VERIFY = 15;

    private static final short HP_CLIENT_KEY_EXCHANGE = 16;

    private static final short HP_FINISHED = 20;

    /*
    * Our Connection states
    */

    private static final short CS_CLIENT_HELLO_SEND = 1;

    private static final short CS_SERVER_HELLO_RECEIVED = 2;

    private static final short CS_SERVER_CERTIFICATE_RECEIVED = 3;

    private static final short CS_SERVER_KEY_EXCHANGE_RECEIVED = 4;

    private static final short CS_CERTIFICATE_REQUEST_RECEIVED = 5;

    private static final short CS_SERVER_HELLO_DONE_RECEIVED = 6;

    private static final short CS_CLIENT_KEY_EXCHANGE_SEND = 7;

    private static final short CS_CLIENT_CHANGE_CIPHER_SPEC_SEND = 8;

    private static final short CS_CLIENT_FINISHED_SEND = 9;

    private static final short CS_SERVER_CHANGE_CIPHER_SPEC_RECEIVED = 10;

    private static final short CS_DONE = 11;


    protected static final short AP_close_notify = 0;
    protected static final short AP_unexpected_message = 10;
    protected static final short AP_bad_record_mac = 20;
    protected static final short AP_decryption_failed = 21;
    protected static final short AP_record_overflow = 22;
    protected static final short AP_decompression_failure = 30;
    protected static final short AP_handshake_failure = 40;
    protected static final short AP_bad_certificate = 42;
    protected static final short AP_unsupported_certificate = 43;
    protected static final short AP_certificate_revoked = 44;
    protected static final short AP_certificate_expired = 45;
    protected static final short AP_certificate_unknown = 46;
    protected static final short AP_illegal_parameter = 47;
    protected static final short AP_unknown_ca = 48;
    protected static final short AP_access_denied = 49;
    protected static final short AP_decode_error = 50;
    protected static final short AP_decrypt_error = 51;
    protected static final short AP_export_restriction = 60;
    protected static final short AP_protocol_version = 70;
    protected static final short AP_insufficient_security = 71;
    protected static final short AP_internal_error = 80;
    protected static final short AP_user_canceled = 90;
    protected static final short AP_no_renegotiation = 100;

    protected static final short AL_warning = 1;
    protected static final short AL_fatal = 2;

    private static final byte[] emptybuf = new byte[0];

    private static final String TLS_ERROR_MESSAGE = "Internal TLS error, this could be an attack";

    /*
    * Queues for data from some protocols.
    */

    private ByteQueue applicationDataQueue = new ByteQueue();

    private ByteQueue changeCipherSpecQueue = new ByteQueue();

    private ByteQueue alertQueue = new ByteQueue();

    private ByteQueue handshakeQueue = new ByteQueue();

    /*
    * The Record Stream we use
    */

    private RecordStream rs;

    private SecureRandom random;

    /*
    * The public key of the server.
    */

    private AsymmetricKeyParameter serverPublicKey = null;

    private TlsInputStream tlsInputStream = null;
    private TlsOuputStream tlsOutputStream = null;

    private boolean closed = false;
    private boolean failedWithError = false;
    private boolean appDataReady = false;

    private byte[] clientRandom;
    private byte[] serverRandom;
    private byte[] ms;

    private TlsCipherSuite chosenCipherSuite = null;

    private BigInteger Yc;
    private byte[] pms;

    private CertificateVerifyer verifyer = null;

    public TlsProtocolHandler(InputStream is, OutputStream os)
    {
        /*
         * We use our threaded seed generator to generate a good random
         * seed. If the user has a better random seed, he should use
         * the constructor with a SecureRandom.
         */
        ThreadedSeedGenerator tsg = new ThreadedSeedGenerator();
        this.random = new SecureRandom();
        /*
         * Hopefully, 20 bytes in fast mode are good enough.
         */
        this.random.setSeed(tsg.generateSeed(20, true));

        this.rs = new RecordStream(this, is, os);
    }

    public TlsProtocolHandler(InputStream is, OutputStream os, SecureRandom sr)
    {
        this.random = sr;
        this.rs = new RecordStream(this, is, os);
    }

    private short connection_state;

    protected void processData(short protocol, byte[] buf, int offset, int len)
        throws IOException
    {
        /*
         * Have a look at the protocol type, and add it to the correct queue.
         */
        switch (protocol)
        {
            case RL_CHANGE_CIPHER_SPEC:
                changeCipherSpecQueue.addData(buf, offset, len);
                processChangeCipherSpec();
                break;
            case RL_ALERT:
                alertQueue.addData(buf, offset, len);
                processAlert();
                break;
            case RL_HANDSHAKE:
                handshakeQueue.addData(buf, offset, len);
                processHandshake();
                break;
            case RL_APPLICATION_DATA:
                if (!appDataReady)
                {
                    this.failWithError(AL_fatal, AP_unexpected_message);
                }
                applicationDataQueue.addData(buf, offset, len);
                processApplicationData();
                break;
            default:
                /*
                * Uh, we don't know this protocol.
                *
                * RFC2246 defines on page 13, that we should ignore this.
                */

        }
    }

    private void processHandshake() throws IOException
    {
        boolean read;
        do
        {
            read = false;

            /*
            * We need the first 4 bytes, they contain type and length of
            * the message.
            */
            if (handshakeQueue.size() >= 4)
            {
                byte[] beginning = new byte[4];
                handshakeQueue.read(beginning, 0, 4, 0);
                ByteArrayInputStream bis = new ByteArrayInputStream(beginning);
                short type = TlsUtils.readUint8(bis);
                int len = TlsUtils.readUint24(bis);

                /*
                * Check if we have enough bytes in the buffer to read
                * the full message.
                */
                if (handshakeQueue.size() >= (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.update(beginning, 0, 4);
                        rs.hash2.update(beginning, 0, 4);
                        rs.hash1.update(buf, 0, len);
                        rs.hash2.update(buf, 0, len);
                    }

                    /*
                    * Now, parse the message.
                    */
                    ByteArrayInputStream is = new ByteArrayInputStream(buf);

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

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

                                    try
                                    {
                                        this.serverPublicKey = PublicKeyFactory.createKey(keyInfo);
                                    }
                                    catch (RuntimeException e)
                                    {
                                        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.getKeyExchangeAlgorithm())
                                    {
                                        case TlsCipherSuite.KE_RSA:
                                            if (!(this.serverPublicKey instanceof RSAKeyParameters))
                                            {
                                                this.failWithError(AL_fatal, AP_certificate_unknown);
                                            }
                                            validateKeyUsage(x509Cert, KeyUsage.keyEncipherment);
                                            break;
                                        case TlsCipherSuite.KE_DHE_RSA:
                                            if (!(this.serverPublicKey instanceof RSAKeyParameters))
                                            {
                                                this.failWithError(AL_fatal, AP_certificate_unknown);
                                            }
                                            validateKeyUsage(x509Cert, KeyUsage.digitalSignature);
                                            break;
                                        case TlsCipherSuite.KE_DHE_DSS:
                                            if (!(this.serverPublicKey instanceof DSAPublicKeyParameters))
                                            {
                                                this.failWithError(AL_fatal, AP_certificate_unknown);
                                            }
                                            break;
                                        default:
                                            this.failWithError(AL_fatal, AP_unsupported_certificate);
                                    }

                                    /*
                                     * Verify them.
                                     */
                                    if (!this.verifyer.isValid(cert.getCerts()))
                                    {
                                        this.failWithError(AL_fatal, AP_user_canceled);
                                    }

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

                            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];
                                    TlsUtils.readFully(receivedChecksum, is);
                                    assertEmpty(is);

                                    /*
                                    * Calculate our own checksum.
                                    */
                                    byte[] checksum = new byte[12];
                                    byte[] md5andsha1 = new byte[16 + 20];
                                    rs.hash2.doFinal(md5andsha1, 0);
                                    TlsUtils.PRF(this.ms, TlsUtils.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;
                        case HP_SERVER_HELLO:
                            switch (connection_state)
                            {
                                case CS_CLIENT_HELLO_SEND:
                                    /*
                                    * Read the server hello message
                                    */
                                    TlsUtils.checkVersion(is, this);

                                    /*
                                    * Read the server random
                                    */
                                    this.serverRandom = new byte[32];
                                    TlsUtils.readFully(this.serverRandom, is);

                                    /*
                                    * Currently, we don't support session ids
                                    */
                                    short sessionIdLength = TlsUtils.readUint8(is);
                                    byte[] sessionId = new byte[sessionIdLength];
                                    TlsUtils.readFully(sessionId, is);

                                    /*
                                    * 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(TlsUtils.readUint16(is), this);

                                    /*
                                    * We support only the null compression which
                                    * means no compression.
                                    */
                                    short compressionMethod = TlsUtils.readUint8(is);
                                    if (compressionMethod != 0)
                                    {
                                        this.failWithError(TlsProtocolHandler.AL_fatal, TlsProtocolHandler.AP_illegal_parameter);
                                    }
                                    assertEmpty(is);

                                    connection_state = CS_SERVER_HELLO_RECEIVED;
                                    read = true;
                                    break;
                                default:
                                    this.failWithError(AL_fatal, AP_unexpected_message);
                            }
                            break;
                        case HP_SERVER_HELLO_DONE:
                            switch (connection_state)
                            {

                                case CS_SERVER_CERTIFICATE_RECEIVED:
                                    /*
                                    * There was no server key exchange message, check
                                    * that we are doing RSA key exchange.
                                    */
                                    if (this.chosenCipherSuite.getKeyExchangeAlgorithm() != TlsCipherSuite.KE_RSA)
                                    {
                                        this.failWithError(AL_fatal, AP_unexpected_message);
                                    }

                                    /*
                                    * NB: Fall through to next case label to continue RSA key exchange
                                    */

                                case CS_SERVER_KEY_EXCHANGE_RECEIVED:
                                case CS_CERTIFICATE_REQUEST_RECEIVED:

                                    assertEmpty(is);
                                    boolean 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.getKeyExchangeAlgorithm())
                                    {
                                        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];
                                            random.nextBytes(pms);
                                            pms[0] = 3;
                                            pms[1] = 1;

                                            /*
                                            * 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 e)
                                            {
                                                /*
                                                * 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);

                                    }

                                    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[] random = new byte[clientRandom.length + serverRandom.length];
                                    System.arraycopy(clientRandom, 0, random, 0, clientRandom.length);
                                    System.arraycopy(serverRandom, 0, random, clientRandom.length, serverRandom.length);
                                    TlsUtils.PRF(pms, TlsUtils.toByteArray("master secret"), random, 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);
                                    TlsUtils.PRF(this.ms, TlsUtils.toByteArray("client finished"), md5andsha1, checksum);

                                    ByteArrayOutputStream bos = new ByteArrayOutputStream();
                                    TlsUtils.writeUint8(HP_FINISHED, bos);
                                    TlsUtils.writeUint24(12, bos);
                                    bos.write(checksum);
                                    byte[] message = bos.toByteArray();

                                    rs.writeMessage((short)RL_HANDSHAKE, message, 0, message.length);

                                    this.connection_state = CS_CLIENT_FINISHED_SEND;
                                    read = true;
                                    break;
                                default:
                                    this.failWithError(AL_fatal, AP_handshake_failure);
                            }
                            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.getKeyExchangeAlgorithm())
                                    {
                                        case TlsCipherSuite.KE_DHE_RSA:
                                        {
                                            processDHEKeyExchange(is, new TlsRSASigner());
                                            break;
                                        }
                                        case TlsCipherSuite.KE_DHE_DSS:
                                        {
                                            processDHEKeyExchange(is, new TlsDSSSigner());
                                            break;
                                        }
                                        default:
                                            this.failWithError(AL_fatal, AP_unexpected_message);
                                    }
                                    break;
                                }
                                default:
                                    this.failWithError(AL_fatal, AP_unexpected_message);
                            }

                            this.connection_state = CS_SERVER_KEY_EXCHANGE_RECEIVED;
                            read = true;
                            break;
                        }
                        case HP_CERTIFICATE_REQUEST:
                        {
                            switch (connection_state)
                            {
                                case CS_SERVER_CERTIFICATE_RECEIVED:
                                    /*
                                    * There was no server key exchange message, check
                                    * that we are doing RSA key exchange.
                                    */
                                    if (this.chosenCipherSuite.getKeyExchangeAlgorithm() != TlsCipherSuite.KE_RSA)
                                    {
                                        this.failWithError(AL_fatal, AP_unexpected_message);
                                    }

                                    /*
                                    * NB: Fall through to next case label to continue RSA key exchange
                                    */

                                case CS_SERVER_KEY_EXCHANGE_RECEIVED:
                                {
                                    short typesLength = TlsUtils.readUint8(is);
                                    byte[] types = new byte[typesLength];
                                    TlsUtils.readFully(types, is);

                                    int authsLength = TlsUtils.readUint16(is);
                                    byte[] auths = new byte[authsLength];
                                    TlsUtils.readFully(auths, is);

                                    assertEmpty(is);
                                    break;
                                }
                                default:
                                    this.failWithError(AL_fatal, AP_unexpected_message);
                            }

                            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 processApplicationData()
    {
        /*
         * There is nothing we need to do here.
         * 
         * This function could be used for callbacks when application
         * data arrives in the future.
         */
    }

    private void processAlert() throws IOException
    {
        while (alertQueue.size() >= 2)
        {
            /*
             * An alert is always 2 bytes. Read the alert.
             */
            byte[] tmp = new byte[2];
            alertQueue.read(tmp, 0, 2, 0);
            alertQueue.removeData(2);
            short level = tmp[0];
            short description = tmp[1];
            if (level == AL_fatal)
            {
                /*
                 * This is a fatal error.
                 */
                this.failedWithError = true;
                this.closed = true;
                /*
                 * Now try to close the stream, ignore errors.
                 */
                try
                {
                    rs.close();
                }
                catch (Exception e)
                {

                }
                throw new IOException(TLS_ERROR_MESSAGE);
            }
            else
            {
                /*
                 * This is just a warning.
                 */
                if (description == AP_close_notify)
                {
                    /*
                     * Close notify
                     */
                    this.failWithError(AL_warning, AP_close_notify);
                }
                /*
                 * If it is just a warning, we continue.
                 */
            }
        }
    }

    /**
     * This method is called, when a change cipher spec message is received.
     *
     * @throws IOException If the message has an invalid content or the
     *                     handshake is not in the correct state.
     */
    private void processChangeCipherSpec() throws IOException
    {
        while (changeCipherSpecQueue.size() > 0)
        {
            /*
             * A change cipher spec message is only one byte with the value 1.
             */
            byte[] b = new byte[1];
            changeCipherSpecQueue.read(b, 0, 1, 0);
            changeCipherSpecQueue.removeData(1);
            if (b[0] != 1)
            {
                /*
                 * This should never happen.
                 */
                this.failWithError(AL_fatal, AP_unexpected_message);

            }
            else
            {
                /*
                 * Check if we are in the correct connection state.
                 */
                if (this.connection_state == CS_CLIENT_FINISHED_SEND)
                {
                    rs.readSuite = rs.writeSuite;
                    this.connection_state = CS_SERVER_CHANGE_CIPHER_SPEC_RECEIVED;
                }
                else
                {
                    /*
                     * We are not in the correct connection state.
                     */
                    this.failWithError(AL_fatal, AP_handshake_failure);
                }

            }
        }
    }

    private void processDHEKeyExchange(ByteArrayInputStream is, Signer signer)
        throws IOException
    {
        InputStream sigIn = is;
        if (signer != null)
        {
            signer.init(false, this.serverPublicKey);
            signer.update(this.clientRandom, 0, this.clientRandom.length);
            signer.update(this.serverRandom, 0, this.serverRandom.length);

            sigIn = new SignerInputStream(is, signer);
        }

        /*
         * Parse the Structure
         */
         int pLength = TlsUtils.readUint16(sigIn);
         byte[] pByte = new byte[pLength];
         TlsUtils.readFully(pByte, sigIn);

         int gLength = TlsUtils.readUint16(sigIn);
         byte[] gByte = new byte[gLength];
         TlsUtils.readFully(gByte, sigIn);

         int YsLength = TlsUtils.readUint16(sigIn);
         byte[] YsByte = new byte[YsLength];
         TlsUtils.readFully(YsByte, sigIn);

         if (signer != null)
         {
             int sigLength = TlsUtils.readUint16(is);
             byte[] sigByte = new byte[sigLength];
             TlsUtils.readFully(sigByte, is);

             /*
              * Verify the Signature.
              */
             if (!signer.verifySignature(sigByte))
             {
                 this.failWithError(AL_fatal, AP_bad_certificate);
             }
         }

         this.assertEmpty(is);

         /*
         * Do the DH calculation.
         */
         BigInteger p = new BigInteger(1, pByte);
         BigInteger g = new BigInteger(1, gByte);
         BigInteger Ys = new BigInteger(1, YsByte);

         /*
          * Check the DH parameter values
          */
         if (!p.isProbablePrime(10))
         {
             this.failWithError(AL_fatal, AP_illegal_parameter);
         }
         if (g.compareTo(TWO) < 0 || g.compareTo(p.subtract(TWO)) > 0)
         {
             this.failWithError(AL_fatal, AP_illegal_parameter);
         }
         // TODO For static DH public values, see additional checks in RFC 2631 2.1.5 
         if (Ys.compareTo(TWO) < 0 || Ys.compareTo(p.subtract(ONE)) > 0)
         {
             this.failWithError(AL_fatal, AP_illegal_parameter);
         }

         /*
          * Diffie-Hellman basic key agreement
          */
         DHParameters dhParams = new DHParameters(p, g);

         // Generate a keypair
         DHBasicKeyPairGenerator dhGen = new DHBasicKeyPairGenerator();
         dhGen.init(new DHKeyGenerationParameters(random, dhParams));

         AsymmetricCipherKeyPair dhPair = dhGen.generateKeyPair();

         // Store the public value to send to server
         this.Yc = ((DHPublicKeyParameters)dhPair.getPublic()).getY();

         // Calculate the shared secret
         DHBasicAgreement dhAgree = new DHBasicAgreement();
         dhAgree.init(dhPair.getPrivate());

         BigInteger agreement = dhAgree.calculateAgreement(new DHPublicKeyParameters(Ys, dhParams));

         this.pms = BigIntegers.asUnsignedByteArray(agreement);
    }

    private void validateKeyUsage(X509CertificateStructure c, int keyUsageBits)
        throws IOException
    {
        X509Extensions exts = c.getTBSCertificate().getExtensions();
        if (exts != null)
        {
            X509Extension ext = exts.getExtension(X509Extensions.KeyUsage);
            if (ext != null)
            {
                DERBitString ku = KeyUsage.getInstance(ext);
                int bits = ku.getBytes()[0] & 0xff;
                if ((bits & keyUsageBits) != keyUsageBits)
                {
                    this.failWithError(AL_fatal, AP_certificate_unknown);
                }
            }
        }
    }

    private void sendClientCertificate() throws IOException
    {
        /*
         * just write back the "no client certificate" message
         * see also gnutls, auth_cert.c:643 (0B 00 00 03 00 00 00)
         */
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        TlsUtils.writeUint8(HP_CERTIFICATE, bos);
        TlsUtils.writeUint24(3, bos);
        TlsUtils.writeUint24(0, bos);
{'fraction_non_alphanumeric': 0.05765253360910031, 'fraction_numerical': 0.008790072388831437, 'mean_word_length': 0.8954061006982726, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 13, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 790, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3311347', 'n_tokens_mistral': 17471, 'n_tokens_neox': 15823, 'n_words': 3792}